Naive hash table & tests
This commit is contained in:
parent
05919248eb
commit
ebaa52c326
8 changed files with 388 additions and 18 deletions
8
build.c
8
build.c
|
@ -3,7 +3,7 @@
|
||||||
///
|
///
|
||||||
// Build config stuff
|
// Build config stuff
|
||||||
|
|
||||||
#define RUN_TESTS 0
|
#define RUN_TESTS 1
|
||||||
|
|
||||||
// This is only for people developing oogabooga!
|
// This is only for people developing oogabooga!
|
||||||
#define OOGABOOGA_DEV 1
|
#define OOGABOOGA_DEV 1
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
#define ENABLE_PROFILING 0
|
#define ENABLE_PROFILING 0
|
||||||
|
|
||||||
// ENABLE_SIMD Requires CPU to support at least SSE1 but I will be very surprised if you find a system today which doesn't
|
// ENABLE_SIMD Requires CPU to support at least SSE1 but I will be very surprised if you find a system today which doesn't
|
||||||
#define ENABLE_SIMD 1
|
#define ENABLE_SIMD 0
|
||||||
|
|
||||||
|
|
||||||
#define INITIAL_PROGRAM_MEMORY_SIZE MB(5)
|
#define INITIAL_PROGRAM_MEMORY_SIZE MB(5)
|
||||||
|
@ -40,10 +40,10 @@ typedef struct Context_Extra {
|
||||||
// #include "oogabooga/examples/minimal_game_loop.c"
|
// #include "oogabooga/examples/minimal_game_loop.c"
|
||||||
|
|
||||||
// An engine dev stress test for rendering
|
// An engine dev stress test for rendering
|
||||||
// #include "oogabooga/examples/renderer_stress_test.c"
|
#include "oogabooga/examples/renderer_stress_test.c"
|
||||||
|
|
||||||
// Randy's example game that he's building out as a tutorial for using the engine
|
// Randy's example game that he's building out as a tutorial for using the engine
|
||||||
#include "entry_randygame.c"
|
// #include "entry_randygame.c"
|
||||||
|
|
||||||
// This is where you swap in your own project!
|
// This is where you swap in your own project!
|
||||||
// #include "entry_yourepicgamename.c"
|
// #include "entry_yourepicgamename.c"
|
||||||
|
|
|
@ -118,3 +118,19 @@ void pop_context() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
u64 get_next_power_of_two(u64 x) {
|
||||||
|
if (x == 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
x--;
|
||||||
|
x |= x >> 1;
|
||||||
|
x |= x >> 2;
|
||||||
|
x |= x >> 4;
|
||||||
|
x |= x >> 8;
|
||||||
|
x |= x >> 16;
|
||||||
|
x |= x >> 32;
|
||||||
|
|
||||||
|
return x + 1;
|
||||||
|
}
|
|
@ -76,7 +76,7 @@ int entry(int argc, char **argv) {
|
||||||
draw_frame.view = camera_view;
|
draw_frame.view = camera_view;
|
||||||
|
|
||||||
seed_for_random = 69;
|
seed_for_random = 69;
|
||||||
for (u64 i = 0; i < 100000; i++) {
|
for (u64 i = 0; i < 10000; i++) {
|
||||||
float32 aspect = (float32)window.width/(float32)window.height;
|
float32 aspect = (float32)window.width/(float32)window.height;
|
||||||
float min_x = -aspect;
|
float min_x = -aspect;
|
||||||
float max_x = aspect;
|
float max_x = aspect;
|
||||||
|
@ -102,11 +102,13 @@ int entry(int argc, char **argv) {
|
||||||
|
|
||||||
draw_image(bush_image, v2(0.65, 0.65), v2(0.2*sin(now), 0.2*sin(now)), COLOR_WHITE);
|
draw_image(bush_image, v2(0.65, 0.65), v2(0.2*sin(now), 0.2*sin(now)), COLOR_WHITE);
|
||||||
|
|
||||||
|
//draw_text(font, "I am text", v2(0.1, 0.6), COLOR_BLACK);
|
||||||
|
//draw_text(font, "I am text", v2(0.09, 0.61), COLOR_WHITE);
|
||||||
|
|
||||||
tm_scope_cycles("gfx_update") {
|
tm_scope_cycles("gfx_update") {
|
||||||
gfx_update();
|
gfx_update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (is_key_just_released('E')) {
|
if (is_key_just_released('E')) {
|
||||||
log("FPS: %.2f", 1.0 / delta);
|
log("FPS: %.2f", 1.0 / delta);
|
||||||
log("ms: %.2f", delta*1000.0);
|
log("ms: %.2f", delta*1000.0);
|
||||||
|
|
84
oogabooga/hash.c
Normal file
84
oogabooga/hash.c
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
|
||||||
|
|
||||||
|
#define PRIME64_1 11400714785074694791ULL
|
||||||
|
#define PRIME64_2 14029467366897019727ULL
|
||||||
|
#define PRIME64_3 1609587929392839161ULL
|
||||||
|
#define PRIME64_4 9650029242287828579ULL
|
||||||
|
#define PRIME64_5 2870177450012600261ULL
|
||||||
|
|
||||||
|
static inline u64 xx_hash(u64 x) {
|
||||||
|
u64 h64 = PRIME64_5 + 8;
|
||||||
|
h64 += x * PRIME64_3;
|
||||||
|
h64 = ((h64 << 23) | (h64 >> (64 - 23))) * PRIME64_2 + PRIME64_4;
|
||||||
|
h64 ^= h64 >> 33;
|
||||||
|
h64 *= PRIME64_2;
|
||||||
|
h64 ^= h64 >> 29;
|
||||||
|
h64 *= PRIME64_3;
|
||||||
|
h64 ^= h64 >> 32;
|
||||||
|
return h64;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64 city_hash(string s) {
|
||||||
|
const u64 k = 0x9ddfea08eb382d69ULL;
|
||||||
|
u64 a = s.count;
|
||||||
|
u64 b = s.count * 5;
|
||||||
|
u64 c = 9;
|
||||||
|
u64 d = b;
|
||||||
|
|
||||||
|
if (s.count <= 16) {
|
||||||
|
memcpy(&a, s.data, sizeof(u64));
|
||||||
|
memcpy(&b, s.data + s.count - 8, sizeof(u64));
|
||||||
|
} else {
|
||||||
|
memcpy(&a, s.data, sizeof(u64));
|
||||||
|
memcpy(&b, s.data + 8, sizeof(u64));
|
||||||
|
memcpy(&c, s.data + s.count - 8, sizeof(u64));
|
||||||
|
memcpy(&d, s.data + s.count - 16, sizeof(u64));
|
||||||
|
}
|
||||||
|
|
||||||
|
a += b;
|
||||||
|
a = (a << 43) | (a >> (64 - 43));
|
||||||
|
a += c;
|
||||||
|
a = a * 5 + 0x52dce729;
|
||||||
|
d ^= a;
|
||||||
|
d = (d << 44) | (d >> (64 - 44));
|
||||||
|
d += b;
|
||||||
|
|
||||||
|
return d * k;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 djb2_hash(string s) {
|
||||||
|
u64 hash = 5381;
|
||||||
|
for (u64 i = 0; i < s.count; i++) {
|
||||||
|
hash = ((hash << 5) + hash) + s.data[i];
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 string_get_hash(string s) {
|
||||||
|
if (s.count > 32) return djb2_hash(s);
|
||||||
|
return city_hash(s);
|
||||||
|
}
|
||||||
|
u64 pointer_get_hash(void *p) {
|
||||||
|
return xx_hash((u64)p);
|
||||||
|
}
|
||||||
|
u64 float64_get_hash(float64 x) {
|
||||||
|
return xx_hash(*(u64*)&x);
|
||||||
|
}
|
||||||
|
u64 float32_get_hash(float32 x) {
|
||||||
|
return float64_get_hash((float64)x);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define get_hash(x) _Generic((x), \
|
||||||
|
string: string_get_hash, \
|
||||||
|
s8: xx_hash, \
|
||||||
|
u8: xx_hash, \
|
||||||
|
s16: xx_hash, \
|
||||||
|
u16: xx_hash, \
|
||||||
|
s32: xx_hash, \
|
||||||
|
u32: xx_hash, \
|
||||||
|
s64: xx_hash, \
|
||||||
|
u64: xx_hash, \
|
||||||
|
f32: float32_get_hash, \
|
||||||
|
f64: float64_get_hash, \
|
||||||
|
default: pointer_get_hash \
|
||||||
|
)(x)
|
208
oogabooga/hash_table.c
Normal file
208
oogabooga/hash_table.c
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
|
||||||
|
// Very naive implementation for now but it should be cache efficient so not really a
|
||||||
|
// problem until very large (in which case you should probably write your own data structure
|
||||||
|
// anyways)
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
|
@ -197,7 +197,7 @@ Heap_Block *make_heap_block(Heap_Block *parent, u64 size) {
|
||||||
// #Speed #Cleanup
|
// #Speed #Cleanup
|
||||||
if (((u8*)block)+size >= ((u8*)program_memory)+program_memory_size) {
|
if (((u8*)block)+size >= ((u8*)program_memory)+program_memory_size) {
|
||||||
u64 minimum_size = ((u8*)block+size) - (u8*)program_memory + 1;
|
u64 minimum_size = ((u8*)block+size) - (u8*)program_memory + 1;
|
||||||
u64 new_program_size = max((cast(u64)(minimum_size*2)), program_memory_size*2);
|
u64 new_program_size = get_next_power_of_two(minimum_size);
|
||||||
assert(new_program_size >= minimum_size, "Bröd");
|
assert(new_program_size >= minimum_size, "Bröd");
|
||||||
const u64 ATTEMPTS = 1000;
|
const u64 ATTEMPTS = 1000;
|
||||||
for (u64 i = 0; i <= ATTEMPTS; i++) {
|
for (u64 i = 0; i <= ATTEMPTS; i++) {
|
||||||
|
|
|
@ -253,9 +253,12 @@ typedef u8 bool;
|
||||||
#include "string.c"
|
#include "string.c"
|
||||||
#include "unicode.c"
|
#include "unicode.c"
|
||||||
#include "string_format.c"
|
#include "string_format.c"
|
||||||
|
#include "hash.c"
|
||||||
#include "path_utils.c"
|
#include "path_utils.c"
|
||||||
#include "linmath.c"
|
#include "linmath.c"
|
||||||
|
|
||||||
|
#include "hash_table.c"
|
||||||
|
|
||||||
#include "os_interface.c"
|
#include "os_interface.c"
|
||||||
#include "gfx_interface.c"
|
#include "gfx_interface.c"
|
||||||
|
|
||||||
|
@ -291,6 +294,8 @@ typedef u8 bool;
|
||||||
|
|
||||||
#include "tests.c"
|
#include "tests.c"
|
||||||
|
|
||||||
|
#define malloc please_use_alloc_for_memory_allocations_instead_of_malloc
|
||||||
|
#define free please_use_dealloc_for_memory_deallocations_instead_of_free
|
||||||
|
|
||||||
void oogabooga_init(u64 program_memory_size) {
|
void oogabooga_init(u64 program_memory_size) {
|
||||||
context.logger = default_logger;
|
context.logger = default_logger;
|
||||||
|
|
|
@ -36,6 +36,8 @@ void log_heap() {
|
||||||
|
|
||||||
void test_allocator(bool do_log_heap) {
|
void test_allocator(bool do_log_heap) {
|
||||||
|
|
||||||
|
u64 h = get_hash((string*)69);
|
||||||
|
|
||||||
Allocator heap = get_heap_allocator();
|
Allocator heap = get_heap_allocator();
|
||||||
|
|
||||||
// Basic allocation and free
|
// Basic allocation and free
|
||||||
|
@ -1018,6 +1020,51 @@ void test_linmath() {
|
||||||
assert(floats_roughly_match(v3_dot, 38), "Failed: v3_dot_product");
|
assert(floats_roughly_match(v3_dot, 38), "Failed: v3_dot_product");
|
||||||
assert(floats_roughly_match(v4_dot, 30), "Failed: v4_dot_product");
|
assert(floats_roughly_match(v4_dot, 30), "Failed: v4_dot_product");
|
||||||
}
|
}
|
||||||
|
void test_hash_table() {
|
||||||
|
// Initialize a hash table with key type 'string' and value type 'int'
|
||||||
|
Hash_Table table = make_hash_table(string, int, get_heap_allocator());
|
||||||
|
|
||||||
|
// Test hash_table_set for adding new key-value pairs
|
||||||
|
string key1 = STR("Key string");
|
||||||
|
int value1 = 69;
|
||||||
|
bool newly_added = hash_table_set(&table, key1, value1);
|
||||||
|
assert(newly_added == true, "Failed: Key should be newly added");
|
||||||
|
|
||||||
|
// Test hash_table_find for existing key
|
||||||
|
int* found_value = hash_table_find(&table, key1);
|
||||||
|
assert(found_value != NULL, "Failed: Key should exist in hash table");
|
||||||
|
assert(*found_value == 69, "Failed: Value should be 69, got %i", *found_value);
|
||||||
|
|
||||||
|
// Test hash_table_set for updating existing key
|
||||||
|
int new_value1 = 70;
|
||||||
|
newly_added = hash_table_set(&table, key1, new_value1);
|
||||||
|
assert(newly_added == false, "Failed: Key should not be newly added");
|
||||||
|
|
||||||
|
// Test hash_table_find for updated value
|
||||||
|
found_value = hash_table_find(&table, key1);
|
||||||
|
assert(found_value != NULL, "Failed: Key should exist in hash table");
|
||||||
|
assert(*found_value == 70, "Failed: Value should be 70, got %i", *found_value);
|
||||||
|
|
||||||
|
// Test hash_table_contains for existing key
|
||||||
|
bool contains = hash_table_contains(&table, key1);
|
||||||
|
assert(contains == true, "Failed: Hash table should contain key1");
|
||||||
|
|
||||||
|
// Test hash_table_contains for non-existing key
|
||||||
|
string key2 = STR("Non-existing key");
|
||||||
|
contains = hash_table_contains(&table, key2);
|
||||||
|
assert(contains == false, "Failed: Hash table should not contain key2");
|
||||||
|
|
||||||
|
// Test hash_table_reset
|
||||||
|
hash_table_reset(&table);
|
||||||
|
found_value = hash_table_find(&table, key1);
|
||||||
|
assert(found_value == NULL, "Failed: Hash table should be empty after reset");
|
||||||
|
|
||||||
|
// Test hash_table_destroy
|
||||||
|
hash_table_destroy(&table);
|
||||||
|
assert(table.entries == NULL, "Failed: Hash table entries should be NULL after destroy");
|
||||||
|
assert(table.count == 0, "Failed: Hash table count should be 0 after destroy");
|
||||||
|
assert(table.capacity_count == 0, "Failed: Hash table capacity count should be 0 after destroy");
|
||||||
|
}
|
||||||
void oogabooga_run_tests() {
|
void oogabooga_run_tests() {
|
||||||
|
|
||||||
|
|
||||||
|
@ -1033,17 +1080,21 @@ void oogabooga_run_tests() {
|
||||||
test_strings();
|
test_strings();
|
||||||
print("OK!\n");
|
print("OK!\n");
|
||||||
|
|
||||||
|
|
||||||
//print("Thread bombing allocator... ");
|
// #Temporary
|
||||||
//Thread* threads[100];
|
// This makes entire os freeze in release lol
|
||||||
//for (int i = 0; i < 100; i++) {
|
#if CONFIGURATION != RELEASE
|
||||||
// threads[i] = os_make_thread(test_allocator_threaded, get_heap_allocator());
|
print("Thread bombing allocator... ");
|
||||||
// os_start_thread(threads[i]);
|
Thread* threads[100];
|
||||||
//}
|
for (int i = 0; i < 100; i++) {
|
||||||
//for (int i = 0; i < 100; i++) {
|
threads[i] = os_make_thread(test_allocator_threaded, get_heap_allocator());
|
||||||
// os_join_thread(threads[i]);
|
os_start_thread(threads[i]);
|
||||||
//}
|
}
|
||||||
//print("OK!\n");
|
for (int i = 0; i < 100; i++) {
|
||||||
|
os_join_thread(threads[i]);
|
||||||
|
}
|
||||||
|
print("OK!\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
print("Testing file IO... ");
|
print("Testing file IO... ");
|
||||||
test_file_io();
|
test_file_io();
|
||||||
|
@ -1057,4 +1108,8 @@ void oogabooga_run_tests() {
|
||||||
test_simd();
|
test_simd();
|
||||||
print("OK!\n");
|
print("OK!\n");
|
||||||
|
|
||||||
|
print("Testing hash table... ");
|
||||||
|
test_hash_table();
|
||||||
|
print("OK!\n");
|
||||||
|
|
||||||
}
|
}
|
Reference in a new issue