Merge branch 'master' into fix-2694

This commit is contained in:
Feoramund
2025-06-15 15:30:26 -04:00
10 changed files with 224 additions and 63 deletions
+57 -21
View File
@@ -2420,7 +2420,7 @@ buddy_allocator_proc :: proc(
// on the old size to work.
//
// The overhead of this allocator is an extra max(alignment, size_of(Header)) bytes allocated for each allocation, these bytes are
// used to store the size and original pointer.
// used to store the size and alignment.
Compat_Allocator :: struct {
parent: Allocator,
}
@@ -2440,51 +2440,87 @@ compat_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int,
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
size, old_size := size, old_size
Header :: struct {
size: int,
ptr: rawptr,
size: int,
alignment: int,
}
@(no_sanitize_address)
get_unpoisoned_header :: #force_inline proc(ptr: rawptr) -> Header {
header := ([^]Header)(ptr)[-1]
a := max(header.alignment, size_of(Header))
sanitizer.address_unpoison(rawptr(uintptr(ptr)-uintptr(a)), a)
return header
}
rra := (^Compat_Allocator)(allocator_data)
switch mode {
case .Alloc, .Alloc_Non_Zeroed:
a := max(alignment, size_of(Header))
size += a
assert(size >= 0, "overflow")
a := max(alignment, size_of(Header))
req_size := size + a
assert(req_size >= 0, "overflow")
allocation := rra.parent.procedure(rra.parent.data, mode, size, alignment, old_memory, old_size, location) or_return
allocation := rra.parent.procedure(rra.parent.data, mode, req_size, alignment, old_memory, old_size, location) or_return
#no_bounds_check data = allocation[a:]
([^]Header)(raw_data(data))[-1] = {
size = size,
ptr = raw_data(allocation),
size = size,
alignment = alignment,
}
sanitizer.address_poison(raw_data(allocation), a)
return
case .Free:
header := ([^]Header)(old_memory)[-1]
return rra.parent.procedure(rra.parent.data, mode, size, alignment, header.ptr, header.size, location)
header := get_unpoisoned_header(old_memory)
a := max(header.alignment, size_of(Header))
orig_ptr := rawptr(uintptr(old_memory)-uintptr(a))
orig_size := header.size + a
return rra.parent.procedure(rra.parent.data, mode, orig_size, header.alignment, orig_ptr, orig_size, location)
case .Resize, .Resize_Non_Zeroed:
header := ([^]Header)(old_memory)[-1]
header := get_unpoisoned_header(old_memory)
orig_a := max(header.alignment, size_of(Header))
orig_ptr := rawptr(uintptr(old_memory)-uintptr(orig_a))
orig_size := header.size + orig_a
a := max(alignment, size_of(header))
size += a
new_alignment := max(header.alignment, alignment)
a := max(new_alignment, size_of(header))
req_size := size + a
assert(size >= 0, "overflow")
allocation := rra.parent.procedure(rra.parent.data, mode, size, alignment, header.ptr, header.size, location) or_return
allocation := rra.parent.procedure(rra.parent.data, mode, req_size, new_alignment, orig_ptr, orig_size, location) or_return
#no_bounds_check data = allocation[a:]
([^]Header)(raw_data(data))[-1] = {
size = size,
ptr = raw_data(allocation),
size = size,
alignment = new_alignment,
}
sanitizer.address_poison(raw_data(allocation), a)
return
case .Free_All:
return rra.parent.procedure(rra.parent.data, mode, size, alignment, old_memory, old_size, location)
case .Query_Info:
info := (^Allocator_Query_Info)(old_memory)
if info != nil && info.pointer != nil {
header := get_unpoisoned_header(info.pointer)
info.size = header.size
info.alignment = header.alignment
}
return
case .Free_All, .Query_Info, .Query_Features:
return rra.parent.procedure(rra.parent.data, mode, size, alignment, old_memory, old_size, location)
case .Query_Features:
data, err = rra.parent.procedure(rra.parent.data, mode, size, alignment, old_memory, old_size, location)
if err != nil {
set := (^Allocator_Mode_Set)(old_memory)
set^ += {.Query_Info}
}
return
case: unreachable()
}
+2 -24
View File
@@ -49,8 +49,8 @@ init_dns_configuration :: proc() {
/*
Resolve %ENVIRONMENT% placeholders in their paths.
*/
dns_configuration.resolv_conf, _ = replace_environment_path(dns_configuration.resolv_conf)
dns_configuration.hosts_file, _ = replace_environment_path(dns_configuration.hosts_file)
dns_configuration.resolv_conf = os.replace_environment_placeholders(dns_configuration.resolv_conf)
dns_configuration.hosts_file = os.replace_environment_placeholders(dns_configuration.hosts_file)
}
@(fini, private)
@@ -63,28 +63,6 @@ destroy_dns_configuration :: proc() {
dns_configuration := DEFAULT_DNS_CONFIGURATION
// Always allocates for consistency.
replace_environment_path :: proc(path: string, allocator := context.allocator) -> (res: string, ok: bool) {
// Nothing to replace. Return a clone of the original.
if strings.count(path, "%") != 2 {
return strings.clone(path, allocator), true
}
left := strings.index(path, "%") + 1
assert(left > 0 && left <= len(path)) // should be covered by there being two %
right := strings.index(path[left:], "%") + 1
assert(right > 0 && right <= len(path)) // should be covered by there being two %
env_key := path[left: right]
env_val := os.get_env(env_key, allocator)
defer delete(env_val)
res, _ = strings.replace(path, path[left - 1: right + 1], env_val, 1, allocator)
return res, true
}
/*
Resolves a hostname to exactly one IP4 and IP6 endpoint.
It's then up to you which one you use.
+53
View File
@@ -4,6 +4,7 @@ import "base:intrinsics"
import "base:runtime"
import "core:io"
import "core:strconv"
import "core:strings"
import "core:unicode/utf8"
@@ -210,3 +211,55 @@ heap_free :: runtime.heap_free
processor_core_count :: proc() -> int {
return _processor_core_count()
}
// Always allocates for consistency.
replace_environment_placeholders :: proc(path: string, allocator := context.allocator) -> (res: string) {
path := path
sb: strings.Builder
strings.builder_init_none(&sb, allocator)
for len(path) > 0 {
switch path[0] {
case '%': // Windows
when ODIN_OS == .Windows {
for r, i in path[1:] {
if r == '%' {
env_key := path[1:i+1]
env_val := get_env(env_key, context.temp_allocator)
strings.write_string(&sb, env_val)
path = path[i+1:] // % is part of key, so skip 1 character extra
}
}
} else {
strings.write_rune(&sb, rune(path[0]))
}
case '$': // Posix
when ODIN_OS != .Windows {
env_key := ""
dollar_loop: for r, i in path[1:] {
switch r {
case 'A'..='Z', 'a'..='z', '0'..='9', '_': // Part of key ident
case:
env_key = path[1:i+1]
break dollar_loop
}
}
if len(env_key) > 0 {
env_val := get_env(env_key, context.temp_allocator)
strings.write_string(&sb, env_val)
path = path[len(env_key):]
}
} else {
strings.write_rune(&sb, rune(path[0]))
}
case:
strings.write_rune(&sb, rune(path[0]))
}
path = path[1:]
}
return strings.to_string(sb)
}
+52
View File
@@ -1,6 +1,7 @@
package os2
import "base:runtime"
import "core:strings"
// get_env retrieves the value of the environment variable named by the key
// It returns the value, which will be empty if the variable is not present
@@ -45,4 +46,55 @@ environ :: proc(allocator: runtime.Allocator) -> ([]string, Error) {
return _environ(allocator)
}
// Always allocates for consistency.
replace_environment_placeholders :: proc(path: string, allocator: runtime.Allocator) -> (res: string) {
path := path
sb: strings.Builder
strings.builder_init_none(&sb, allocator)
for len(path) > 0 {
switch path[0] {
case '%': // Windows
when ODIN_OS == .Windows {
for r, i in path[1:] {
if r == '%' {
env_key := path[1:i+1]
env_val := get_env(env_key, context.temp_allocator)
strings.write_string(&sb, env_val)
path = path[i+1:] // % is part of key, so skip 1 character extra
}
}
} else {
strings.write_rune(&sb, rune(path[0]))
}
case '$': // Posix
when ODIN_OS != .Windows {
env_key := ""
dollar_loop: for r, i in path[1:] {
switch r {
case 'A'..='Z', 'a'..='z', '0'..='9', '_': // Part of key ident
case:
env_key = path[1:i+1]
break dollar_loop
}
}
if len(env_key) > 0 {
env_val := get_env(env_key, context.temp_allocator)
strings.write_string(&sb, env_val)
path = path[len(env_key):]
}
} else {
strings.write_rune(&sb, rune(path[0]))
}
case:
strings.write_rune(&sb, rune(path[0]))
}
path = path[1:]
}
return strings.to_string(sb)
}
+1 -9
View File
@@ -4,7 +4,6 @@ package os2
import "base:runtime"
import "core:encoding/ini"
import "core:strings"
import "core:sys/posix"
_user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
#partial switch ODIN_OS {
@@ -169,14 +168,7 @@ _xdg_user_dirs_lookup :: proc(xdg_key: string, allocator: runtime.Allocator) ->
for k, v in ini.iterate(&it) {
if k == xdg_key {
we: posix.wordexp_t
defer posix.wordfree(&we)
if _err := posix.wordexp(strings.clone_to_cstring(v, temp_allocator), &we, nil); _err != nil || we.we_wordc != 1 {
return "", .Wordexp_Failed
}
return strings.clone_from_cstring(we.we_wordv[0], allocator)
return replace_environment_placeholders(v, allocator), nil
}
}
return
+5
View File
@@ -252,4 +252,9 @@ current_thread_id :: proc "contextless" () -> int {
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
return "", false
}
get_env :: proc(key: string, allocator := context.allocator) -> string {
value, _ := lookup_env(key, allocator)
return value
}
+9
View File
@@ -239,3 +239,12 @@ exit :: proc "contextless" (code: int) -> ! {
runtime._cleanup_runtime_contextless()
wasi.proc_exit(wasi.exitcode_t(code))
}
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
return "", false
}
get_env :: proc(key: string, allocator := context.allocator) -> string {
value, _ := lookup_env(key, allocator)
return value
}
+1
View File
@@ -34,6 +34,7 @@ init_platform :: proc() {
} else {
os_version.platform = .MacOS
switch version.majorVersion {
case 26: ws(&b, "macOS Tahoe")
case 15: ws(&b, "macOS Sequoia")
case 14: ws(&b, "macOS Sonoma")
case 13: ws(&b, "macOS Ventura")