aboutsummaryrefslogtreecommitdiff
path: root/core/runtime
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2022-09-17 15:30:53 +0100
committerGitHub <noreply@github.com>2022-09-17 15:30:53 +0100
commitcb207afdf390462e2eb1bcafb1708f55fe63bef1 (patch)
tree9130a1f5da7da6867316ba42b318e3063fcafe68 /core/runtime
parent756c1b7bcb8c881076594bf0ed73f64971e77f1b (diff)
parentcd484979a840a093967dcd7076e7cc39cb900096 (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.odin2
-rw-r--r--core/runtime/core_builtin.odin15
-rw-r--r--core/runtime/dynamic_array_internal.odin2
-rw-r--r--core/runtime/dynamic_map_internal.odin218
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
}