Mostly exploring hashtables, some new files...

I made the files for the ast but they're not populated yet.
I made some initial implementation for raddbg flavored linked-lists.
This commit is contained in:
Edward R. Gonzalez 2024-02-29 19:37:37 -05:00
parent 1de141288f
commit 12aa6b4870
14 changed files with 223 additions and 85 deletions

0
code/ast_formatting.odin Normal file
View File

0
code/ast_text.odin Normal file
View File

0
code/ast_whitespace.odin Normal file
View File

View File

@ -3,6 +3,9 @@ package sectr
import "base:builtin"
copy :: builtin.copy
import "base:intrinsics"
type_has_field :: intrinsics.type_has_field
type_elem_type :: intrinsics.type_elem_type
import "base:runtime"
Byte :: runtime.Byte
Kilobyte :: runtime.Kilobyte
@ -54,6 +57,19 @@ import str "core:strings"
str_builder_to_string :: str.to_string
import "core:unicode/utf8"
OS_Type :: type_of(ODIN_OS)
// Alias Tables
get_bounds :: proc {
box_get_bounds,
view_get_bounds,
}
is_power_of_two :: proc {
is_power_of_two_u32,
}
to_runes :: proc {
utf8.string_to_runes,
}
@ -61,47 +77,3 @@ to_runes :: proc {
to_string :: proc {
str_builder_to_string,
}
OS_Type :: type_of(ODIN_OS)
kilobytes :: #force_inline proc "contextless" ( kb : $ integer_type ) -> integer_type {
return kb * Kilobyte
}
megabytes :: #force_inline proc "contextless" ( mb : $ integer_type ) -> integer_type {
return mb * Megabyte
}
gigabytes :: #force_inline proc "contextless" ( gb : $ integer_type ) -> integer_type {
return gb * Gigabyte
}
terabytes :: #force_inline proc "contextless" ( tb : $ integer_type ) -> integer_type {
return tb * Terabyte
}
get_bounds :: proc {
box_get_bounds,
view_get_bounds,
}
// TODO(Ed): Review
//region Doubly Linked List generic procs (verbose)
dbl_linked_list_push_back :: proc(first: ^(^ $ Type), last: ^(^ Type), new_node: ^ Type)
{
if first == nil || first^ == nil {
// List is empty, set first and last to the new node
(first ^) = new_node
(last ^) = new_node
new_node.next = nil
new_node.prev = nil
}
else
{
// List is not empty, add new node to the end
(last^).next = new_node
new_node.prev = last^
(last ^) = new_node
new_node.next = nil
}
}
//endregion

View File

@ -15,9 +15,6 @@ Array :: struct ( $ Type : typeid ) {
data : [^]Type,
}
// @(private=file)
// Array :: Array_ZPL
array_to_slice :: proc( using self : Array( $ Type) ) -> []Type {
return slice_ptr( data, num )
}

View File

@ -0,0 +1,27 @@
// Mask-Step-Index (MSI) Hash Table implementation.
// See: https://nullprogram.com/blog/2022/08/08/
package sectr
// TODO(Ed) : This is a wip, I haven't gotten the nuance of this mess down pact.
// Compute a mask, then a step size, and finally an index.
// The exponent parameter is a power-of-two exponent for the hash-table size.
msi_hmap_lookup :: proc ( hash : u64, exponent, index : u32 ) -> (candidate_index : i32)
{
mask := u32(1 << (exponent)) - 1
step := u32(hash >> (64 - exponent)) | 1
candidate_index = i32( (index + step) & mask )
return
}
HMap_MSI :: struct ( $ Type : typeid, $ Size : u32 )
where is_power_of_two( Size )
{
hashes : [ Size ]( ^ Type ),
length : i32
}
HMap_MSI_Dyn :: struct ( $ Type : typeid ) {
hashes : Array( DLL_Node( ^ Type ) ),
size : u64,
length : i32,
}

View File

