lifted the hash maps and the string cache to the grime package

This commit is contained in:
Edward R. Gonzalez 2024-05-31 20:51:30 -04:00
parent e84ec719b3
commit 9ba718254c
10 changed files with 111 additions and 84 deletions

View File

@ -25,8 +25,9 @@ HMapChainedSlot :: struct( $Type : typeid ) {
}
HMapChainedHeader :: struct( $ Type : typeid ) {
pool : Pool,
lookup : [] ^HMapChainedSlot(Type),
pool : Pool,
lookup : [] ^HMapChainedSlot(Type),
dbg_name : string,
}
HMapChained :: struct( $ Type : typeid) {
@ -55,7 +56,7 @@ hmap_chained_init :: proc( $HMapChainedType : typeid/HMapChained($Type), lookup_
pool_bucket_cap : uint = 1 * Kilo,
pool_bucket_reserve_num : uint = 0,
pool_alignment : uint = mem.DEFAULT_ALIGNMENT,
// dbg_name : string = ""
dbg_name : string = ""
) -> (table : HMapChained(Type), error : AllocatorError)
{
header_size := size_of(HMapChainedHeader(Type))
@ -73,10 +74,11 @@ hmap_chained_init :: proc( $HMapChainedType : typeid/HMapChained($Type), lookup_
bucket_reserve_num = pool_bucket_reserve_num,
alignment = pool_alignment,
allocator = allocator,
// dbg_name = str_intern(str_fmt("%v: pool", dbg_name)).str
dbg_name = str_intern(str_fmt("%v: pool", dbg_name)).str
)
data := transmute([^] ^HMapChainedSlot(Type)) (transmute( [^]HMapChainedHeader(Type)) table.header)[1:]
table.lookup = slice_ptr( data, int(lookup_capacity) )
data := transmute([^] ^HMapChainedSlot(Type)) (transmute( [^]HMapChainedHeader(Type)) table.header)[1:]
table.lookup = slice_ptr( data, int(lookup_capacity) )
table.dbg_name = dbg_name
return
}

View File

@ -18,7 +18,7 @@ Each entry contains a next field, which is an index pointing to the next entry i
Growing this hashtable is destructive, so it should usually be kept to a fixed-size unless
the populating operations only occur in one place and from then on its read-only.
*/
package sectr
package grime
import "core:slice"
@ -69,7 +69,7 @@ hmap_zpl_init :: proc
return result, AllocatorError.None
}
hamp_zpl_clear :: proc( using self : ^ HMapZPL( $ Type ) ) {
hmap_zpl_clear :: proc( using self : ^ HMapZPL( $ Type ) ) {
for id := 0; id < table.num; id += 1 {
table[id] = -1
}
@ -78,17 +78,17 @@ hamp_zpl_clear :: proc( using self : ^ HMapZPL( $ Type ) ) {
array_clear( entries )
}
hamp_zpl_destroy :: proc( using self : ^ HMapZPL( $ Type ) ) {
hmap_zpl_destroy :: proc( using self : ^ HMapZPL( $ Type ) ) {
if table.data != nil && table.capacity > 0 {
array_free( table )
array_free( entries )
}
}
hamp_zpl_get :: proc ( using self : ^ HMapZPL( $ Type ), key : u64 ) -> ^ Type
hmap_zpl_get :: proc ( using self : ^ HMapZPL( $ Type ), key : u64 ) -> ^ Type
{
// profile(#procedure)
id := hamp_zpl_find( self, key ).entry_index
id := hmap_zpl_find( self, key ).entry_index
if id >= 0 {
return & entries.data[id].value
}
@ -96,26 +96,26 @@ hamp_zpl_get :: proc ( using self : ^ HMapZPL( $ Type ), key : u64 ) -> ^ Type
return nil
}
hamp_zpl_map :: proc( using self : ^ HMapZPL( $ Type), map_proc : HMapZPL_MapProc ) {
hmap_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 )
}
}
hamp_zpl_map_mut :: proc( using self : ^ HMapZPL( $ Type), map_proc : HMapZPL_MapMutProc ) {
hmap_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 )
}
}
hamp_zpl_grow :: proc( using self : ^ HMapZPL( $ Type ) ) -> AllocatorError {
hmap_zpl_grow :: proc( using self : ^ HMapZPL( $ Type ) ) -> AllocatorError {
new_num := array_grow_formula( entries.num )
return hamp_zpl_rehash( self, new_num )
return hmap_zpl_rehash( self, new_num )
}
hamp_zpl_rehash :: proc( ht : ^ HMapZPL( $ Type ), new_num : u64 ) -> AllocatorError
hmap_zpl_rehash :: proc( ht : ^ HMapZPL( $ Type ), new_num : u64 ) -> AllocatorError
{
profile(#procedure)
// For now the prototype should never allow this to happen.
@ -124,7 +124,7 @@ hamp_zpl_rehash :: proc( ht : ^ HMapZPL( $ Type ), new_num : u64 ) -> AllocatorE
new_ht, init_result := hmap_zpl_init( HMapZPL(Type), new_num, ht.table.backing, ht.table.dbg_name )
if init_result != AllocatorError.None {
ensure( false, "New hamp_zpl failed to allocate" )
ensure( false, "New hmap_zpl failed to allocate" )
return init_result
}
@ -132,8 +132,8 @@ hamp_zpl_rehash :: proc( ht : ^ HMapZPL( $ Type ), new_num : u64 ) -> AllocatorE
find_result : HMapZPL_FindResult
entry := & ht.entries.data[id]
find_result = hamp_zpl_find( & new_ht, entry.key )
last_added_index = hamp_zpl_add_entry( & new_ht, entry.key )
find_result = hmap_zpl_find( & new_ht, entry.key )
last_added_index = hmap_zpl_add_entry( & new_ht, entry.key )
if find_result.prev_index < 0 {
new_ht.table.data[ find_result.hash_index ] = last_added_index
@ -146,13 +146,13 @@ hamp_zpl_rehash :: proc( ht : ^ HMapZPL( $ Type ), new_num : u64 ) -> AllocatorE
new_ht.entries.data[ last_added_index ].value = entry.value
}
hamp_zpl_destroy( ht )
hmap_zpl_destroy( ht )
(ht ^) = new_ht
return AllocatorError.None
}
hamp_zpl_rehash_fast :: proc( using self : ^ HMapZPL( $ Type ) )
hmap_zpl_rehash_fast :: proc( using self : ^ HMapZPL( $ Type ) )
{
for id := 0; id < entries.num; id += 1 {
entries[id].Next = -1;
@ -162,7 +162,7 @@ hamp_zpl_rehash_fast :: proc( using self : ^ HMapZPL( $ Type ) )
}
for id := 0; id < entries.num; id += 1 {
entry := & entries[id]
find_result := hamp_zpl_find( entry.key )
find_result := hmap_zpl_find( entry.key )
if find_result.prev_index < 0 {
table[ find_result.hash_index ] = id
@ -174,45 +174,45 @@ hamp_zpl_rehash_fast :: proc( using self : ^ HMapZPL( $ Type ) )
}
// Used when the address space of the allocator changes and the backing reference must be updated
hamp_zpl_reload :: proc( using self : ^HMapZPL($Type), new_backing : Allocator ) {
hmap_zpl_reload :: proc( using self : ^HMapZPL($Type), new_backing : Allocator ) {
table.backing = new_backing
entries.backing = new_backing
}
hamp_zpl_remove :: proc( self : ^ HMapZPL( $ Type ), key : u64 ) {
find_result := hamp_zpl_find( key )
hmap_zpl_remove :: proc( self : ^ HMapZPL( $ Type ), key : u64 ) {
find_result := hmap_zpl_find( key )
if find_result.entry_index >= 0 {
array_remove_at( & entries, find_result.entry_index )
hamp_zpl_rehash_fast( self )
hmap_zpl_rehash_fast( self )
}
}
hamp_zpl_remove_entry :: proc( using self : ^ HMapZPL( $ Type ), id : i64 ) {
hmap_zpl_remove_entry :: proc( using self : ^ HMapZPL( $ Type ), id : i64 ) {
array_remove_at( & entries, id )
}
hamp_zpl_set :: proc( using self : ^ HMapZPL( $ Type), key : u64, value : Type ) -> (^ Type, AllocatorError)
hmap_zpl_set :: proc( using self : ^ HMapZPL( $ Type), key : u64, value : Type ) -> (^ Type, AllocatorError)
{
// profile(#procedure)
id : i64 = 0
find_result : HMapZPL_FindResult
if hamp_zpl_full( self )
if hmap_zpl_full( self )
{
grow_result := hamp_zpl_grow( self )
grow_result := hmap_zpl_grow( self )
if grow_result != AllocatorError.None {
return nil, grow_result
}
}
find_result = hamp_zpl_find( self, key )
find_result = hmap_zpl_find( self, key )
if find_result.entry_index >= 0 {
id = find_result.entry_index
}
else
{
id = hamp_zpl_add_entry( self, key )
id = hmap_zpl_add_entry( self, key )
if find_result.prev_index >= 0 {
entries.data[ find_result.prev_index ].next = id
}
@ -223,15 +223,15 @@ hamp_zpl_set :: proc( using self : ^ HMapZPL( $ Type), key : u64, value : Type )
entries.data[id].value = value
if hamp_zpl_full( self ) {
alloc_error := hamp_zpl_grow( self )
if hmap_zpl_full( self ) {
alloc_error := hmap_zpl_grow( self )
return & entries.data[id].value, alloc_error
}
return & entries.data[id].value, AllocatorError.None
}
hamp_zpl_slot :: proc( using self : ^ HMapZPL( $ Type), key : u64 ) -> i64 {
hmap_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
@ -240,14 +240,14 @@ hamp_zpl_slot :: proc( using self : ^ HMapZPL( $ Type), key : u64 ) -> i64 {
return -1
}
hamp_zpl_add_entry :: proc( using self : ^ HMapZPL( $ Type), key : u64 ) -> i64 {
hmap_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
}
hamp_zpl_find :: proc( using self : ^ HMapZPL( $ Type), key : u64 ) -> HMapZPL_FindResult
hmap_zpl_find :: proc( using self : ^ HMapZPL( $ Type), key : u64 ) -> HMapZPL_FindResult
{
// profile(#procedure)
result : HMapZPL_FindResult = { -1, -1, -1 }
@ -271,7 +271,7 @@ hamp_zpl_find :: proc( using self : ^ HMapZPL( $ Type), key : u64 ) -> HMapZPL_F
return result
}
hamp_zpl_full :: proc( using self : ^ HMapZPL( $ Type) ) -> b32 {
hmap_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

@ -145,8 +145,8 @@ is_power_of_two :: proc {
make :: proc {
array_init,
// hmap_chained_init,
// hmap_zpl_init,
hmap_chained_init,
hmap_zpl_init,
// Usual
make_slice,
@ -161,6 +161,10 @@ push :: proc {
stack_push,
}
to_runes :: proc {
string_to_runes,
}
to_string :: proc {
runes_to_string,
str_builder_to_string,

View File

@ -8,7 +8,7 @@ TODO(Ed): Move the string cache to its own virtual arena?
Its going to be used heavily and we can better utilize memory that way.
The arena can deal with alignment just fine or we can pad in a min amount per string.
*/
package sectr
package grime
import "base:runtime"
import "core:mem"
@ -34,7 +34,11 @@ StringCache :: struct {
table : HMapZPL(StrRunesPair),
}
str_cache_init :: proc( /*allocator : Allocator*/ ) -> ( cache : StringCache ) {
// This is the default string cache for the runtime module.
Module_String_Cache : ^StringCache
str_cache_init :: proc( table_allocator, slabs_allocator : Allocator ) -> (cache : StringCache)
{
alignment := uint(mem.DEFAULT_ALIGNMENT)
policy : SlabPolicy
@ -60,54 +64,47 @@ str_cache_init :: proc( /*allocator : Allocator*/ ) -> ( cache : StringCache ) {
@static dbg_name := "StringCache slab"
state := get_state()
alloc_error : AllocatorError
cache.slab, alloc_error = slab_init( & policy, dbg_name = dbg_name, allocator = persistent_allocator() )
cache.slab, alloc_error = slab_init( & policy, allocator = slabs_allocator, dbg_name = dbg_name )
verify(alloc_error == .None, "Failed to initialize the string cache" )
cache.table, alloc_error = make( HMapZPL(StrRunesPair), 4 * Megabyte, persistent_allocator(), dbg_name )
cache.table, alloc_error = make( HMapZPL(StrRunesPair), 4 * Megabyte, table_allocator, 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 hamp_zpl_get( & get_state().string_cache.table, transmute(u64) key ) }
str_cache_reload :: #force_inline proc ( cache : ^StringCache, table_allocator, slabs_allocator : Allocator ) {
slab_reload( cache.slab, table_allocator )
hmap_zpl_reload( & cache.table, slabs_allocator )
}
str_cache_set_module_ctx :: #force_inline proc "contextless" ( cache : ^StringCache ) { Module_String_Cache = cache }
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 hmap_zpl_get( & Module_String_Cache.table, transmute(u64) key ) }
str_intern :: proc( content : string ) -> StrRunesPair
{
// profile(#procedure)
cache := & get_state().string_cache
cache := Module_String_Cache
key := str_intern_key(content)
result := hamp_zpl_get( & cache.table, transmute(u64) key )
result := hmap_zpl_get( & cache.table, transmute(u64) key )
if result != nil {
return (result ^)
}
// profile_begin("new entry")
{
length := len(content)
// str_mem, alloc_error := alloc( length, mem.DEFAULT_ALIGNMENT )
str_mem, alloc_error := slab_alloc( cache.slab, uint(length), uint(mem.DEFAULT_ALIGNMENT), zero_memory = false )
verify( alloc_error == .None, "String cache had a backing allocator error" )
length := len(content)
str_mem, alloc_error := slab_alloc( cache.slab, uint(length), uint(mem.DEFAULT_ALIGNMENT), zero_memory = false )
verify( alloc_error == .None, "String cache had a backing allocator error" )
// copy_non_overlapping( str_mem, raw_data(content), length )
copy_non_overlapping( raw_data(str_mem), raw_data(content), length )
copy_non_overlapping( raw_data(str_mem), raw_data(content), length )
runes : []rune
// runes, alloc_error = to_runes( content, persistent_allocator() )
runes, alloc_error = to_runes( content, slab_allocator(cache.slab) )
verify( alloc_error == .None, "String cache had a backing allocator error" )
runes : []rune
runes, alloc_error = to_runes( content, slab_allocator(cache.slab) )
verify( alloc_error == .None, "String cache had a backing allocator error" )
// slab_validate_pools( cache.slab.backing )
slab_validate_pools( get_state().persistent_slab )
// 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 )
}
// profile_end()
result, alloc_error = hmap_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( cache.slab.backing )
return (result ^)
}

View File

@ -99,7 +99,8 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
transient_clear_time = 120 // Seconds, 2 Minutes
string_cache = str_cache_init()
string_cache = str_cache_init( persistent_allocator(), persistent_allocator() )
str_cache_set_module_ctx( & string_cache )
}
// Setup input frame poll references
@ -424,8 +425,8 @@ 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() )
hamp_zpl_reload( & string_cache.table, persistent_slab_allocator())
str_cache_reload( & string_cache, persistent_allocator(), persistent_allocator() )
str_cache_set_module_ctx( & string_cache )
slab_reload( frame_slab, frame_allocator())
slab_reload( transient_slab, transient_allocator())

View File

@ -0,0 +1,3 @@
# Sectr's Grime
This just contains the mappings file and some stuff I haven't felt like lifting to the grime package yet.

View File

@ -174,6 +174,13 @@ import "codebase:grime"
hmap_chained_set :: grime.hmap_chained_set
hmap_chained_reload :: grime.hmap_chained_reload
HMapZPL :: grime.HMapZPL
hmap_zpl_init :: grime.hmap_zpl_init
hmap_zpl_get :: grime.hmap_zpl_get
hmap_zpl_reload :: grime.hmap_zpl_reload
hmap_zpl_set :: grime.hmap_zpl_set
Pool :: grime.Pool
Slab :: grime.Slab
@ -234,12 +241,25 @@ import "codebase:grime"
memtracker_register_auto_name_slice :: grime.memtracker_register_auto_name_slice
memtracker_unregister :: grime.memtracker_unregister
calc_padding_with_header :: grime.calc_padding_with_header
memory_after_header :: grime.memory_after_header
memory_after :: grime.memory_after
swap :: grime.swap
// strings
StrRunesPair :: grime.StrRunesPair
StringCache :: grime.StringCache
str_cache_init :: grime.str_cache_init
str_cache_reload :: grime.str_cache_reload
str_cache_set_module_ctx :: grime.str_cache_set_module_ctx
// str_intern_key :: grime.str_intern_key
// str_intern_lookup :: grime.str_intern_lookup
str_intern :: grime.str_intern
str_intern_fmt :: grime.str_intern_fmt
to_str_runes_pair_via_string :: grime.to_str_runes_pair_via_string
to_str_runes_pair_via_runes :: grime.to_str_runes_pair_via_runes
// profiler
SpallProfiler :: grime.SpallProfiler

View File

@ -151,7 +151,7 @@ ui_reload :: proc( ui : ^ UI_State, cache_allocator : Allocator )
{
// We need to repopulate Allocator references
for & cache in ui.caches {
hamp_zpl_reload( & cache, cache_allocator)
hmap_zpl_reload( & cache, cache_allocator)
}
ui.render_queue.backing = cache_allocator
}

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 hamp_zpl_get( cache, cast(u64) key )
return hmap_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 := hamp_zpl_get( prev_cache, cast(u64) key )
prev_box := hmap_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 = hamp_zpl_set( curr_cache, cast(u64) key, (prev_box ^) )
set_result, set_error = hmap_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 = hamp_zpl_set( curr_cache, cast(u64) key, box )
set_result, set_error = hmap_zpl_set( curr_cache, cast(u64) key, box )
}
verify( set_error == AllocatorError.None, "Failed to set hamp_zpl due to allocator error" )
verify( set_error == AllocatorError.None, "Failed to set hmap_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 hamp_zpl_get( ui_context().prev_cache, cast(u64) box.key ) }
ui_prev_cached_box :: #force_inline proc( box : ^UI_Box ) -> ^UI_Box { return hmap_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 := hamp_zpl_get( ui.prev_cache, u64(ui.hot) )
// prev_hot := hmap_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