diff options
| author | gingerBill <gingerBill@users.noreply.github.com> | 2022-09-17 15:30:53 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-09-17 15:30:53 +0100 |
| commit | cb207afdf390462e2eb1bcafb1708f55fe63bef1 (patch) | |
| tree | 9130a1f5da7da6867316ba42b318e3063fcafe68 /core/runtime | |
| parent | 756c1b7bcb8c881076594bf0ed73f64971e77f1b (diff) | |
| parent | cd484979a840a093967dcd7076e7cc39cb900096 (diff) | |
Merge pull request #2055 from odin-lang/map-index-internal
Map Internals Improvements
Diffstat (limited to 'core/runtime')
| -rw-r--r-- | core/runtime/core.odin | 2 | ||||
| -rw-r--r-- | core/runtime/core_builtin.odin | 15 | ||||
| -rw-r--r-- | core/runtime/dynamic_array_internal.odin | 2 | ||||
| -rw-r--r-- | core/runtime/dynamic_map_internal.odin | 218 |
4 files changed, 110 insertions, 127 deletions
diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 0310aff6d..81cce8caf 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -394,7 +394,7 @@ Raw_Dynamic_Array :: struct { } Raw_Map :: struct { - hashes: []int, + hashes: []Map_Index, entries: Raw_Dynamic_Array, } diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin index 4c736b6f6..f8e39b8b2 100644 --- a/core/runtime/core_builtin.odin +++ b/core/runtime/core_builtin.odin @@ -289,14 +289,14 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) { entries := (^Raw_Dynamic_Array)(&raw_map.entries) entries.len = 0 for _, i in raw_map.hashes { - raw_map.hashes[i] = -1 + raw_map.hashes[i] = MAP_SENTINEL } } @builtin reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) { if m != nil { - __dynamic_map_reserve(__get_map_header(m), capacity, loc) + __dynamic_map_reserve(__get_map_header(m), uint(capacity), loc) } } @@ -325,9 +325,8 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value: if m != nil { key := key h := __get_map_header(m) - hash := __get_map_hash(&key) - fr := __dynamic_map_find(h, hash) - if fr.entry_index >= 0 { + fr := __map_find(h, &key) + if fr.entry_index != MAP_SENTINEL { entry := __dynamic_map_get_entry(h, fr.entry_index) deleted_key = (^K)(uintptr(entry)+h.key_offset)^ deleted_value = (^V)(uintptr(entry)+h.value_offset)^ @@ -335,7 +334,6 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value: __dynamic_map_erase(h, fr) } } - return } @@ -674,9 +672,8 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (ptr: ^V) { key, value := key, value h := __get_map_header(m) - hash := __get_map_hash(&key) - - data := uintptr(__dynamic_map_set(h, hash, &value, loc)) + + data := uintptr(__dynamic_map_set(h, __get_map_key_hash(&key), &key, &value, loc)) return (^V)(data + h.value_offset) } diff --git a/core/runtime/dynamic_array_internal.odin b/core/runtime/dynamic_array_internal.odin index b6a685fcf..267ee0785 100644 --- a/core/runtime/dynamic_array_internal.odin +++ b/core/runtime/dynamic_array_internal.odin @@ -59,6 +59,8 @@ __dynamic_array_shrink :: proc(array_: rawptr, elem_size, elem_align: int, new_c return } + new_cap := new_cap + new_cap = max(new_cap, 0) old_size := array.cap * elem_size new_size := new_cap * elem_size allocator := array.allocator diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index d2aaa49f4..6ca9455ef 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -11,30 +11,27 @@ Map_Hash :: struct { key_ptr: rawptr, // address of Map_Entry_Header.key } -__get_map_hash :: proc "contextless" (k: ^$K) -> (map_hash: Map_Hash) { +__get_map_key_hash :: #force_inline proc "contextless" (k: ^$K) -> uintptr { hasher := intrinsics.type_hasher_proc(K) - map_hash.key_ptr = k - map_hash.hash = hasher(k, 0) - return + return hasher(k, 0) } -__get_map_hash_from_entry :: proc "contextless" (h: Map_Header, entry: ^Map_Entry_Header) -> (hash: Map_Hash) { - hash.hash = entry.hash - hash.key_ptr = rawptr(uintptr(entry) + h.key_offset) - return +__get_map_entry_key_ptr :: #force_inline proc "contextless" (h: Map_Header, entry: ^Map_Entry_Header) -> rawptr { + return rawptr(uintptr(entry) + h.key_offset) } - +Map_Index :: distinct uint +MAP_SENTINEL :: ~Map_Index(0) Map_Find_Result :: struct { - hash_index: int, - entry_prev: int, - entry_index: int, + hash_index: Map_Index, + entry_prev: Map_Index, + entry_index: Map_Index, } Map_Entry_Header :: struct { hash: uintptr, - next: int, + next: Map_Index, /* key: Key_Value, value: Value_Type, @@ -183,7 +180,7 @@ __get_map_header_runtime :: proc "contextless" (m: ^Raw_Map, ti: Type_Info_Map) } -__slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, loc := #caller_location) -> bool { +__slice_resize :: proc "odin" (array_: ^$T/[]$E, new_count: int, allocator: Allocator, loc := #caller_location) -> bool { array := (^Raw_Slice)(array_) if new_count < array.len { @@ -205,86 +202,95 @@ __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, l return false } -__dynamic_map_reset_entries :: proc(using header: Map_Header, loc := #caller_location) { - for i in 0..<len(m.hashes) { - m.hashes[i] = -1 +__dynamic_map_reset_entries :: proc "contextless" (h: Map_Header, loc := #caller_location) { + for i in 0..<len(h.m.hashes) { + h.m.hashes[i] = MAP_SENTINEL } - for i in 0..<m.entries.len { - entry_header := __dynamic_map_get_entry(header, i) - entry_hash := __get_map_hash_from_entry(header, entry_header) - entry_header.next = -1 - - fr := __dynamic_map_find(header, entry_hash) - if fr.entry_prev < 0 { - m.hashes[fr.hash_index] = i - } else { - e := __dynamic_map_get_entry(header, fr.entry_prev) + for i in 0..<Map_Index(h.m.entries.len) { + entry_header := __dynamic_map_get_entry(h, i) + entry_header.next = MAP_SENTINEL + + fr := __dynamic_map_find_from_entry(h, entry_header) + if fr.entry_prev != MAP_SENTINEL { + e := __dynamic_map_get_entry(h, fr.entry_prev) e.next = i + } else { + h.m.hashes[fr.hash_index] = i } } } -__dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller_location) { +__dynamic_map_reserve :: proc "odin" (h: Map_Header, cap: uint, loc := #caller_location) { c := context - if m.entries.allocator.procedure != nil { - c.allocator = m.entries.allocator + if h.m.entries.allocator.procedure != nil { + c.allocator = h.m.entries.allocator } context = c cap := cap cap = ceil_to_pow2(cap) - __dynamic_array_reserve(&m.entries, entry_size, entry_align, cap, loc) + __dynamic_array_reserve(&h.m.entries, h.entry_size, h.entry_align, int(cap), loc) - if m.entries.len*2 < len(m.hashes) { + if h.m.entries.len*2 < len(h.m.hashes) { return } - if __slice_resize(&m.hashes, cap*2, m.entries.allocator, loc) { - __dynamic_map_reset_entries(header, loc) + if __slice_resize(&h.m.hashes, int(cap*2), h.m.entries.allocator, loc) { + __dynamic_map_reset_entries(h, loc) } } -__dynamic_map_shrink :: proc(using header: Map_Header, cap: int, loc := #caller_location) -> (did_shrink: bool) { +__dynamic_map_shrink :: proc "odin" (h: Map_Header, cap: int, loc := #caller_location) -> (did_shrink: bool) { c := context - if m.entries.allocator.procedure != nil { - c.allocator = m.entries.allocator + if h.m.entries.allocator.procedure != nil { + c.allocator = h.m.entries.allocator } context = c - return __dynamic_array_shrink(&m.entries, entry_size, entry_align, cap, loc) -} - -__dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #caller_location) { - #force_inline __dynamic_map_reserve(header, new_count, loc) + return __dynamic_array_shrink(&h.m.entries, h.entry_size, h.entry_align, cap, loc) } -__dynamic_map_get :: proc(h: Map_Header, hash: Map_Hash) -> rawptr { - index := __dynamic_map_find(h, hash).entry_index - if index >= 0 { +// USED INTERNALLY BY THE COMPILER +__dynamic_map_get :: proc "contextless" (h: Map_Header, key_hash: uintptr, key_ptr: rawptr) -> rawptr { + index := __dynamic_map_find(h, key_hash, key_ptr).entry_index + if index != MAP_SENTINEL { data := uintptr(__dynamic_map_get_entry(h, index)) return rawptr(data + h.value_offset) } return nil } -__dynamic_map_set :: proc(h: Map_Header, hash: Map_Hash, value: rawptr, loc := #caller_location) -> ^Map_Entry_Header #no_bounds_check { - index: int +// USED INTERNALLY BY THE COMPILER +__dynamic_map_set :: proc "odin" (h: Map_Header, key_hash: uintptr, key_ptr: rawptr, value: rawptr, loc := #caller_location) -> ^Map_Entry_Header #no_bounds_check { + add_entry :: proc "odin" (h: Map_Header, key_hash: uintptr, key_ptr: rawptr, loc := #caller_location) -> Map_Index { + prev := Map_Index(h.m.entries.len) + c := Map_Index(__dynamic_array_append_nothing(&h.m.entries, h.entry_size, h.entry_align, loc)) + if c != prev { + end := __dynamic_map_get_entry(h, c-1) + end.hash = key_hash + mem_copy(rawptr(uintptr(end) + h.key_offset), key_ptr, h.key_size) + end.next = MAP_SENTINEL + } + return prev + } + + index := MAP_SENTINEL if len(h.m.hashes) == 0 { __dynamic_map_reserve(h, INITIAL_MAP_CAP, loc) __dynamic_map_grow(h, loc) } - fr := __dynamic_map_find(h, hash) - if fr.entry_index >= 0 { + fr := __dynamic_map_find(h, key_hash, key_ptr) + if fr.entry_index != MAP_SENTINEL { index = fr.entry_index } else { - index = __dynamic_map_add_entry(h, hash, loc) - if fr.entry_prev >= 0 { + index = add_entry(h, key_hash, key_ptr, loc) + if fr.entry_prev != MAP_SENTINEL { entry := __dynamic_map_get_entry(h, fr.entry_prev) entry.next = index - } else if fr.hash_index >= 0 { + } else if fr.hash_index != MAP_SENTINEL { h.m.hashes[fr.hash_index] = index } else { return nil @@ -292,12 +298,12 @@ __dynamic_map_set :: proc(h: Map_Header, hash: Map_Hash, value: rawptr, loc := # } e := __dynamic_map_get_entry(h, index) - e.hash = hash.hash + e.hash = key_hash key := rawptr(uintptr(e) + h.key_offset) - mem_copy(key, hash.key_ptr, h.key_size) - val := rawptr(uintptr(e) + h.value_offset) + + mem_copy(key, key_ptr, h.key_size) mem_copy(val, value, h.value_size) if __dynamic_map_full(h) { @@ -309,13 +315,11 @@ __dynamic_map_set :: proc(h: Map_Header, hash: Map_Hash, value: rawptr, loc := # @(private="file") -ceil_to_pow2 :: proc "contextless" (n: int) -> int { - n := n - if n <= 0 { - return 0 - } else if n <= 2 { +ceil_to_pow2 :: proc "contextless" (n: uint) -> uint { + if n <= 2 { return n } + n := n n -= 1 n |= n >> 1 n |= n >> 2 @@ -329,33 +333,33 @@ ceil_to_pow2 :: proc "contextless" (n: int) -> int { return n } -__dynamic_map_grow :: proc(using h: Map_Header, loc := #caller_location) { - // TODO(bill): Determine an efficient growing rate - new_count := max(m.entries.cap * 2, INITIAL_MAP_CAP) - __dynamic_map_rehash(h, new_count, loc) +__dynamic_map_grow :: proc "odin" (h: Map_Header, loc := #caller_location) { + new_count := max(uint(h.m.entries.cap) * 2, INITIAL_MAP_CAP) + // Rehash through Reserve + __dynamic_map_reserve(h, new_count, loc) } -__dynamic_map_full :: #force_inline proc "contextless" (using h: Map_Header) -> bool { - return int(0.75 * f64(len(m.hashes))) <= m.entries.len +__dynamic_map_full :: #force_inline proc "contextless" (h: Map_Header) -> bool { + return int(0.75 * f64(len(h.m.hashes))) <= h.m.entries.len } +__dynamic_map_find_from_entry :: proc "contextless" (h: Map_Header, e: ^Map_Entry_Header) -> Map_Find_Result #no_bounds_check { + key_ptr := __get_map_entry_key_ptr(h, e) + return __dynamic_map_find(h, e.hash, key_ptr) -__dynamic_map_hash_equal :: proc "contextless" (h: Map_Header, a, b: Map_Hash) -> bool { - return a.hash == b.hash && h.equal(a.key_ptr, b.key_ptr) } -__dynamic_map_find :: proc(using h: Map_Header, hash: Map_Hash) -> Map_Find_Result #no_bounds_check { - fr := Map_Find_Result{-1, -1, -1} - if n := uintptr(len(m.hashes)); n != 0 { - fr.hash_index = int(hash.hash & (n-1)) - fr.entry_index = m.hashes[fr.hash_index] - for fr.entry_index >= 0 { +__dynamic_map_find :: proc "contextless" (h: Map_Header, key_hash: uintptr, key_ptr: rawptr) -> Map_Find_Result #no_bounds_check { + fr := Map_Find_Result{MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL} + if n := uintptr(len(h.m.hashes)); n != 0 { + fr.hash_index = Map_Index(key_hash & (n-1)) + fr.entry_index = h.m.hashes[fr.hash_index] + for fr.entry_index != MAP_SENTINEL { entry := __dynamic_map_get_entry(h, fr.entry_index) - entry_hash := __get_map_hash_from_entry(h, entry) - if __dynamic_map_hash_equal(h, entry_hash, hash) { + entry_key_ptr := __get_map_entry_key_ptr(h, entry) + if entry.hash == key_hash && h.equal(entry_key_ptr, key_ptr) { return fr } - // assert(entry.next < m.entries.len) fr.entry_prev = fr.entry_index fr.entry_index = entry.next @@ -364,58 +368,38 @@ __dynamic_map_find :: proc(using h: Map_Header, hash: Map_Hash) -> Map_Find_Resu return fr } -__dynamic_map_add_entry :: proc(using h: Map_Header, hash: Map_Hash, loc := #caller_location) -> int { - prev := m.entries.len - c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align, loc) - if c != prev { - end := __dynamic_map_get_entry(h, c-1) - end.hash = hash.hash - mem_copy(rawptr(uintptr(end) + key_offset), hash.key_ptr, key_size) - end.next = -1 - } - return prev -} - -__dynamic_map_delete_key :: proc(using h: Map_Header, hash: Map_Hash) { - fr := __dynamic_map_find(h, hash) - if fr.entry_index >= 0 { - __dynamic_map_erase(h, fr) - } -} - -__dynamic_map_get_entry :: proc(using h: Map_Header, index: int) -> ^Map_Entry_Header { - // assert(0 <= index && index < m.entries.len) - return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*entry_size)) +// Utility procedure used by other runtime procedures +__map_find :: proc "contextless" (h: Map_Header, key_ptr: ^$K) -> Map_Find_Result #no_bounds_check { + hash := __get_map_key_hash(key_ptr) + return #force_inline __dynamic_map_find(h, hash, key_ptr) } -__dynamic_map_copy_entry :: proc(h: Map_Header, new, old: ^Map_Entry_Header) { - mem_copy(new, old, h.entry_size) +__dynamic_map_get_entry :: #force_inline proc "contextless" (h: Map_Header, index: Map_Index) -> ^Map_Entry_Header { + return (^Map_Entry_Header)(uintptr(h.m.entries.data) + uintptr(index*Map_Index(h.entry_size))) } -__dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds_check { - if fr.entry_prev < 0 { - m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next - } else { +__dynamic_map_erase :: proc "contextless" (h: Map_Header, fr: Map_Find_Result) #no_bounds_check { + if fr.entry_prev != MAP_SENTINEL { prev := __dynamic_map_get_entry(h, fr.entry_prev) curr := __dynamic_map_get_entry(h, fr.entry_index) prev.next = curr.next - } - if fr.entry_index == m.entries.len-1 { - // NOTE(bill): No need to do anything else, just pop } else { + h.m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next + } + last_index := Map_Index(h.m.entries.len-1) + if fr.entry_index != last_index { old := __dynamic_map_get_entry(h, fr.entry_index) - end := __dynamic_map_get_entry(h, m.entries.len-1) - __dynamic_map_copy_entry(h, old, end) - - old_hash := __get_map_hash_from_entry(h, old) + end := __dynamic_map_get_entry(h, last_index) + mem_copy(old, end, h.entry_size) - if last := __dynamic_map_find(h, old_hash); last.entry_prev >= 0 { - last_entry := __dynamic_map_get_entry(h, last.entry_prev) - last_entry.next = fr.entry_index + last := __dynamic_map_find_from_entry(h, old) + if last.entry_prev != MAP_SENTINEL { + e := __dynamic_map_get_entry(h, last.entry_prev) + e.next = fr.entry_index } else { - m.hashes[last.hash_index] = fr.entry_index + h.m.hashes[last.hash_index] = fr.entry_index } } - m.entries.len -= 1 + h.m.entries.len -= 1 } |