Initial impl pass for grime array and hashtable

This commit is contained in:
Edward R. Gonzalez 2024-02-14 02:29:28 -05:00
parent 2da0554c78
commit 9b959ef869
3 changed files with 500 additions and 1 deletions

View File

@ -2,6 +2,7 @@
package sectr
// At least its less than C/C++ ...
import "base:builtin"
import "core:mem"
import "core:mem/virtual"
import "core:path/filepath"
@ -27,6 +28,7 @@ terabyte :: proc ( tb : $ integer_type ) -> integer_type {
return tb * Terabyte
}
copy :: builtin.copy
Allocator :: mem.Allocator
AllocatorError :: mem.Allocator_Error
alloc :: mem.alloc
@ -34,6 +36,7 @@ alloc_bytes :: mem.alloc_bytes
Arena :: mem.Arena
arena_allocator :: mem.arena_allocator
arena_init :: mem.arena_init
free :: mem.free
ptr_offset :: mem.ptr_offset
slice_ptr :: mem.slice_ptr
Tracking_Allocator :: mem.Tracking_Allocator
@ -42,7 +45,6 @@ tracking_allocator_init :: mem.tracking_allocator_init
file_name_from_path :: filepath.short_stem
OS_Type :: type_of(ODIN_OS)
get_bounds :: proc {
box_get_bounds,
view_get_bounds,

239
code/grime_array.odin Normal file
View File

@ -0,0 +1,239 @@
// 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 HashTable impl with the [dynamic] array for now to isolate
// what in the world is going on with the memory...
package sectr
import "core:c/libc"
import "core:mem"
Array :: struct ( $ Type : typeid ) {
allocator : Allocator,
capacity : u64,
num : u64,
data : [^]Type,
}
array_to_slice :: proc ( arr : Array( $ Type) ) -> []Type {
using arr; return slice_ptr( data, num )
}
array_grow_formula :: proc( value : u64 ) -> u64 {
return 2 * value + 8
}
array_init :: proc( $ Type : typeid, allocator : Allocator ) -> ( Array(Type), AllocatorError ) {
return array_init_reserve( Type, allocator, array_grow_formula(0) )
}
array_init_reserve :: proc( $ Type : typeid, allocator : Allocator, capacity : u64 ) -> ( Array(Type), AllocatorError )
{
raw_data, result_code = alloc( capacity * size_of(Type), allocator = allocator )
result : Array( Type)
result.data = cast( [^] Type ) raw_data
result.allocator = allocator
result.capacity = capacity
return result, result_code
}
array_append :: proc( array : ^ Array( $ Type), value : Type ) -> AllocatorError
{
using array
if num == capacity
{
grow_result := array_grow( array, capacity )
if grow_result != AllocatorError.None {
return grow_result
}
}
data[ num ] = value
num += 1
return AllocatorError.None
}
array_append_slice :: proc( array : ^ Array( $ Type ), items : []Type ) -> AllocatorError
{
using array
if num + len(items) > capacity
{
grow_result := array_grow( array, capacity )
if grow_result != AllocatorError.None {
return grow_result
}
}
// Note(Ed) : Original code from gencpp
// libc.memcpy( ptr_offset(data, num), raw_data(items), len(items) * size_of(Type) )
// TODO(Ed) : VERIFY VIA DEBUG THIS COPY IS FINE.
target := ptr_offset( data, num )
copy( slice_ptr(target, capacity - num), items )
num += len(items)
return AllocatorError.None
}
array_append_at :: proc( array : ^ Array( $ Type ), item : Type, id : u64 ) -> AllocatorError
{
id := id
using array
if id >= num {
id = num - 1
}
if id < 0 {
id = 0
}
if capacity < num + 1
{
grow_result := array_grow( array, capacity )
if grow_result != AllocatorError.None {
return grow_result
}
}
target := & data[id]
// TODO(Ed) : VERIFY VIA DEBUG THIS COPY IS FINE.
dst = slice_ptr( ptr_offset(target) + 1, num - id - 1 )
src = slice_ptr( target, num - id )
copy( dst, src )
// Note(Ed) : Original code from gencpp
// libc.memmove( ptr_offset(target, 1), target, (num - idx) * size_of(Type) )
data[id] = item
num += 1
return AllocatorError.None
}
array_append_at_slice :: proc ( array : ^ Array( $ Type ), items : []Type, id : u64 ) -> AllocatorError
{
id := id
using array
if id >= num {
return array_append_slice( items )
}
if len(items) > capacity
{
grow_result := array_grow( array, capacity )
if grow_result != AllocatorError.None {
return grow_result
}
}
// Note(Ed) : Original code from gencpp
// target := ptr_offset( data, id + len(items) )
// src := ptr_offset( data, id )
// libc.memmove( target, src, num - id * size_of(Type) )
// libc.memcpy ( src, raw_data(items), len(items) * size_of(Type) )
// TODO(Ed) : VERIFY VIA DEBUG THIS COPY IS FINE
target := & data[id + len(items)]
dst := slice_ptr( target, num - id - len(items) )
src := slice_ptr( & data[id], num - id )
copy( dst, src )
copy( src, items )
num += len(items)
return AllocatorError.None
}
array_back :: proc( array : ^ Array( $ Type ) ) -> ^ Type {
using array; return & data[ num - 1 ]
}
array_clear :: proc ( array : ^ Array( $ Type ) ) {
array.num = 0
}
array_fill :: proc ( array : ^ Array( $ Type ), begin, end : u64, value : Type ) -> b32
{
using array
if begin < 0 || end >= num {
return false
}
for id := begin; id < end; id += 1 {
data[ id ] = value
}
return true
}
array_free :: proc( array : ^ Array( $ Type ) ) {
using array
free( data, allocator )
data = nil
}
array_grow :: proc( array : ^ Array( $ Type ), min_capacity : u64 ) -> AllocatorError
{
using array
new_capacity = grow_formula( capacity )
if new_capacity < min_capacity {
new_capacity = min_capacity
}
return array_set_capacity( array, new_capacity )
}
array_pop :: proc( array : ^ Array( $ Type ) ) {
verify( array.num == 0, "Attempted to pop an array with no elements" )
array.num -= 1
}
array_remove_at :: proc( array : ^ Array( $ Type ), id : u64 )
{
using array
verify( id >= num, "Attempted to remove from an index larger than the array" )
left = slice_ptr( data, id )
right = slice_ptr( ptr_offset( memory_after(left), 1), num - len(left) - 1 )
copy( left, right )
num -= 1
}
array_reserve :: proc( array : ^ Array( $ Type ), new_capacity : u64 ) -> AllocatorError
{
using array
if capacity < new_capacity {
return array_set_capacity( array, new_capacity )
}
return AllocatorError.None
}
array_resize :: proc ( array : ^ Array( $ Type ), num : u64 ) -> AllocatorError
{
if array.capacity < num
{
grow_result := array_grow( array, capacity )
if grow_result != AllocatorError.None {
return grow_result
}
}
array.num = num
return AllocatorError.None
}
array_set_capacity :: proc( array : ^ Array( $ Type ), new_capacity : u64 ) -> AllocatorError
{
using array
if new_capacity == capacity {
return true
}
if new_capacity < num {
num = new_capacity
return true
}
raw_data, result_code = alloc( new_capacity * size_of(Type), allocator = allocator )
data = cast( [^] Type ) raw_data
capacity = new_capacity
return result_code
}

258
code/grime_hashtable.odin Normal file
View File

@ -0,0 +1,258 @@
// This is an alternative to Odin's default map type.
// The only reason I may need this is due to issues with allocator callbacks or something else going on
// with hot-reloads...
package sectr
// This might be problematic...
HT_MapProc :: #type proc( $ Type : typeid, key : u64, value : Type )
HT_MapMutProc :: #type proc( $ Type : typeid, key : u64, value : ^ Type )
HT_FindResult :: struct {
hash_index : i64,
prev_index : i64,
entry_index : i64,
}
HashTable_Entry :: struct ( $ Type : typeid) {
key : u64,
next : u64,
value : Type,
}
HashTable :: struct ( $ Type : typeid) {
hashes : Array( i64 ),
entries : Array( HashTable_Entry(Type) ),
}
hashtable_init :: proc( $ Type : typeid, allocator : Allocator ) -> ( HashTable( Type), AllocatorError ) {
return hashtable_init_reserve( Type, allocator )
}
hashtable_init_reserve :: proc ( $ Type : typeid, allcoator : Allocator, num : u64 ) -> ( HashTable( Type), AllocatorError )
{
result : HashTable(Type)
hashes_result, entries_result : AllocatorError
result.hashes, hashes_result = array_init_reserve( i64, allocator, num )
if hashes_result != AllocatorError.None {
ensure( false, "Failed to allocate hashes array" )
return result, hashes_result
}
result.entries, entries_result = array_init_reserve( allocator, num )
if entries_result != AllocatorError.None {
ensure( false, "Failed to allocate entries array" )
return result, entries_result
}
return result, AllocatorError.None
}
hashtable_clear :: proc( ht : ^ HashTable( $ Type ) ) {
using ht
for id := 0; id < hashes.num; id += 1 {
hashes[id] = -1
}
array_clear( hashes )
array_clear( entries )
}
hashtable_destroy :: proc( ht : ^ HashTable( $ Type ) ) {
if hashes.data && hashes.capacity {
array_free( hashes )
array_free( entries )
}
}
hashtable_get :: proc( ht : ^ HashTable( $ Type ), key : u64 ) -> ^ Type
{
using ht
id := hashtable_find( key ).entry_index
if id >= 0 {
return & entries[id].value
}
return nil
}
hashtable_map :: proc( ht : ^ HashTable( $ Type), map_proc : HT_MapProc ) {
using ht
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 )
}
}
hashtable_map_mut :: proc( ht : ^ HashTable( $ Type), map_proc : HT_MapMutProc ) {
using ht
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 )
}
}
hashtable_grow :: proc( ht : ^ HashTable( $ Type ) ) -> AllocatorError {
new_num := array_grow_formula( entries.num )
return rehash( ht, new_num )
}
hashtable_rehash :: proc ( ht : ^ HashTabe( $ Type ), new_num : i64 ) -> AllocatorError
{
last_added_index : i64
new_ht, init_result := hashtable_init_reserve( Type, ht.hashes.allocator, new_num )
if init_result != AllocatorError.None {
ensure( false, "New hashtable failed to allocate" )
return init_result
}
for id := 0; id < new_ht.hashes.num; id += 1 {
new_ht.hashes[id] = -1
}
for id := 0; id < ht.entries.num; id += 1 {
find_result : HT_FindResult
if new_ht.hashes.num == 0 {
hashtable_grow( new_ht )
}
entry = & entries[id]
find_result = hashtable_find( & new_ht, entry.key )
last_added_index = hashtable_add_entry( & new_ht, entry.key )
if find_result.prev_index < 0 {
new_ht.hashes[ find_result.hash_index ] = last_added_index
}
else {
new_ht.hashes[ find_result.prev_index ].next = last_added_index
}
new_ht.entries[ last_added_index ].next = find_result.entry_index
new_ht.entries[ last_added_index ].value = entry.value
}
hashtable_destroy( ht )
(ht ^) = new_ht
return AllocatorError.None
}
hashtable_rehash_fast :: proc ( ht : ^ HashTable( $ Type ) )
{
using ht
for id := 0; id < entries.num; id += 1 {
entries[id].Next = -1;
}
for id := 0; id < hashes.num; id += 1 {
hashes[id] = -1
}
for id := 0; id < entries.num; id += 1 {
entry := & entries[id]
find_result := hashtable_find( entry.key )
if find_result.prev_index < 0 {
hashes[ find_result.hash_index ] = id
}
else {
entries[ find_result.prev_index ].next = id
}
}
}
hashtable_remove :: proc ( ht : ^ HashTable( $ Type ), key : u64 ) {
using ht
find_result := hashtable_find( key )
if find_result.entry_index >= 0 {
array_remove_at( & ht.entries, find_result.entry_index )
hashtable_rehash_fast( ht )
}
}
hashtable_remove_entry :: proc( ht : ^ HashTable( $ Type ), id : i64 ) {
array_remove_at( & ht.entries, id )
}
hashtable_set :: proc( ht : ^ HashTable( $ Type), key : u64, value : Type ) -> AllocatorError
{
using ht
id := 0
find_result : HT_FindResult
if hashes.num == 0
{
grow_result := hashtable_grow( ht )
if grow_result != AllocatorError.None {
return grow_result
}
}
find_result = hashtable_find( key )
if find_result.entry_index >= 0 {
id = find_result.entry_index
}
else
{
id = hashtable_add_entry( ht, key )
if find_result.prev_index >= 0 {
entries[ find_result.prev_index ].next = id
}
else {
hashes[ find_result.hash_index ] = id
}
}
entries[id].value = value
if hashtable_full( ht ) {
return hashtable_grow( ht )
}
return AllocatorError.None
}
hashtable_slot :: proc( ht : ^ HashTable( $ Type), key : u64 ) -> i64 {
using ht
for id := 0; id < hashes.num; id += 1 {
if hashes[id] == key {
return id
}
}
return -1
}
hashtable_add_entry :: proc( ht : ^ HashTable( $ Type), key : u64 ) -> i64 {
using ht
entry : HashTable_Entry = { key, -1 }
id := entries.num
array_append( entries, entry )
return id
}
hashtable_find :: proc( ht : ^ HashTable( $ Type), key : u64 ) -> HT_FindResult
{
using ht
find_result : HT_FindResult = { -1, -1, -1 }
if hashes.num > 0 {
result.hash_index = key % hash.num
result.entry_index = hashes[ result.hash_index ]
for ; result.entry_index >= 0; {
if entries[ result.entry_index ].key == key {
break
}
result.prev_index = result.entry_index
result.entry_index = entries[ result.entry_index ].next
}
}
return result
}
hashtable_full :: proc( ht : ^ HashTable( $ Type) ) -> b32 {
return 0.75 * hashes.num < entries.num
}