This repository has been archived on 2025-02-04. You can view files and clone it, but cannot push or open issues or pull requests.
helpless/oogabooga/hash_table.c
Charlie 18f4fc8123 - Text rendering
- Font loading
	- Measuring for formatting & justification
	- Utf8 Glyph walking
	- Commented example in oogabooga/examples/text_rendering.c
- Small 2D renderer refactor
	- Pass 8-bit integers "type" and "sampler_index" to shader
	- Sample texture differently depending on "type" (text or regular quad)
	- Sample with nearest/linear min/mag depending on sampler_index
	- Set min/mag filtering in Draw_Quad
	- Images are now created and deleted directly with gfx calls rather than deferring it for gfx_update.
	- We can now set image sub data with gfx_set_image_data()
	- Images are no longer hard coded to 4 channels
- Utf8 utility:
	- utf8_to_utf32(): convert utf8 bytes to a single u32 codepoint
	- next_utf8(): Convert first utf8 character in a string to a u32 codepoint and advance the passed string to the next unicode
- Renamed m4_multiply -> m4_mul for consistency
- Refactored os window to be DPI aware (scaled_width vs pixel_width)
- in minimal example, renamed hammer_xform -> rect_xform
2024-07-07 20:27:34 +02:00

218 lines
6 KiB
C