@ -0,0 +1,39 @@
// This was an attempt to learn Ryan's hash table implementation used with the UI module of the RAD Debugger.
// Its not completed
package sectr
HMapRJF :: struct ( $ Type : typeid ) {
slots : Array ( DLL_NodeFL( Type ) ),
size : u64,
first_free : ^ Type,
}
rjf_hmap_init :: proc( $ Type : typeid, allocator : Allocator, size : u64 ) -> ( HMapRJF( Type ), AllocatorError ) {
result : HMapRJF( Type )
alloc_error : AllocatorError
result.slots, alloc_error := array_init_reserve( Type, allocator, size )
if alloc_error != AllocatorError.None {
ensure( false, "Failed to allocate slots array" )
return result, alloc_error
}
array_resize( & result.slots, size )
return result, AllocatorError.None
}
rjf_hmap_slot_index :: #force_inline proc ( using self : HMapRJF( $ Type ), key : u64 ) -> u64 {
return key % size
}
rjf_hmap_get_slot :: #force_inline proc ( using self : HMapRJF ( $ Type ), key : u64 ) -> ^ DLL_NodeFL ( Type ) {
slot_index := key % size
return & slots[ slot_index ]
}
rjf_hmap_insert :: proc ( using self : HMapRJF ( $ Type ), key : u64, value : ^ Type ) {
slot_index := key % size
slot := & slots[ slot_index ]
dll_insert_raw( nil, slot.first, slot.last, slot.last, value )
}

View File

@ -17,6 +17,7 @@ HMapZPL_MapProc :: #type proc( $ Type : typeid, key : u64, value : Type )
HMapZPL_MapMutProc :: #type proc( $ Type : typeid, key : u64, value : ^ Type )
HMapZPL_CritialLoadScale :: 0.70
HMapZPL_HashToEntryRatio :: 1.50
HMapZPL_FindResult :: struct {
hash_index : i64,
@ -44,12 +45,14 @@ zpl_hmap_init_reserve :: proc( $ Type : typeid, allocator : Allocator, num : u64
result : HMapZPL(Type)
hashes_result, entries_result : AllocatorError
result.hashes, hashes_result = array_init_reserve( i64, allocator, num )
hashes_size := cast(u64) (HMapZPL_HashToEntryRatio * f32(num))
result.hashes, hashes_result = array_init_reserve( i64, allocator, hashes_size )
if hashes_result != AllocatorError.None {
ensure( false, "Failed to allocate hashes array" )
return result, hashes_result
}
array_resize( & result.hashes, num )
array_resize( & result.hashes, hashes_size )
slice.fill( slice_ptr( result.hashes.data, cast(int) result.hashes.num), -1 )
result.entries, entries_result = array_init_reserve( HMapZPL_Entry(Type), allocator, num )

View File

@ -0,0 +1,71 @@
// I'm not sure about this, it was created to figure out Ryan's linked-list usage in the UI module of the RAD Debugger.
// The code takes advantage of macros for the linked list interface in a way that odin doesn't really permit without a much worse interface.
package sectr
DLL_Node :: struct ( $ Type : typeid ) #raw_union {
using _ : struct {
left, right : ^ Type,
},
using _ : struct {
prev, next : ^ Type,
},
using _ : struct {
first, last : ^ Type,
},
}
DLL_NodeFull :: struct ( $ Type : typeid ) {
first, last, prev, next : ^ Type,
}
DLL_NodeLR :: struct ( $ Type : typeid ) {
left, right : ^ Type,
}
DLL_NodePN :: struct ( $ Type : typeid ) {
prev, next : ^ Type,
}
DLL_NodeFL :: struct ( $ Type : typeid ) {
first, last : ^ Type,
}
type_is_node :: #force_inline proc "contextless" ( $ Type : typeid ) -> b32
{
// elem_type := type_elem_type(Type)
return type_has_field( type_elem_type(Type), "prev" ) && type_has_field( type_elem_type(Type), "next" )
}
dll_insert_raw :: proc "contextless" ( null, first, last, position, new : ^ DLL_Node( $ Type ) )
{
// Empty Case
if first == null {
first = new
last = new
new.next = null
new.prev = null
}
else if position == null {
// Position is not set, insert at beginning
new.next = first
first.prev = new
first = new
new.prev = null
}
else if position == last {
// Positin is set to last, insert at end
last.next = new
new.prev = last
last = new
new.next = null
}
else {
// Insert around position
if position.next != null {
position.next.prev = new
}
new.next = position.next
position.next = new
new.prev = position
}
}

View File

@ -7,6 +7,19 @@ import "core:mem/virtual"
import "core:runtime"
import "core:os"
kilobytes :: #force_inline proc "contextless" ( kb : $ integer_type ) -> integer_type {
return kb * Kilobyte
}
megabytes :: #force_inline proc "contextless" ( mb : $ integer_type ) -> integer_type {
return mb * Megabyte
}
gigabytes :: #force_inline proc "contextless" ( gb : $ integer_type ) -> integer_type {
return gb * Gigabyte
}
terabytes :: #force_inline proc "contextless" ( tb : $ integer_type ) -> integer_type {
return tb * Terabyte
}
// Initialize a sub-section of our virtual memory as a sub-arena
sub_arena_init :: proc( address : ^ byte, size : int ) -> ( ^ Arena) {
Arena :: mem.Arena

View File

@ -1,5 +1,10 @@
package sectr
is_power_of_two_u32 :: proc( value : u32 ) -> b32
{
return value != 0 && ( value & ( value - 1 )) == 0
}
import "core:math/linalg"
Vec2 :: linalg.Vector2f32
@ -7,31 +12,3 @@ Vec3 :: linalg.Vector3f32
Vec2i :: [2]i32
Vec3i :: [3]i32
when false {
// TODO(Ed) : Evaluate if this is needed
Vec2 :: Vec2_f32
Vec2_f32 :: struct #raw_union {
basis : [2] f32,
using components : struct {
x, y : f32
}
}
// make_vec2 :: proc( x, y : f32 ) {
// }
Vec3 :: Vec3_f32
Vec3_f32 :: struct #raw_union {
basis : [3] f32,
using components : struct {
x, y, z : f32
}
}
}

