wip messing around with adding jai flavored hash/key table.

This commit is contained in:
2025-10-20 12:51:29 -04:00
parent 0607d81f70
commit 43141183a6
9 changed files with 188 additions and 19 deletions

View File

@@ -172,6 +172,6 @@ when ODIN_DEBUG {
farena_allocator :: #force_inline proc "contextless" (arena: ^FArena) -> Odin_Allocator { return transmute(Odin_Allocator) AllocatorInfo{proc_id = .FArena, data = arena} }
}
else {
farena_ainfo :: #force_inline proc "contextless" (arena: ^FArena) -> AllocatorInfo { return AllocatorInfo{procedure = farena_allocator_proc, data = arena} }
farena_allocator :: #force_inline proc "contextless" (arena: ^FArena) -> Odin_Allocator { return transmute(Odin_Allocator) AllocatorInfo{procedure = farena_odin_allocator_proc, data = arena} }
farena_ainfo :: #force_inline proc "contextless" (arena: ^FArena) -> AllocatorInfo { return AllocatorInfo{procedure = farena_allocator_proc, data = arena} }
farena_allocator :: #force_inline proc "contextless" (arena: ^FArena) -> Odin_Allocator { return transmute(Odin_Allocator) AllocatorInfo{procedure = farena_allocator_proc, data = arena} }
}

141
code2/grime/key_table.odin Normal file
View File

@@ -0,0 +1,141 @@
package grime
/*
Hassh Table based on John's Jai & Sean Barrett's
I don't like the table definition cntaining
the allocator, hash or compare procedure to be used.
So it has been stripped and instead applied on procedure site,
the parent container or is responsible for tracking that.
TODO(Ed): Resolve appropriate Key-Table term for it.
*/
KT_Slot :: struct(
$TypeHash: typeid,
$TypeKey: typeid,
$TypeValue: typeid
) {
hash: TypeHash,
key: TypeKey,
value: TypeValue,
}
KT :: struct($KT_Slot: typeid) {
load_factor_perent: int,
count: int,
allocated: int,
slots_filled: int,
slots: []KT_Slot,
}
KT_Info :: struct {
key_width: int,
value_width: int,
slot_width: int,
}
KT_Opaque :: struct {
count: int,
allocated: int,
slots_filled: int,
slots: []byte,
}
KT_ByteMeta :: struct {
hash_width: int,
value_width: int,
}
KT_COUNT_COLLISIONS :: #config(KT_COUNT_COLLISIONS, false)
KT_HASH_NEVER_OCCUPIED :: 0
KT_HASH_REMOVED :: 1
KT_HASH_FIRST_VALID :: 2
KT_LOAD_FACTOR_PERCENT :: 70
kt_byte_init :: proc(info: KT_Info, tbl_allocator: Odin_Allocator, kt: ^KT_Opaque, $HashType: typeid)
{
#assert(size_of(HashType) >= 32)
assert(tbl_allocator.procedure != nil)
assert(info.value_width >= 32)
assert(info.slot_width >= 64)
}
kt_deinit :: proc(table: ^$KT / typeid, allocator: Odin_Allocator)
{
}
kt_walk_table_body_proc :: #type proc($TypeHash: typeid, hash: TypeHash, kt: ^KT_Opaque, info: KT_Info, id: TypeHash) -> (should_break: bool)
kt_walk_table :: proc($TypeHash: typeid, hash: TypeHash, kt: ^KT_Opaque, info: KT_Info, $walk_body: kt_walk_table_body_proc) -> (index: TypeHash)
{
mask := cast(TypeHash)(kt.allocated - 1) // Cast may truncate
if hash < KT_HASH_FIRST_VALID do hash += KT_HASH_FIRST_VALID
index : TypeHash = hash & mask
probe_increment: TypeHash = 1
for id := transmute(TypeHash) kt.slots[info.slot_width * index:]; id != 0;
{
if #force_inline walk_body(hash, kt, info, id) do break
index = (index + probe_increment) & mask
probe_increment += 1
}
}
// Will not expand table if capacity reached, user must do that check beforehand.
// Will return existing if hash found
kt_byte_add :: proc(value: [^]byte, key: [^]byte, hash: $TypeHash, kt: ^KT_Opaque, info: KT_Info)-> [^]byte
{
aasert(kt.slots_filled, kt.allocated)
index := #force_inline kt_walk_table(hash, kt, info,
proc(hash: $TypeHash, kt: ^KT_Opaque, info: KT_Info, id: TypeHash) -> (should_break: bool)
{
if id == KT_HASH_REMOVED {
kt.slots_filled -= 1
should_break = true
return
}
//TODO(Ed): Add collision tracking
return
})
kt.count += 1
kt.slots_filled += 1
slot_offset := info.slot_width * index
entry := table.slots[info.slot_width * index:]
mem_copy_non_overlapping(entry, hash, size_of(TypeHash))
mem_copy_non_overlapping(entry[size_of(hash):], key, info.key_width)
mem_copy_non_overlapping(entry[size_of(hash) + size_of(key):], value, info.value_width)
return entry
}
// Will not expand table if capacity reached, user must do that check beforehand.
// Will override if hash exists
kt_byte_set :: proc()
{
}
kt_remove :: proc()
{
}
kt_byte_contains :: proc()
{
}
kt_byte_find_pointer :: proc()
{
}
kt_find :: proc()
{
}
kt_find_multiple :: proc()
{
}
kt_next_power_of_two :: #force_inline proc(x: int) -> int { power := 1; for ;x > power; do power += power; return power }