// Very naive implementation but it should be very cache efficient so it's alright
// for non-excessive use for now.
/*
Example Usage:
// Make a table with key type 'string' and value type 'int', allocated on the heap
Hash_Table table = make_hash_table(string, int, get_heap_allocator());
// Set key "Key string" to integer value 69. This returns whether or not key was newly added.
string key = STR("Key string");
bool newly_added = hash_table_set(&table, key, 69);
// Find value associated with given key. Returns pointer to that value.
string other_key = STR("Some other key");
int* value = hash_table_find(&table, other_key);
if (value) {
// Pointer is OK, item with key exists
} else {
// Pointer is null, item with key does NOT exist
}
// Same as hash_table_find() != NULL
string another_key = STR("Another key");
if (hash_table_contains(&table, another_key)) {
}
// Reset all entries (but keep allocated memory)
hash_table_reset(&table);
// Free allocated entries in hash table
hash_table_destroy(&table);
Limitations:
- Key can only be a base type or pointer
- Key and value passed to the following function needs to be lvalues (we need to be able to take their addresses with '&'):
- hash_table_add
- hash_table_find
- hash_table_contains
- hash_table_set
Example:
hash_table_set(&table, my_key+5, my_value+3); // ERROR
int key = my_key+5;
int value = my_value+3;
hash_table_set(&table, key, value); // OK
*/
typedef struct Hash_Table Hash_Table;
// API:
#define make_hash_table_reserve(Key_Type, Value_Type, capacity_count, allocator) \
make_hash_table_reserve(sizeof(Key_Type), sizeof(Value_Type), capacity_count, allocator)
#define make_hash_table(Key_Type, Value_Type, allocator) \
make_hash_table_raw(sizeof(Key_Type), sizeof(Value_Type), allocator)
#define hash_table_add(table_ptr, key, value) \
hash_table_add_raw((table_ptr), get_hash(key), &(key), &(value), sizeof(key), sizeof(value))
#define hash_table_find(table_ptr, key) \
hash_table_find_raw((table_ptr), get_hash(key))
#define hash_table_contains(table_ptr, key) \
hash_table_contains_raw((table_ptr), get_hash(key))
#define hash_table_set(table_ptr, key, value) \
hash_table_set_raw((table_ptr), get_hash(key), &key, &value, sizeof(key), sizeof(value))
void hash_table_reserve(Hash_Table *t, u64 required_count);
typedef struct Hash_Table {
// Each entry is hash-key-value
// Hash is sizeof(u64) bytes, key is _key_size bytes and value is _value_size bytes
void *entries;
u64 count; // Number of valid entries
u64 capacity_count; // Number of allocated entries
u64 _key_size;
u64 _value_size;
Allocator allocator;
} Hash_Table;
Hash_Table make_hash_table_reserve_raw(u64 key_size, u64 value_size, u64 capacity_count, Allocator allocator) {
capacity_count = min(capacity_count, 8);
Hash_Table t = ZERO(Hash_Table);
t._key_size = key_size;
t._value_size = value_size;
t.allocator = allocator;
u64 entry_size = value_size+sizeof(u64);
t.entries = alloc(t.allocator, entry_size*capacity_count);
memset(t.entries, 0, entry_size*capacity_count);
t.capacity_count = capacity_count;
return t;
}
inline Hash_Table make_hash_table_raw(u64 key_size, u64 value_size, Allocator allocator) {
return make_hash_table_reserve_raw(key_size, value_size, 128, allocator);
}
void hash_table_reset(Hash_Table *t) {
t->count = 0;
}
void hash_table_destroy(Hash_Table *t) {
dealloc(t->allocator, t->entries);
t->entries = 0;
t->count = 0;
t->capacity_count = 0;
}
void hash_table_reserve(Hash_Table *t, u64 required_count) {
u64 entry_size = t->_value_size+sizeof(u64);
u64 required_size = required_count*entry_size;
u64 current_size = t->capacity_count*entry_size;
if (current_size >= required_size) return;
u64 new_count = get_next_power_of_two(required_count);
u64 new_size = new_count*entry_size;
void *new_entries = alloc(t->allocator, new_size);
memcpy(new_entries, t->entries, current_size);
dealloc(t->allocator, t->entries);
t->entries = new_entries;
t->capacity_count = new_count;
}
// This can add multiple entries of same hash, beware!
void hash_table_add_raw(Hash_Table *t, u64 hash, void *k, void *v, u64 key_size, u64 value_size) {
assert(t->_key_size == key_size, "Key type size does not match hash table initted key type size");
assert(t->_value_size == value_size, "Value type size does not match hash table initted value type size");
hash_table_reserve(t, t->count+1);
u64 entry_size = t->_value_size+sizeof(u64);
u64 index = entry_size*t->count;
t->count += 1;
u64 hash_offset = 0;
u64 value_offset = hash_offset + sizeof(u64);
memcpy((u8*)t->entries+index+hash_offset, &hash, sizeof(u64));
memcpy((u8*)t->entries+index+value_offset, v, value_size);
}
void *hash_table_find_raw(Hash_Table *t, u64 hash) {
// #Speed #Incomplete
// Do quadratic probe 'triangular numbers'
u64 entry_size = t->_value_size+sizeof(u64);
u64 hash_offset = 0;
u64 value_offset = hash_offset + sizeof(u64);
for (u64 i = 0; i < t->count; i += 1) {
u64 existing_hash = *(u64*)((u8*)t->entries+i*entry_size+hash_offset);
if (existing_hash == hash) {
void *value = ((u8*)t->entries+i*entry_size+value_offset);
return value;
}
}
return 0;
}
void *hash_table_get_nth_value(Hash_Table *t, u64 n) {
assert(n < t->count, "Hash table n is out of range");
u64 entry_size = t->_value_size+sizeof(u64);
u64 hash_offset = 0;
u64 value_offset = hash_offset + sizeof(u64);
return (u8*)t->entries+entry_size*n+value_offset;
}
bool hash_table_contains_raw(Hash_Table *t, u64 hash) {
return hash_table_find_raw(t, hash) != 0;
}
// Returns true if key was newly added or false if it already existed
bool hash_table_set_raw(Hash_Table *t, u64 hash, void *k, void *v, u64 key_size, u64 value_size) {
bool newly_added = true;
if (hash_table_contains_raw(t, hash)) newly_added = false;
if (newly_added) {
hash_table_add_raw(t, hash, k, v, key_size, value_size);
} else {
memcpy(hash_table_find_raw(t, hash), v, value_size);
}
return newly_added;
}