From 737062b339806c24f45a1ef5920545fca05d0a80 Mon Sep 17 00:00:00 2001 From: Musa Mahmood Date: Mon, 1 Dec 2025 15:00:41 -0500 Subject: [PATCH] Add Hash_Table implementation. --- exe_main.cpp | 8 +- lib/Base/Allocator.h | 2 - lib/Base/Arena_Array.h | 2 +- lib/Base/Arena_Hash_Table.h | 283 ++++++++++++++++++++++ lib/Base/Array.h | 10 +- lib/Base/General_Purpose_Allocator.cpp | 2 - lib/Base/Hash_Functions.h | 70 ++++++ lib/Base/Hash_Table.h | 320 +++++++++++++++++++++++++ lib/Base/run_tests.cpp | 74 ++++++ lib_main.cpp | 7 +- src/Base_Entry_Point.cpp | 59 +---- 11 files changed, 777 insertions(+), 60 deletions(-) create mode 100644 lib/Base/Arena_Hash_Table.h create mode 100644 lib/Base/Hash_Functions.h create mode 100644 lib/Base/Hash_Table.h create mode 100644 lib/Base/run_tests.cpp diff --git a/exe_main.cpp b/exe_main.cpp index 1f4b221..a1e17e0 100644 --- a/exe_main.cpp +++ b/exe_main.cpp @@ -12,8 +12,12 @@ // #include "lib/third_party/dear-imgui/imgui_impl_win32.h" // #include "lib/third_party/dear-imgui/imgui_impl_dx11.h" -#pragma comment(lib, "d3d11") -#pragma comment(lib, "d3dcompiler") +// #pragma comment(lib, "d3d11") +// #pragma comment(lib, "d3dcompiler") +#define BASE_RUN_TESTS 1 +#if BASE_RUN_TESTS + #include "lib/Base/run_tests.cpp" +#endif #include "src/Base_Entry_Point.cpp" diff --git a/lib/Base/Allocator.h b/lib/Base/Allocator.h index cc43e58..83e1a01 100644 --- a/lib/Base/Allocator.h +++ b/lib/Base/Allocator.h @@ -1,7 +1,5 @@ #pragma once -#include "Base.h" - #define ALLOCATOR_DEBUG_MODE 1 #define ALLOCATOR_POISON_MEMORY_ON_ALLOCATION \ (BUILD_DEBUG && ALLOCATOR_DEBUG_MODE) diff --git a/lib/Base/Arena_Array.h b/lib/Base/Arena_Array.h index 3b41b8d..794c766 100644 --- a/lib/Base/Arena_Array.h +++ b/lib/Base/Arena_Array.h @@ -47,7 +47,7 @@ ArenaArray* arena_array_new (s64 preallocate_count, Arena_Reserve reserve_siz } template T* array_start (ArenaArray& array) { - return (array.arena->memory_base + ARRAY_ARENA_START_OFFSET); + return (T*)(array.arena->memory_base + ARRAY_ARENA_START_OFFSET); } template bool is_empty (ArenaArray& array) { diff --git a/lib/Base/Arena_Hash_Table.h b/lib/Base/Arena_Hash_Table.h new file mode 100644 index 0000000..6bf8301 --- /dev/null +++ b/lib/Base/Arena_Hash_Table.h @@ -0,0 +1,283 @@ +// Should ArenaTable be bootstrapped like String_Builder? +// see: new_string_builder +template +struct ArenaTable { + using KeyType = T; using ValueType = U; + ArenaArray>* entries = {}; + s64 count = 0; + s64 slots_filled = 0; + + Hash_Function hash_function = nullptr; + Hash_Compare_Function compare_function = nullptr; + + s64 add_collisions = 0; + s64 find_collisions = 0; + + u32 load_factor_percent = 70; + + bool refill_removed = true; + bool count_collisions = false; +}; + +template bool table_is_valid (ArenaTable* table) { + if (table == nullptr) return false; + if (table->entries == nullptr) return false; + if (table->entries->allocated == 0) return false; + + if (table->hash_function == nullptr) return false; + if (table->compare_function == nullptr) return false; + + return true; +} + +template void table_init (ArenaTable* table, s64 slots_to_allocate=64, Arena_Reserve new_reserve=Arena_Reserve::Size_64M) { + s64 n = Next_Power_Of_Two(slots_to_allocate); + + table->entries = arena_array_new> (n, new_reserve); + array_resize(*table->entries, n, false); // don't init + + for (s64 i = 0; i < n; i += 1) { + (*table->entries)[i].hash = 0; + } + // default hash and compare functions: + table->hash_function = table_hash_function_fnv1a; + table->compare_function = keys_match_u64; +} + +// Adds given key value pair to the table, returns a pointer to the inserted value. +template U* table_add (ArenaTable* table, T key, U value) { + Assert(table_is_valid(table)); + Assert(table->load_factor_percent < 100); + + if ( ((table->slots_filled + 1) * 100) >= (table->entries->allocated * table->load_factor_percent) ) { + table_resize(table, Next_Power_Of_Two(table->entries->allocated + 64)); + } + Assert(table->slots_filled < table->entries->allocated); + + // #Walk_Table + u32 mask = (u32)(table->entries->allocated - 1); + + u32 hash = table->hash_function(&key, sizeof(T)); + + if (hash < HASH_TABLE_FIRST_VALID_HASH) { + hash += HASH_TABLE_FIRST_VALID_HASH; + } + + u32 index = hash & mask; + + u32 probe_increment = 1; + u32 table_while_loop = (*table->entries)[index].hash; + while (table_while_loop) { + if (table->refill_removed) { + if ((*table->entries)[index].hash == HASH_TABLE_REMOVED_HASH) { + table->slots_filled -= 1; // 1 will get re-added below, for total increment 0. + break; + } + } + + if (table->count_collisions) { + table->add_collisions += 1; + } + + index = (index + probe_increment) & mask; + probe_increment += 1; + + table_while_loop = (*table->entries)[index].hash; + } + + // Walk_Table walked us to an unused entry, so add our new data into this slot: + table->count += 1; + table->slots_filled += 1; + + Table_Entry* entry = &(*table->entries)[index]; + entry->hash = hash; + entry->key = key; + entry->value = value; + + return &entry->value; +} + +template U* table_find_pointer (ArenaTable* table, T key) { + Assert(table_is_valid(table)); + if (!table_is_valid(table)) return nullptr; + + // #Walk_Table + u32 mask = (u32)(table->entries->allocated - 1); + + u32 hash = table->hash_function(&key, sizeof(T)); + + if (hash < HASH_TABLE_FIRST_VALID_HASH) { + hash += HASH_TABLE_FIRST_VALID_HASH; + } + + u32 index = hash & mask; + + u32 probe_increment = 1; + u32 table_while_loop = (*table->entries)[index].hash; + while (table_while_loop) { + Table_Entry* entry = &(*table->entries)[index]; + if (entry->hash == hash) { + if (table->compare_function(&entry->key, &key)) { + return &entry->value; + } + } + + if (table->count_collisions) { table->find_collisions += 1; } + + index = (index + probe_increment) & mask; + probe_increment += 1; + + table_while_loop = (*table->entries)[index].hash; + } + + return nullptr; +} + +template U* table_set (ArenaTable* table, T key, U value) { + U* value_ptr = table_find_pointer(table, key); + if (value_ptr) { + (*value_ptr) = value; + return value_ptr; + } else { + return table_add(table, key, value); + } +} + +template void table_resize (ArenaTable* table, s64 slots_to_allocate) { + s64 initial_count = table->entries->count; + Assert(slots_to_allocate > initial_count); + if (slots_to_allocate <= initial_count) { + return; + } + + // #TODO: When you resize you need to reinsert all the values, so we + // need a temporary copy of the original data: + ArrayView> old_entries = array_copy(temp(), to_view(*table->entries)); + + s64 n = Next_Power_Of_Two(slots_to_allocate); + + array_resize(*table->entries, n, false); + + table->count = 0; + table->slots_filled = 0; + + // Initialize new values: + for (s64 i = 0; i < old_entries.count; i += 1) { + Table_Entry entry = old_entries[i]; + // if entry is valid! + if (entry.hash > HASH_TABLE_FIRST_VALID_HASH) { + table_add(table, entry.key, entry.value); + } + } +} + +template void table_reset (ArenaTable* table, bool keep_memory=true) { + table->count = 0; + table->slots_filled = 0; + + for (s64 i = 0; i < table->entries->count; i += 1) { + (*table->entries)[i].hash = 0; + } + + if (!keep_memory) { array_reset(*table->entries); } +} + +template void table_release (ArenaTable* table) { + arena_array_free(*table->entries); + +#if BUILD_DEBUG + poison_struct(table); +#endif +} + + +template bool table_contains (ArenaTable* table, T key) { + return (table_find_pointer(table, key) != nullptr); +} + +template bool table_find (ArenaTable* table, T key, U* value) { + U* pointer = table_find_pointer(table, key); + if (pointer) { + (*value) = (*pointer); + return true; + } + + return false; +} + +template bool table_remove (ArenaTable* table, T key, U* value) { + Assert(table_is_valid(table)); + if (!table_is_valid(table)) return nullptr; + + // #Walk_Table + u32 mask = (u32)(table->entries->allocated - 1); + + u32 hash = table->hash_function(&key, sizeof(T)); + + if (hash < HASH_TABLE_FIRST_VALID_HASH) { + hash += HASH_TABLE_FIRST_VALID_HASH; + } + + u32 index = hash & mask; + + u32 probe_increment = 1; + u32 table_while_loop = (*table->entries)[index].hash; + while (table_while_loop) { + Table_Entry* entry = &(*table->entries)[index]; + + if ((entry->hash == hash) && table->compare_function(&entry->key, &key)) { + entry->hash = HASH_TABLE_REMOVED_HASH; + table->count -= 1; + (*value) = entry->value; + return true; + } + + index = (index + probe_increment) & mask; + probe_increment += 1; + + table_while_loop = (*table->entries)[index].hash; + } + + return false; +} + +// #TODO: we need a for expansion iterator? +// table_find_multiple (put results in Temp-backed Array<>, and return it as an ArrayView) { +template ArrayView table_remove (ArenaTable* table, T key, U* value) { + Array results; + results.allocator = temp(); + + // #Walk_Table + u32 mask = (u32)(table->entries->allocated - 1); + + u32 hash = table->hash_function(&key, sizeof(T)); + + if (hash < HASH_TABLE_FIRST_VALID_HASH) { + hash += HASH_TABLE_FIRST_VALID_HASH; + } + + u32 index = hash & mask; + + u32 probe_increment = 1; + u32 table_while_loop = (*table->entries)[index].hash; + while (table_while_loop) { + Table_Entry* entry = &(*table->entries)[index]; + + if (entry->hash == hash) { + if (table->compare_function(&entry->key, &key)) { + array_add(results, entry->value); + } else { + if (table->count_collisions) { table->find_collisions += 1; } + } + } else { + if (table->count_collisions) { table->find_collisions += 1; } + } + + index = (index + probe_increment) & mask; + probe_increment += 1; + + table_while_loop = (*table->entries)[index].hash; + } + + return to_view(results); +} diff --git a/lib/Base/Array.h b/lib/Base/Array.h index ea85394..be9ea18 100644 --- a/lib/Base/Array.h +++ b/lib/Base/Array.h @@ -300,7 +300,7 @@ bool is_valid (ArrayView src) { // can also use ArrayView(count, data) for initialization! template -ArrayView array_view (Array array) { +ArrayView to_view (Array array) { ArrayView av; av.count = array.count; av.data = array.data; @@ -343,6 +343,12 @@ ArrayView array_copy (const ArrayView& src) { return ArrayView(src.count, (T*)new_data); } +template +ArrayView array_copy (Allocator allocator, const ArrayView& src) { + push_allocator(allocator); + return array_copy(src); +} + template void array_free (ArrayView& src) { if (!src.data || src.count == 0) { return; } @@ -380,4 +386,4 @@ ArrayView array_view_from_values (ArgValues... args) { return array; } -MSVC_RUNTIME_CHECKS_RESTORE \ No newline at end of file +MSVC_RUNTIME_CHECKS_RESTORE diff --git a/lib/Base/General_Purpose_Allocator.cpp b/lib/Base/General_Purpose_Allocator.cpp index 30bbe0c..67cf795 100644 --- a/lib/Base/General_Purpose_Allocator.cpp +++ b/lib/Base/General_Purpose_Allocator.cpp @@ -1,5 +1,3 @@ -#include - #if GP_ALLOCATOR_TRACK_ALLOCATIONS #include // #TODO: Replace with Mutex (see OS_Win32.cpp) global General_Allocator gAllocator; // @Shared diff --git a/lib/Base/Hash_Functions.h b/lib/Base/Hash_Functions.h new file mode 100644 index 0000000..0d8801b --- /dev/null +++ b/lib/Base/Hash_Functions.h @@ -0,0 +1,70 @@ +constexpr u32 HASH_INIT = 5381; +u32 sdbm_hash (void* data, s64 size) { + u32 h = HASH_INIT; + for (s64 i = 0; i < size; i += 1) { + h = (h << 16) + (h << 6) - h + ((u8*)data)[i]; + } + + return (u32)h; +} + +u64 knuth_hash (u64 x) { + constexpr u64 KNUTH_GOLDEN_RATIO_64 = 11400714819323198485ULL; + + return (KNUTH_GOLDEN_RATIO_64 * x); +} + +u32 knuth_hash_u32 (u64 x) { + u32 h = HASH_INIT; + + return (u32)((knuth_hash(x) ^ h) >> 32); +} + +constexpr u64 FNV_64_PRIME = 0x100000001b3ULL; +constexpr u64 FNV_64_OFFSET_BIAS = 0xcbf29ce484222325ULL; + +u64 fnv1a_hash (u64 x, u64 h = FNV_64_OFFSET_BIAS) { + h ^= x; + + return h * FNV_64_PRIME; +} + +// Good for hashing strings. +u64 fnv1a_hash_any (void* data, s64 size, u64 h = FNV_64_OFFSET_BIAS) { + for (s64 i = 0; i < size; i += 1) { + h = fnv1a_hash( ((u8*)data)[i], h); + } + + return h; +} + +u32 table_hash_function_fnv1a (void* key, s64 size) { + return (u32)fnv1a_hash_any(key, size); +} + +u32 table_hash_function_knuth (void* key, s64 size) { + Assert(size == 8); + + return knuth_hash_u32(*(u64*)key); +} + +u32 string_hash_function_fnv1a (void* key, s64 size) { + Assert(size == sizeof(string)); + string key_as_string = *((string*)key); + + return (u32)(fnv1a_hash_any(key_as_string.data, key_as_string.count)); +} + +bool keys_match_u64 (void* key1, void* key2) { + u64 key1_u64 = *(u64*)key1; + u64 key2_u64 = *(u64*)key2; + + return key1_u64 == key2_u64; +} + +bool string_keys_match_u64 (void* key1, void* key2) { + string key1_s = *((string*)key1); + string key2_s = *((string*)key2); + + return strings_match(key1_s, key2_s); +} \ No newline at end of file diff --git a/lib/Base/Hash_Table.h b/lib/Base/Hash_Table.h new file mode 100644 index 0000000..1852fda --- /dev/null +++ b/lib/Base/Hash_Table.h @@ -0,0 +1,320 @@ +// #NOTE: This Hash Table is borrowed from Jai's implementation! (With some tweaks) +// I made my own version that's arena-backed, but the mechanisms are the same. + +typedef u32 hash_result; + +constexpr hash_result HASH_TABLE_FIRST_VALID_HASH = 2; +constexpr hash_result HASH_TABLE_REMOVED_HASH = 1; + +typedef hash_result (*Hash_Function)(void* key, s64 size); +typedef bool (*Hash_Compare_Function)(void* key1, void* key2); + +template +struct Table_Entry { + using KeyType = T; using ValueType = U; + T key; + U value; + hash_result hash; +}; + +template +struct Table { + using KeyType = T; using ValueType = U; + Allocator allocator = {}; + ArrayView> entries = {}; + s64 allocated = 0; + s64 count = 0; + s64 slots_filled = 0; + + Hash_Function hash_function = nullptr; + Hash_Compare_Function compare_function = nullptr; + + u32 load_factor_percent = 70; + + s64 add_collisions = 0; + s64 find_collisions = 0; + + bool refill_removed = true; + bool count_collisions = false; +}; + +template bool table_is_valid (Table* table) { + if (table == nullptr) return false; + if (table->entries == nullptr) return false; + if (table->entries->allocated == 0) return false; + + if (table->hash_function == nullptr) return false; + if (table->compare_function == nullptr) return false; + + return true; +} + +template void table_init (Table* table, s64 slots_to_allocate=64, Arena_Reserve new_reserve=Arena_Reserve::Size_64M) { + if (table->allocator->proc == nullptr) { + table->allocator = context_allocator(); // #remember_allocator + } + push_allocator(table->allocator); + + s64 n = Next_Power_Of_Two(slots_to_allocate); + + table->entries = ArrayView>(n, false); + table->allocated = n; + + for (s64 i = 0; i < n; i += 1) { + table->entries[i].hash = 0; + } + // default hash and compare functions: + table->hash_function = table_hash_function_fnv1a; + table->compare_function = keys_match_u64; +} + +// Adds given key value pair to the table, returns a pointer to the inserted value. +template U* table_add (Table* table, T key, U value) { + Assert(table_is_valid(table)); + Assert(table->load_factor_percent < 100); + + if ( ((table->slots_filled + 1) * 100) >= (table->entries->allocated * table->load_factor_percent) ) { + table_resize(table, Next_Power_Of_Two(table->entries->allocated + 64)); + } + Assert(table->slots_filled < table->entries->allocated); + + // #Walk_Table + u32 mask = (u32)(table->entries->allocated - 1); + + u32 hash = table->hash_function(&key, sizeof(T)); + + if (hash < HASH_TABLE_FIRST_VALID_HASH) { + hash += HASH_TABLE_FIRST_VALID_HASH; + } + + u32 index = hash & mask; + + u32 probe_increment = 1; + u32 table_while_loop = table->entries[index].hash; + while (table_while_loop) { + if (table->refill_removed) { + if (table->entries[index].hash == HASH_TABLE_REMOVED_HASH) { + table->slots_filled -= 1; // 1 will get re-added below, for total increment 0. + break; + } + } + + if (table->count_collisions) { + table->add_collisions += 1; + } + + index = (index + probe_increment) & mask; + probe_increment += 1; + + table_while_loop = table->entries[index].hash; + } + + // Walk_Table walked us to an unused entry, so add our new data into this slot: + table->count += 1; + table->slots_filled += 1; + + Table_Entry* entry = &table->entries[index]; + entry->hash = hash; + entry->key = key; + entry->value = value; + + return &entry->value; +} + +template U* table_find_pointer (Table* table, T key) { + Assert(table_is_valid(table)); + if (!table_is_valid(table)) return nullptr; + + // #Walk_Table + u32 mask = (u32)(table->entries->allocated - 1); + + u32 hash = table->hash_function(&key, sizeof(T)); + + if (hash < HASH_TABLE_FIRST_VALID_HASH) { + hash += HASH_TABLE_FIRST_VALID_HASH; + } + + u32 index = hash & mask; + + u32 probe_increment = 1; + u32 table_while_loop = table->entries[index].hash; + while (table_while_loop) { + Table_Entry* entry = &table->entries[index]; + if (entry->hash == hash) { + if (table->compare_function(&entry->key, &key)) { + return &entry.value + } + } + + if (table->count_collisions) { table->find_collisions += 1; } + + index = (index + probe_increment) & mask; + probe_increment += 1; + + table_while_loop = table->entries[index].hash; + } + + return nullptr +} + +template U* table_set (Table* table, T key, U value) { + U* value_ptr = table_find_pointer(table, key); + if (value_ptr) { + (*value_ptr) = value; + return value_ptr; + } else { + return table_add(table, key, value); + } +} + +template void table_resize (Table* table, s64 slots_to_allocate) { + s64 initial_count = table->entries->count; + Assert(slots_to_allocate > initial_count); + if (slots_to_allocate <= initial_count) { + return; + } + + // #TODO: When you resize you need to reinsert all the values, so we + // need a temporary copy of the original data: + ArrayView> old_entries = table->entries; + + s64 n = Next_Power_Of_Two(slots_to_allocate); + + table->entries = ArrayView>(n, false); + table->allocated = n; + + table->count = 0; + table->slots_filled = 0; + + // Initialize new values: + for (s64 i = 0; i < old_entries.count; i += 1) { + Table_Entry entry = old_entries[i]; + // if entry is valid! + if (entry.hash > HASH_TABLE_FIRST_VALID_HASH) { + table_add(table, entry.key, entry.value); + } + } +} + +template void table_reset (Table* table, bool keep_memory=true) { + table->count = 0; + table->slots_filled = 0; + + for (s64 i = 0; i < table->entries->count; i += 1) { + table->entries[i].hash = 0; + } + + if (!keep_memory) { array_reset(table->entries); } +} + +template void table_release (Table* table) { + arena_array_free(table->entries); + +#if BUILD_DEBUG + poison_struct(table); +#endif +} + + +template bool table_contains (Table* table, T key) { + return (table_find_pointer(table, key) != nullptr); +} + +template bool table_find (Table* table, T key, U* value) { + U* pointer = table_find_pointer(table, key); + if (pointer) { + (*value) = (*pointer); + return true; + } + + return false; +} + +template bool table_remove (Table* table, T key, U* value) { + Assert(table_is_valid(table)); + if (!table_is_valid(table)) return nullptr; + + // #Walk_Table + u32 mask = (u32)(table->entries->allocated - 1); + + u32 hash = table->hash_function(&key, sizeof(T)); + + if (hash < HASH_TABLE_FIRST_VALID_HASH) { + hash += HASH_TABLE_FIRST_VALID_HASH; + } + + u32 index = hash & mask; + + u32 probe_increment = 1; + u32 table_while_loop = table->entries[index].hash; + while (table_while_loop) { + Table_Entry* entry = &table->entries[index]; + + if ((entry->hash == hash) && table->compare_function(&entry->key, &key)) { + entry->hash = HASH_TABLE_REMOVED_HASH; + table->count -= 1; + (*value) = entry->value; + return true; + } + + index = (index + probe_increment) & mask; + probe_increment += 1; + + table_while_loop = table->entries[index].hash; + } + + return false; +} + +// #TODO: we need a for expansion iterator? +// table_find_multiple (put results in Temp-backed Array<>, and return it as an ArrayView) { +template ArrayView table_remove (Table* table, T key, U* value) { + Array results; + results.allocator = temp(); + + // #Walk_Table + u32 mask = (u32)(table->entries->allocated - 1); + + u32 hash = table->hash_function(&key, sizeof(T)); + + if (hash < HASH_TABLE_FIRST_VALID_HASH) { + hash += HASH_TABLE_FIRST_VALID_HASH; + } + + u32 index = hash & mask; + + u32 probe_increment = 1; + u32 table_while_loop = table->entries[index].hash; + while (table_while_loop) { + Table_Entry* entry = &table->entries[index]; + + if (entry->hash == hash) { + if (table->compare_function(&entry->key, &key)) { + array_add(results, entry->value); + } else { + if (table->count_collisions) { table->find_collisions += 1; } + } + } else { + if (table->count_collisions) { table->find_collisions += 1; } + } + + index = (index + probe_increment) & mask; + probe_increment += 1; + + table_while_loop = table->entries[index].hash; + } + + return to_view(results); +} + +// #TODO: +// find_or_add is kind of like table_set, but used when you +// just want a pointer to the value, which you can fill in. +// find_or_add :: (table: *Table, key: table.Key_Type) -> (entry: *table.Value_Type, newly_added: bool) { +// value := table_find_pointer(table, key); +// if value return value, false; + +// new_value: table.Value_Type; +// value = table_add(table, key, new_value); +// return value, true; +// } diff --git a/lib/Base/run_tests.cpp b/lib/Base/run_tests.cpp new file mode 100644 index 0000000..4ed6f4f --- /dev/null +++ b/lib/Base/run_tests.cpp @@ -0,0 +1,74 @@ +void run_pre_setup_tests() { + printf("Running pre-setup tests...\n"); + printf("\nFinished running pre-setup tests...\n"); +} + +string string_literal_example = "Hello, I am a string literal."; +void run_post_setup_tests() { + printf("Running post-setup tests...\n"); + // See: main_thread_base_entry_point + { Timed_Block_Print("string_builder_testing"); + temp_reset(); + push_allocator(temp()); + // tip: use auto_reset or auto_release with `thread_context()->arena` + + // String builder example: + // OK. I can work with this. + + auto sb = new_string_builder(Arena_Reserve::Size_64K); + append(sb, "string_literal_example"); + append(sb, " "); + print_to_builder(sb, "There are %d cats in the %s", 64, "house.\n"); + append(sb, " > "); + print_to_builder(sb, "some size_t: %u", (u64)3982739867); + append(sb, "\n"); + print_to_builder(sb, "the literal: %s", string_literal_example.data); + + // string result = string_view(sb); + string result = builder_to_string(sb); // also frees + // print((char*)result.data); + log("Hello."); + log("log() should automatically append newlines to these prints."); + log("If it doesn't, I will be very upset."); + log(result); + log("Hello. There are %s things here\n", string_literal_example.data); + + print("Hello, I am just a printed message to stdout\n\n"); + + + // Testing file writes and reads: + print("Writing some nonsense to a file.\n"); + bool success = write_entire_file("D:/TempLocal/junk.txt", to_view(result)); + log("Done. Success: %d\n", success); + + // push_allocator(allocator(thread_context()->arena)); + push_allocator(GPAllocator()); + string file_path = "D:/Work/OpenBCI/ToolZ/prototyping-gui-main/modules/native-proto-lib/native-sdk-prototyping/src/SignalProcessing.cpp"; + ArrayView file_data = read_entire_file(file_path, true); + log("file_data: \n"); + log("%s\n", file_data.data); + } + // ImGui_Application(); + { // Test hashing: + u64 start = 0x512585; + u32 sdbm = sdbm_hash(&start, sizeof(u64)); + u64 kh = knuth_hash(start); + u64 fnv = fnv1a_hash(start); + + string some_data = "Hello there, my name is Musa"; + u64 result = fnv1a_hash_any(some_data.data, some_data.count); + } + + { + ArenaTable table; + table_init(&table); + table_resize(&table, 2048); + table_add(&table, (u64)52975125, (u64)5); + table_set(&table, (u64)52975125, (u64)99); + + auto ptr = table_find_pointer(&table, (u64)52975125); + printf("ptr.* = %llu\n", *ptr); + } + + printf("\nFinished running post-setup tests...\n"); +} \ No newline at end of file diff --git a/lib_main.cpp b/lib_main.cpp index 9ad88cd..31bf6ba 100644 --- a/lib_main.cpp +++ b/lib_main.cpp @@ -11,11 +11,12 @@ #include "lib/Base/Allocator.h" #include "lib/Base/Array.h" #include "lib/Base/General_Purpose_Allocator.h" - - #include "lib/Base/Arena.h" #include "lib/Base/Arena_Array.h" #include "lib/Base/String.h" +#include "lib/Base/Hash_Functions.h" +#include "lib/Base/Hash_Table.h" +#include "lib/Base/Arena_Hash_Table.h" #if OS_WINDOWS # include "lib/OS/OS_Win32.h" #endif @@ -48,8 +49,6 @@ #include "lib/Base/Thread_Group.cpp" -// #include "imgui-docking.cpp" - // #if OS_LINUX.. // #include "src/OS_Linux.cpp" // #if OS_MACOS.. diff --git a/src/Base_Entry_Point.cpp b/src/Base_Entry_Point.cpp index b8e3976..c9f212a 100644 --- a/src/Base_Entry_Point.cpp +++ b/src/Base_Entry_Point.cpp @@ -181,62 +181,27 @@ void ImGui_Application () { ::UnregisterClassW(wc.lpszClassName, wc.hInstance); }*/ -string string_literal_example = "Hello, I am a string literal."; - internal void Main_Entry_Point (int argc, WCHAR **argv) { - set_cpu_base_frequency(3200); // REQUIRED FOR TIMING MODULE! + set_cpu_base_frequency(3200); // REQUIRED FOR TIMING MODULE! will depend on CPU + +#if BASE_RUN_TESTS + run_pre_setup_tests(); // #no_context +#endif // #NOTE: Be careful using a timing or auto-release macros // before setting up the thread context! Bootstrap_Main_Thread_Context(); #if OS_WINDOWS - Win32_Entry_Point(argc, argv); // This might be the problem. + Win32_Entry_Point(argc, argv); #endif #if OS_LINUX // Linux_Entry_Point(argc, argv); #endif - // See: main_thread_base_entry_point - { Timed_Block_Print("string_builder_testing"); - temp_reset(); - push_allocator(temp()); - // tip: use auto_reset or auto_release with `thread_context()->arena` - - // String builder example: - // OK. I can work with this. - - auto sb = new_string_builder(Arena_Reserve::Size_64K); - append(sb, "string_literal_example"); - append(sb, " "); - print_to_builder(sb, "There are %d cats in the %s", 64, "house.\n"); - append(sb, " > "); - print_to_builder(sb, "some size_t: %u", (u64)3982739867); - append(sb, "\n"); - print_to_builder(sb, "the literal: %s", string_literal_example.data); - - // string result = string_view(sb); - string result = builder_to_string(sb); // also frees - // print((char*)result.data); - log("Hello."); - log("log() should automatically append newlines to these prints."); - log("If it doesn't, I will be very upset."); - log(result); - log("Hello. There are %s things here\n", string_literal_example.data); - - print("Hello, I am just a printed message to stdout\n\n"); - - - // Testing file writes and reads: - print("Writing some nonsense to a file.\n"); - bool success = write_entire_file("D:/TempLocal/junk.txt", to_view(result)); - log("Done. Success: %d\n", success); - - // push_allocator(allocator(thread_context()->arena)); - push_allocator(GPAllocator()); - string file_path = "D:/Work/OpenBCI/ToolZ/prototyping-gui-main/modules/native-proto-lib/native-sdk-prototyping/src/SignalProcessing.cpp"; - ArrayView file_data = read_entire_file(file_path, true); - log("file_data: \n"); - log("%s\n", file_data.data); - } - // ImGui_Application(); +#if BASE_RUN_TESTS + run_post_setup_tests(); +#endif + + // 2. Setup Window + // 3. Initialize Graphics Backend (DX11 or OpenGL3) // #TODO: #Main - `Main_Entry_Point` // [ ] Setup Mouse and Keyboard Inputs