zpl_hmap prefix to hmap_zpl, plus some todos to go back to builtin containers

This commit is contained in:
Edward R. Gonzalez 2024-05-26 13:35:08 -04:00
parent 469fa5f8ec
commit 5c2f55148a
8 changed files with 62 additions and 54 deletions

View File

@ -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())

View File

@ -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"

View File

@ -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.

View File

@ -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

View File

@ -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 )

View File

@ -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)
{

View File

@ -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

View File

@ -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
}