From 5c2f55148afa0deee48625c30d71097e5d67877f Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sun, 26 May 2024 13:35:08 -0400 Subject: [PATCH] zpl_hmap prefix to hmap_zpl, plus some todos to go back to builtin containers --- code/sectr/engine/client_api.odin | 2 +- code/sectr/grime/array.odin | 12 +++-- code/sectr/grime/hashmap_chained.odin | 1 - code/sectr/grime/hashmap_zpl.odin | 73 ++++++++++++++------------ code/sectr/grime/string_interning.odin | 10 ++-- code/sectr/ui/core/box.odin | 12 ++--- code/sectr/ui/core/signal.odin | 2 +- code/sectr/ui/core/state.odin | 4 +- 8 files changed, 62 insertions(+), 54 deletions(-) diff --git a/code/sectr/engine/client_api.odin b/code/sectr/engine/client_api.odin index d771f8b..4a89a5a 100644 --- a/code/sectr/engine/client_api.odin +++ b/code/sectr/engine/client_api.odin @@ -419,7 +419,7 @@ reload :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem, // hmap_chained_reload( font_provider_data.font_cache, persistent_allocator()) slab_reload( string_cache.slab, persistent_allocator() ) - zpl_hmap_reload( & string_cache.table, persistent_slab_allocator()) + hamp_zpl_reload( & string_cache.table, persistent_slab_allocator()) slab_reload( frame_slab, frame_allocator()) slab_reload( transient_slab, transient_allocator()) diff --git a/code/sectr/grime/array.odin b/code/sectr/grime/array.odin index d80ba35..b1fb55c 100644 --- a/code/sectr/grime/array.odin +++ b/code/sectr/grime/array.odin @@ -1,6 +1,12 @@ -// Based on gencpp's and thus zpl's Array implementation -// Made becasue of the map issue with fonts during hot-reload. -// I didn't want to make the HMapZPL impl with the [dynamic] array for now to isolate the hot-reload issue (when I was diagnoising) +/* +Based on gencpp's and thus zpl's Array implementation +Made becasue of the map issue with fonts during hot-reload. +I didn't want to make the HMapZPL impl with the [dynamic] array for now to isolate the hot-reload issue (when I was diagnoising) + +Update 5-26-2024: +TODO(Ed): Raw_Dynamic_Array is defined within base:runtime/core.odin and exposes what we need for worst case hot-reloads. +So its best to go back to regular dynamic arrays at some point. +*/ package sectr import "core:c/libc" diff --git a/code/sectr/grime/hashmap_chained.odin b/code/sectr/grime/hashmap_chained.odin index c4ef460..4ca2329 100644 --- a/code/sectr/grime/hashmap_chained.odin +++ b/code/sectr/grime/hashmap_chained.odin @@ -2,7 +2,6 @@ Separate chaining hashtable with tombstone (vacancy aware) This is an alternative to odin's map and the zpl hashtable I first used for this codebase. -I haven't felt the need to go back to dealing with odin's map for my edge case hot reload/memory replay failure. So this is a hahstable loosely based at what I saw in the raddbg codebase. It uses a fixed-size lookup table for the base layer of entries that can be chained. diff --git a/code/sectr/grime/hashmap_zpl.odin b/code/sectr/grime/hashmap_zpl.odin index 48b568c..c36412f 100644 --- a/code/sectr/grime/hashmap_zpl.odin +++ b/code/sectr/grime/hashmap_zpl.odin @@ -6,6 +6,9 @@ with hot-reloads... 5-21-2024 Update: Still haven't taken the time to see why but just to add the original case for the above was I believe exclusively when I didn't set the base addresss of vmem OR when I was attempting to use Casey's brute force replay feature with memory. +5-26-2024 Update: +TODO(Ed): There is a Raw_Map structure defined in base:runtime/core.odin +We can use the regulare dynamic --------------------------------------------------------------------------------------------------------- This implementation uses two ZPL-Based Arrays to hold entires and the actual hash table. @@ -44,11 +47,11 @@ HMapZPL :: struct ( $ Type : typeid ) { entries : Array( HMapZPL_Entry(Type) ), } -zpl_hmap_init :: proc( $ Type : typeid, allocator : Allocator ) -> ( HMapZPL( Type), AllocatorError ) { - return zpl_hmap_init_reserve( Type, allocator ) +hamp_zpl_init :: proc( $ Type : typeid, allocator : Allocator ) -> ( HMapZPL( Type), AllocatorError ) { + return hamp_zpl_init_reserve( Type, allocator ) } -zpl_hmap_init_reserve :: proc +hamp_zpl_init_reserve :: proc ( $ Type : typeid, allocator : Allocator, num : u64, dbg_name : string = "" ) -> ( HMapZPL( Type), AllocatorError ) { result : HMapZPL(Type) @@ -70,7 +73,7 @@ zpl_hmap_init_reserve :: proc return result, AllocatorError.None } -zpl_hmap_clear :: proc( using self : ^ HMapZPL( $ Type ) ) { +hamp_zpl_clear :: proc( using self : ^ HMapZPL( $ Type ) ) { for id := 0; id < table.num; id += 1 { table[id] = -1 } @@ -79,17 +82,17 @@ zpl_hmap_clear :: proc( using self : ^ HMapZPL( $ Type ) ) { array_clear( entries ) } -zpl_hmap_destroy :: proc( using self : ^ HMapZPL( $ Type ) ) { +hamp_zpl_destroy :: proc( using self : ^ HMapZPL( $ Type ) ) { if table.data != nil && table.capacity > 0 { array_free( table ) array_free( entries ) } } -zpl_hmap_get :: proc ( using self : ^ HMapZPL( $ Type ), key : u64 ) -> ^ Type +hamp_zpl_get :: proc ( using self : ^ HMapZPL( $ Type ), key : u64 ) -> ^ Type { // profile(#procedure) - id := zpl_hmap_find( self, key ).entry_index + id := hamp_zpl_find( self, key ).entry_index if id >= 0 { return & entries.data[id].value } @@ -97,35 +100,35 @@ zpl_hmap_get :: proc ( using self : ^ HMapZPL( $ Type ), key : u64 ) -> ^ Type return nil } -zpl_hmap_map :: proc( using self : ^ HMapZPL( $ Type), map_proc : HMapZPL_MapProc ) { +hamp_zpl_map :: proc( using self : ^ HMapZPL( $ Type), map_proc : HMapZPL_MapProc ) { ensure( map_proc != nil, "Mapping procedure must not be null" ) for id := 0; id < entries.num; id += 1 { map_proc( Type, entries[id].key, entries[id].value ) } } -zpl_hmap_map_mut :: proc( using self : ^ HMapZPL( $ Type), map_proc : HMapZPL_MapMutProc ) { +hamp_zpl_map_mut :: proc( using self : ^ HMapZPL( $ Type), map_proc : HMapZPL_MapMutProc ) { ensure( map_proc != nil, "Mapping procedure must not be null" ) for id := 0; id < entries.num; id += 1 { map_proc( Type, entries[id].key, & entries[id].value ) } } -zpl_hmap_grow :: proc( using self : ^ HMapZPL( $ Type ) ) -> AllocatorError { +hamp_zpl_grow :: proc( using self : ^ HMapZPL( $ Type ) ) -> AllocatorError { new_num := array_grow_formula( entries.num ) - return zpl_hmap_rehash( self, new_num ) + return hamp_zpl_rehash( self, new_num ) } -zpl_hmap_rehash :: proc( ht : ^ HMapZPL( $ Type ), new_num : u64 ) -> AllocatorError +hamp_zpl_rehash :: proc( ht : ^ HMapZPL( $ Type ), new_num : u64 ) -> AllocatorError { profile(#procedure) // For now the prototype should never allow this to happen. ensure( false, "ZPL HMAP IS REHASHING" ) last_added_index : i64 - new_ht, init_result := zpl_hmap_init_reserve( Type, ht.table.backing, new_num, ht.table.dbg_name ) + new_ht, init_result := hamp_zpl_init_reserve( Type, ht.table.backing, new_num, ht.table.dbg_name ) if init_result != AllocatorError.None { - ensure( false, "New zpl_hmap failed to allocate" ) + ensure( false, "New hamp_zpl failed to allocate" ) return init_result } @@ -133,8 +136,8 @@ zpl_hmap_rehash :: proc( ht : ^ HMapZPL( $ Type ), new_num : u64 ) -> AllocatorE find_result : HMapZPL_FindResult entry := & ht.entries.data[id] - find_result = zpl_hmap_find( & new_ht, entry.key ) - last_added_index = zpl_hmap_add_entry( & new_ht, entry.key ) + find_result = hamp_zpl_find( & new_ht, entry.key ) + last_added_index = hamp_zpl_add_entry( & new_ht, entry.key ) if find_result.prev_index < 0 { new_ht.table.data[ find_result.hash_index ] = last_added_index @@ -147,13 +150,13 @@ zpl_hmap_rehash :: proc( ht : ^ HMapZPL( $ Type ), new_num : u64 ) -> AllocatorE new_ht.entries.data[ last_added_index ].value = entry.value } - zpl_hmap_destroy( ht ) + hamp_zpl_destroy( ht ) (ht ^) = new_ht return AllocatorError.None } -zpl_hmap_rehash_fast :: proc( using self : ^ HMapZPL( $ Type ) ) +hamp_zpl_rehash_fast :: proc( using self : ^ HMapZPL( $ Type ) ) { for id := 0; id < entries.num; id += 1 { entries[id].Next = -1; @@ -163,7 +166,7 @@ zpl_hmap_rehash_fast :: proc( using self : ^ HMapZPL( $ Type ) ) } for id := 0; id < entries.num; id += 1 { entry := & entries[id] - find_result := zpl_hmap_find( entry.key ) + find_result := hamp_zpl_find( entry.key ) if find_result.prev_index < 0 { table[ find_result.hash_index ] = id @@ -175,45 +178,45 @@ zpl_hmap_rehash_fast :: proc( using self : ^ HMapZPL( $ Type ) ) } // Used when the address space of the allocator changes and the backing reference must be updated -zpl_hmap_reload :: proc( using self : ^HMapZPL($Type), new_backing : Allocator ) { +hamp_zpl_reload :: proc( using self : ^HMapZPL($Type), new_backing : Allocator ) { table.backing = new_backing entries.backing = new_backing } -zpl_hmap_remove :: proc( self : ^ HMapZPL( $ Type ), key : u64 ) { - find_result := zpl_hmap_find( key ) +hamp_zpl_remove :: proc( self : ^ HMapZPL( $ Type ), key : u64 ) { + find_result := hamp_zpl_find( key ) if find_result.entry_index >= 0 { array_remove_at( & entries, find_result.entry_index ) - zpl_hmap_rehash_fast( self ) + hamp_zpl_rehash_fast( self ) } } -zpl_hmap_remove_entry :: proc( using self : ^ HMapZPL( $ Type ), id : i64 ) { +hamp_zpl_remove_entry :: proc( using self : ^ HMapZPL( $ Type ), id : i64 ) { array_remove_at( & entries, id ) } -zpl_hmap_set :: proc( using self : ^ HMapZPL( $ Type), key : u64, value : Type ) -> (^ Type, AllocatorError) +hamp_zpl_set :: proc( using self : ^ HMapZPL( $ Type), key : u64, value : Type ) -> (^ Type, AllocatorError) { // profile(#procedure) id : i64 = 0 find_result : HMapZPL_FindResult - if zpl_hmap_full( self ) + if hamp_zpl_full( self ) { - grow_result := zpl_hmap_grow( self ) + grow_result := hamp_zpl_grow( self ) if grow_result != AllocatorError.None { return nil, grow_result } } - find_result = zpl_hmap_find( self, key ) + find_result = hamp_zpl_find( self, key ) if find_result.entry_index >= 0 { id = find_result.entry_index } else { - id = zpl_hmap_add_entry( self, key ) + id = hamp_zpl_add_entry( self, key ) if find_result.prev_index >= 0 { entries.data[ find_result.prev_index ].next = id } @@ -224,15 +227,15 @@ zpl_hmap_set :: proc( using self : ^ HMapZPL( $ Type), key : u64, value : Type ) entries.data[id].value = value - if zpl_hmap_full( self ) { - alloc_error := zpl_hmap_grow( self ) + if hamp_zpl_full( self ) { + alloc_error := hamp_zpl_grow( self ) return & entries.data[id].value, alloc_error } return & entries.data[id].value, AllocatorError.None } -zpl_hmap_slot :: proc( using self : ^ HMapZPL( $ Type), key : u64 ) -> i64 { +hamp_zpl_slot :: proc( using self : ^ HMapZPL( $ Type), key : u64 ) -> i64 { for id : i64 = 0; id < table.num; id += 1 { if table.data[id] == key { return id @@ -241,14 +244,14 @@ zpl_hmap_slot :: proc( using self : ^ HMapZPL( $ Type), key : u64 ) -> i64 { return -1 } -zpl_hmap_add_entry :: proc( using self : ^ HMapZPL( $ Type), key : u64 ) -> i64 { +hamp_zpl_add_entry :: proc( using self : ^ HMapZPL( $ Type), key : u64 ) -> i64 { entry : HMapZPL_Entry(Type) = { key, -1, {} } id := cast(i64) entries.num array_append( & entries, entry ) return id } -zpl_hmap_find :: proc( using self : ^ HMapZPL( $ Type), key : u64 ) -> HMapZPL_FindResult +hamp_zpl_find :: proc( using self : ^ HMapZPL( $ Type), key : u64 ) -> HMapZPL_FindResult { // profile(#procedure) result : HMapZPL_FindResult = { -1, -1, -1 } @@ -272,7 +275,7 @@ zpl_hmap_find :: proc( using self : ^ HMapZPL( $ Type), key : u64 ) -> HMapZPL_F return result } -zpl_hmap_full :: proc( using self : ^ HMapZPL( $ Type) ) -> b32 { +hamp_zpl_full :: proc( using self : ^ HMapZPL( $ Type) ) -> b32 { critical_load := u64(HMapZPL_CritialLoadScale * cast(f64) table.num) result : b32 = entries.num > critical_load return result diff --git a/code/sectr/grime/string_interning.odin b/code/sectr/grime/string_interning.odin index 565feab..29f02ad 100644 --- a/code/sectr/grime/string_interning.odin +++ b/code/sectr/grime/string_interning.odin @@ -66,12 +66,12 @@ str_cache_init :: proc( /*allocator : Allocator*/ ) -> ( cache : StringCache ) { cache.slab, alloc_error = slab_init( & policy, allocator = persistent_allocator(), dbg_name = dbg_name ) verify(alloc_error == .None, "Failed to initialize the string cache" ) - cache.table, alloc_error = zpl_hmap_init_reserve( StrRunesPair, persistent_allocator(), 4 * Megabyte, dbg_name ) + cache.table, alloc_error = hamp_zpl_init_reserve( StrRunesPair, persistent_allocator(), 4 * Megabyte, dbg_name ) return } str_intern_key :: #force_inline proc( content : string ) -> StringKey { return cast(StringKey) crc32( transmute([]byte) content ) } -str_intern_lookup :: #force_inline proc( key : StringKey ) -> (^StrRunesPair) { return zpl_hmap_get( & get_state().string_cache.table, transmute(u64) key ) } +str_intern_lookup :: #force_inline proc( key : StringKey ) -> (^StrRunesPair) { return hamp_zpl_get( & get_state().string_cache.table, transmute(u64) key ) } str_intern :: proc( content : string ) -> StrRunesPair { @@ -79,7 +79,7 @@ str_intern :: proc( content : string ) -> StrRunesPair cache := & get_state().string_cache key := str_intern_key(content) - result := zpl_hmap_get( & cache.table, transmute(u64) key ) + result := hamp_zpl_get( & cache.table, transmute(u64) key ) if result != nil { return (result ^) } @@ -101,8 +101,8 @@ str_intern :: proc( content : string ) -> StrRunesPair slab_validate_pools( get_state().persistent_slab ) - // result, alloc_error = zpl_hmap_set( & cache.table, key, StrRunesPair { transmute(string) byte_slice(str_mem, length), runes } ) - result, alloc_error = zpl_hmap_set( & cache.table, transmute(u64) key, StrRunesPair { transmute(string) str_mem, runes } ) + // result, alloc_error = hamp_zpl_set( & cache.table, key, StrRunesPair { transmute(string) byte_slice(str_mem, length), runes } ) + result, alloc_error = hamp_zpl_set( & cache.table, transmute(u64) key, StrRunesPair { transmute(string) str_mem, runes } ) verify( alloc_error == .None, "String cache had a backing allocator error" ) slab_validate_pools( get_state().persistent_slab ) diff --git a/code/sectr/ui/core/box.odin b/code/sectr/ui/core/box.odin index ab806cf..4c888df 100644 --- a/code/sectr/ui/core/box.odin +++ b/code/sectr/ui/core/box.odin @@ -70,7 +70,7 @@ ui_box_equal :: #force_inline proc "contextless" ( a, b : ^ UI_Box ) -> b32 { } ui_box_from_key :: #force_inline proc ( cache : ^HMapZPL(UI_Box), key : UI_Key ) -> (^UI_Box) { - return zpl_hmap_get( cache, cast(u64) key ) + return hamp_zpl_get( cache, cast(u64) key ) } ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box) @@ -80,7 +80,7 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box) key := ui_key_from_string( label ) curr_box : (^ UI_Box) - prev_box := zpl_hmap_get( prev_cache, cast(u64) key ) + prev_box := hamp_zpl_get( prev_cache, cast(u64) key ) { // profile("Assigning current box") set_result : ^ UI_Box @@ -88,16 +88,16 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box) if prev_box != nil { // Previous history was found, copy over previous state. - set_result, set_error = zpl_hmap_set( curr_cache, cast(u64) key, (prev_box ^) ) + set_result, set_error = hamp_zpl_set( curr_cache, cast(u64) key, (prev_box ^) ) } else { box : UI_Box box.key = key box.label = str_intern( label ) - set_result, set_error = zpl_hmap_set( curr_cache, cast(u64) key, box ) + set_result, set_error = hamp_zpl_set( curr_cache, cast(u64) key, box ) } - verify( set_error == AllocatorError.None, "Failed to set zpl_hmap due to allocator error" ) + verify( set_error == AllocatorError.None, "Failed to set hamp_zpl due to allocator error" ) curr_box = set_result curr_box.first_frame = prev_box == nil curr_box.flags = flags @@ -122,7 +122,7 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box) return curr_box } -ui_prev_cached_box :: #force_inline proc( box : ^UI_Box ) -> ^UI_Box { return zpl_hmap_get( ui_context().prev_cache, cast(u64) box.key ) } +ui_prev_cached_box :: #force_inline proc( box : ^UI_Box ) -> ^UI_Box { return hamp_zpl_get( ui_context().prev_cache, cast(u64) box.key ) } ui_box_tranverse_next :: proc "contextless" ( box : ^ UI_Box ) -> (^ UI_Box) { diff --git a/code/sectr/ui/core/signal.odin b/code/sectr/ui/core/signal.odin index 95d919f..6f2227e 100644 --- a/code/sectr/ui/core/signal.odin +++ b/code/sectr/ui/core/signal.odin @@ -156,7 +156,7 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas prev := ui_box_from_key( ui.curr_cache, ui.hot ) prev.hot_delta = 0 } - // prev_hot := zpl_hmap_get( ui.prev_cache, u64(ui.hot) ) + // prev_hot := hamp_zpl_get( ui.prev_cache, u64(ui.hot) ) // prev_hot_label := prev_hot != nil ? prev_hot.label.str : "" // log( str_fmt_tmp("Detected HOT via CURSOR OVER: %v is_hot: %v is_active: %v prev_hot: %v", box.label.str, is_hot, is_active, prev_hot_label )) ui.hot = box.key diff --git a/code/sectr/ui/core/state.odin b/code/sectr/ui/core/state.odin index 1cfef42..b0bf25b 100644 --- a/code/sectr/ui/core/state.odin +++ b/code/sectr/ui/core/state.odin @@ -133,7 +133,7 @@ ui_startup :: proc( ui : ^ UI_State, cache_allocator : Allocator /* , cache_rese ui^ = {} for & cache in ui.caches { - box_cache, allocation_error := zpl_hmap_init_reserve( UI_Box, cache_allocator, UI_Built_Boxes_Array_Size ) + box_cache, allocation_error := hamp_zpl_init_reserve( UI_Box, cache_allocator, UI_Built_Boxes_Array_Size ) verify( allocation_error == AllocatorError.None, "Failed to allocate box cache" ) cache = box_cache } @@ -151,7 +151,7 @@ ui_reload :: proc( ui : ^ UI_State, cache_allocator : Allocator ) { // We need to repopulate Allocator references for & cache in ui.caches { - zpl_hmap_reload( & cache, cache_allocator) + hamp_zpl_reload( & cache, cache_allocator) } ui.render_queue.backing = cache_allocator }