// String interning thingy package name import "core:mem" import "core:strings" import "core:sync" import "libs:tracy" // When enabled name globals will be initialized automatically // TODO: this breaks wasm, because it's trying to allocate something before we set up a working allocator... NAME_STATIC_INIT :: #config(NAME_STATIC_INIT, false) MAX_STATIC_NAMES :: #config(MAX_STATIC_NAMES, 1024) Name :: distinct u32 NONE :: Name(0) Container :: struct { lock: sync.Atomic_RW_Mutex, names_lookup: map[string]Name, names_allocator: mem.Dynamic_Arena, names_array: [dynamic]string, } @(private = "file") global_container: ^Container setup_global_container :: proc(cnt: ^Container) { global_container = cnt } init :: proc(cnt: ^Container) { mem.dynamic_arena_init(&cnt.names_allocator) assert(len(cnt.names_array) == 0) append(&cnt.names_array, "None") } destroy :: proc() { assert(global_container != nil) delete(global_container.names_array) delete(global_container.names_lookup) mem.dynamic_arena_destroy(&global_container.names_allocator) global_container = nil } when NAME_STATIC_INIT { @(private = "file") static_container: Container @(init) init_static :: proc() { init(&static_container) setup_global_container(&static_container) } @(fini) fini_static :: proc() { destroy() } } from_string :: proc(str: string) -> Name { tracy.Zone() existing: Name ok: bool { sync.guard(&global_container.lock) existing, ok = global_container.names_lookup[str] } if ok { return existing } else { sync.guard(&global_container.lock) existing, ok = global_container.names_lookup[str] if ok { return existing } new_str := strings.clone_to_cstring( str, mem.dynamic_arena_allocator(&global_container.names_allocator), ) idx := u32(len(global_container.names_array)) append(&global_container.names_array, string(new_str)) global_container.names_lookup[str] = Name(idx) return Name(idx) } } from_cstring :: proc(str: cstring) -> Name { return from_string(string(str)) } to_string :: proc(name: Name) -> string { tracy.Zone() sync.guard(&global_container.lock) return global_container.names_array[name] } to_cstring :: proc(name: Name) -> cstring { return strings.unsafe_string_to_cstring(to_string(name)) }