From 8050622fe62522b694f212f06ee902de99c42363 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Thu, 28 Nov 2024 20:07:54 +0100 Subject: [PATCH] add `map_entry` procedure --- base/runtime/core_builtin.odin | 26 ++++++++++++++++++++++++++ base/runtime/dynamic_map_internal.odin | 23 +++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/base/runtime/core_builtin.odin b/base/runtime/core_builtin.odin index d28dadd02..e7c2c0b1f 100644 --- a/base/runtime/core_builtin.odin +++ b/base/runtime/core_builtin.odin @@ -936,6 +936,32 @@ map_upsert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) return } +/* +Retrieves a pointer to the key and value for a possibly just inserted entry into the map. + +If the `key` was not in the map `m`, an entry is inserted with the zero value and `just_inserted` will be `true`. +Otherwise the existing entry is left untouched and pointers to its key and value are returned. + +If the map has to grow in order to insert the entry and the allocation fails, `err` is set and returned. + +If `err` is `nil`, `key_ptr` and `value_ptr` are valid pointers and will not be `nil`. + +WARN: User modification of the key pointed at by `key_ptr` should only be done if the new key is equal to (in hash) the old key. +If that is not the case you will corrupt the map. +*/ +@(builtin, require_results) +map_entry :: proc(m: ^$T/map[$K]$V, key: K, loc := #caller_location) -> (key_ptr: ^K, value_ptr: ^V, just_inserted: bool, err: Allocator_Error) { + key := key + zero: V + + _key_ptr, _value_ptr: rawptr + _key_ptr, _value_ptr, just_inserted, err = __dynamic_map_entry((^Raw_Map)(m), map_info(T), &key, &zero, loc) + + key_ptr = (^K)(_key_ptr) + value_ptr = (^V)(_value_ptr) + return +} + @builtin card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int { diff --git a/base/runtime/dynamic_map_internal.odin b/base/runtime/dynamic_map_internal.odin index 3dded7716..c9d264c6a 100644 --- a/base/runtime/dynamic_map_internal.odin +++ b/base/runtime/dynamic_map_internal.odin @@ -941,6 +941,29 @@ __dynamic_map_set_extra :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^ return nil, rawptr(result) } +__dynamic_map_entry :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, key: rawptr, zero: rawptr, loc := #caller_location) -> (key_ptr: rawptr, value_ptr: rawptr, just_inserted: bool, err: Allocator_Error) { + hash := info.key_hasher(key, map_seed(m^)) + + if key_ptr, value_ptr = __dynamic_map_get_key_and_value(m, info, hash, key); value_ptr != nil { + return + } + + has_grown: bool + if err, has_grown = __dynamic_map_check_grow(m, info, loc); err != nil { + return + } else if has_grown { + hash = info.key_hasher(key, map_seed(m^)) + } + + value_ptr = rawptr(map_insert_hash_dynamic(m, info, hash, uintptr(key), uintptr(zero))) + assert(value_ptr != nil) + key_ptr = rawptr(map_cell_index_dynamic(map_data(m^), info.ks, map_desired_position(m^, hash))) + + m.len += 1 + just_inserted = true + return +} + // IMPORTANT: USED WITHIN THE COMPILER @(private)