View File

@ -187,7 +187,8 @@ UI_Box :: struct {
label : string,
// Regenerated per frame.
first, last, prev, next, parent : ^ UI_Box,
using _ : DLL_NodeFull( UI_Box ), // first, last, prev, next
parent : ^ UI_Box,
num_children : i32,
flags : UI_BoxFlags,
@ -195,8 +196,9 @@ UI_Box :: struct {
style : UI_Style,
// Persistent Data
// hash_links : DLL_Node_PN( ^ UI_Box), // This isn't necessary if not using RJF hash table.
// prev_computed : UI_Computed,
// prev_style : UI_Style,
// prev_style : UI_Style,v
mouse : UI_InteractState,
keyboard : UI_InteractState,
}
@ -240,7 +242,6 @@ UI_State :: struct {
drag_start_mouse : Vec2,
// drag_state_arena : ^ Arena,
// drag_state data : string,
}
ui_key_from_string :: proc( value : string ) -> UI_Key {

View File

@ -1,5 +0,0 @@
package sectr

43
code/ui_rjf.odin Normal file
View File

@ -0,0 +1,43 @@
package sectr
when false {
ui_box_cache_insert :: proc( using cache : HMap_RJF( ^ UI_Box ), key : u64, value : ^ UI_Box ) -> ^ UI_Box {
slot := rjf_hmap_get_slot( cache, key )
// dll_insert_raw( nil, slot.first, slot.last, slot.last, value )
{
new_links := & new.hash_links
// Empty Case
if first == null {
first = new
last = new
new_links.next = null
new_links.prev = null
}
else if position == null {
// Position is not set, insert at beginning
new_links.next = first
first.first = new
first = new
new_links.prev = null
}
else if position == last {
// Positin is set to last, insert at end
last.last = new
new_links.prev = last
last = new
new_links.next = null
}
else {
// Insert around position
if position.next != null {
position.next.prev = new
}
new.next = position.next
position.next = new
new.prev = position
}
}
}
}