View File

@@ -9,9 +9,9 @@ KT1L_Slot :: struct($Type: typeid) {
value: Type,
}
KT1L_Meta :: struct {
slot_size: uintptr,
kt_value_offset: uintptr,
type_width: uintptr,
slot_size: int,
kt_value_offset: int,
type_width: int,
type: typeid,
}
kt1l_populate_slice_a2_Slice_Byte :: proc(kt: ^[]byte, backing: AllocatorInfo, values: []byte, num_values: int, m: KT1L_Meta) {
@@ -21,7 +21,7 @@ kt1l_populate_slice_a2_Slice_Byte :: proc(kt: ^[]byte, backing: AllocatorInfo, v
kt^, _ = mem_alloc(table_size_bytes, ainfo = transmute(Odin_Allocator) backing)
slice_assert(kt ^)
kt_raw : SliceByte = transmute(SliceByte) kt^
for id in 0 ..< cast(uintptr) num_values {
for id in 0 ..< num_values {
slot_offset := id * m.slot_size // slot id
slot_cursor := kt_raw.data[slot_offset:] // slots[id] type: KT1L_<Type>
// slot_key := transmute(^u64) slot_cursor // slots[id].key type: U64
@@ -30,7 +30,7 @@ kt1l_populate_slice_a2_Slice_Byte :: proc(kt: ^[]byte, backing: AllocatorInfo, v
a2_cursor := cursor(values)[a2_offset:] // a2_entries[id] type: A2_<Type>
// a2_key := (transmute(^[]byte) a2_cursor) ^ // a2_entries[id].key type: <Type>
// a2_value := slice(a2_cursor[m.type_width:], m.type_width) // a2_entries[id].value type: <Type>
mem_copy_non_overlapping(slot_cursor[m.kt_value_offset:], a2_cursor[m.type_width:], cast(int) m.type_width) // slots[id].value = a2_entries[id].value
mem_copy_non_overlapping(slot_cursor[m.kt_value_offset:], a2_cursor[m.type_width:], m.type_width) // slots[id].value = a2_entries[id].value
(transmute([^]u64) slot_cursor)[0] = 0;
hash64_djb8(transmute(^u64) slot_cursor, (transmute(^[]byte) a2_cursor) ^) // slots[id].key = hash64_djb8(a2_entries[id].key)
}

View File

@@ -0,0 +1,28 @@
package grime
StrKey_U4 :: struct {
len: u32, // Length of string
offset: u32, // Offset in varena
}
StrKT_U4_Cell_Depth :: 4
StrKT_U4_Slot :: KT1CX_Slot(StrKey_U4)
StrKT_U4_Cell :: KT1CX_Cell(StrKT_U4_Slot, 4)
StrKT_U4_Table :: KT1CX(StrKT_U4_Cell)
VStrKT_U4 :: struct {
varena: VArena, // Backed by growing vmem
entries: StrKT_U4_Table
}
vstrkt_u4_init :: proc(varena: ^VArena) -> (cache: ^VStrKT_U4)
{
return nil
}
vstrkt_u4_intern :: proc(cache: ^VStrKT_U4) -> StrKey_U4
{
// profile(#procedure)
return {}
}

View File

@@ -292,6 +292,6 @@ when ODIN_DEBUG {
varena_allocator :: #force_inline proc "contextless" (arena: ^VArena) -> Odin_Allocator { return transmute(Odin_Allocator) AllocatorInfo{proc_id = .VArena, data = arena} }
}
else {
varena_ainfo :: #force_inline proc "contextless" (arena: ^VArena) -> AllocatorInfo { return AllocatorInfo{procedure = varena_allocator_proc, data = arena} }
varena_allocator :: #force_inline proc "contextless" (arena: ^VArena) -> Odin_Allocator { return transmute(Odin_Allocator) AllocatorInfo{procedure = varena_odin_allocator_proc, data = arena} }
varena_ainfo :: #force_inline proc "contextless" (arena: ^VArena) -> AllocatorInfo { return AllocatorInfo{procedure = varena_allocator_proc, data = arena} }
varena_allocator :: #force_inline proc "contextless" (arena: ^VArena) -> Odin_Allocator { return transmute(Odin_Allocator) AllocatorInfo{procedure = varena_allocator_proc, data = arena} }
}

View File

@@ -23,14 +23,14 @@ load_client_api :: proc(version_id: int) -> (loaded_module: Client_API) {
file_copy_sync( Path_Sectr_Module, Path_Sectr_Live_Module, allocator = context.temp_allocator )
did_load: bool; lib, did_load = os_lib_load( Path_Sectr_Live_Module )
if ! did_load do panic( "Failed to load the sectr module.")
startup = cast( type_of( host_memory.client_api.startup)) os_lib_get_proc(lib, "startup")
shutdown = cast( type_of( host_memory.client_api.shutdown)) os_lib_get_proc(lib, "sectr_shutdown")
tick_lane_startup = cast( type_of( host_memory.client_api.tick_lane_startup)) os_lib_get_proc(lib, "tick_lane_startup")
job_worker_startup = cast( type_of( host_memory.client_api.job_worker_startup)) os_lib_get_proc(lib, "job_worker_startup")
hot_reload = cast( type_of( host_memory.client_api.hot_reload)) os_lib_get_proc(lib, "hot_reload")
tick_lane = cast( type_of( host_memory.client_api.tick_lane)) os_lib_get_proc(lib, "tick_lane")
clean_frame = cast( type_of( host_memory.client_api.clean_frame)) os_lib_get_proc(lib, "clean_frame")
jobsys_worker_tick = cast( type_of( host_memory.client_api.jobsys_worker_tick)) os_lib_get_proc(lib, "jobsys_worker_tick")
startup = transmute( type_of( host_memory.client_api.startup)) os_lib_get_proc(lib, "startup")
shutdown = transmute( type_of( host_memory.client_api.shutdown)) os_lib_get_proc(lib, "sectr_shutdown")
tick_lane_startup = transmute( type_of( host_memory.client_api.tick_lane_startup)) os_lib_get_proc(lib, "tick_lane_startup")
job_worker_startup = transmute( type_of( host_memory.client_api.job_worker_startup)) os_lib_get_proc(lib, "job_worker_startup")
hot_reload = transmute( type_of( host_memory.client_api.hot_reload)) os_lib_get_proc(lib, "hot_reload")
tick_lane = transmute( type_of( host_memory.client_api.tick_lane)) os_lib_get_proc(lib, "tick_lane")
clean_frame = transmute( type_of( host_memory.client_api.clean_frame)) os_lib_get_proc(lib, "clean_frame")
jobsys_worker_tick = transmute( type_of( host_memory.client_api.jobsys_worker_tick)) os_lib_get_proc(lib, "jobsys_worker_tick")
if startup == nil do panic("Failed to load sectr.startup symbol" )
if shutdown == nil do panic("Failed to load sectr.shutdown symbol" )
if tick_lane_startup == nil do panic("Failed to load sectr.tick_lane_startup symbol" )

View File

View File

@@ -216,8 +216,8 @@ push-location $path_root
$build_args += $flag_microarch_zen5
$build_args += $flag_use_separate_modules
$build_args += $flag_thread_count + $CoreCount_Physical
$build_args += $flag_optimize_none
# $build_args += $flag_optimize_minimal
# $build_args += $flag_optimize_none
$build_args += $flag_optimize_minimal
# $build_args += $flag_optimize_speed
# $build_args += $falg_optimize_aggressive
$build_args += $flag_debug