/// /// Most of these are generated by gpt so there might be some goofyness /// void log_heap() { spinlock_acquire_or_wait(&heap_lock); print("\nHEAP:\n"); Heap_Block *block = heap_head; while (block != 0) { print("\tBLOCK @ 0x%I64x, %llu bytes\n", (u64)block, block->size); Heap_Free_Node *node = block->free_head; u64 total_free = 0; while (node != 0) { print("\t\tFREE NODE @ 0x%I64x, %llu bytes\n", (u64)node, node->size); total_free += node->size; node = node->next; } print("\t TOTAL FREE: %llu\n\n", total_free); block = block->next; } spinlock_release(&heap_lock); } void test_allocator(bool do_log_heap) { u64 h = get_hash((string*)69); Allocator heap = get_heap_allocator(); // Basic allocation and free int* a = (int*)alloc(heap, sizeof(int)); int* b = (int*)alloc(heap, sizeof(int)); int* c = (int*)alloc(heap, sizeof(int)); *a = 69; *b = 420; *c = 1337; assert(*a == 69, "Test failed: Memory corrupted"); assert(*b == 420, "Test failed: Memory corrupted"); assert(*c == 1337, "Test failed: Memory corrupted"); // Test growing memory //os_grow_program_memory(1024 * 1024 * 1000); assert(*a == 69, "Test failed: Memory corrupted"); assert(*b == 420, "Test failed: Memory corrupted"); assert(*c == 1337, "Test failed: Memory corrupted"); u8 check_bytes[1024]; for (u64 i = 0; i < 1024; i += 4) { *((int*)&check_bytes[i]) = (int)get_random(); } u8 *check_bytes_copy = (u8*)alloc(heap, 1024); memcpy(check_bytes_copy, check_bytes, 1024); // Allocate and free large block //void* large_block = alloc(heap, 1024 * 1024 * 100); //dealloc(heap, large_block); // Allocate multiple small blocks void* blocks[100]; for (int i = 0; i < 100; ++i) { blocks[i] = alloc(heap, 128); assert(blocks[i] != NULL, "Failed to allocate small block"); } for (int i = 0; i < 100; ++i) { dealloc(heap, blocks[i]); } // Stress test with various sizes for (int i = 1; i <= 1000; ++i) { void* p = alloc(heap, i * 64); assert(p != NULL, "Failed to allocate varying size block"); dealloc(heap, p); } // Free in reverse order for (int i = 0; i < 100; ++i) { blocks[i] = alloc(heap, 128); assert(blocks[i] != NULL, "Failed to allocate small block"); } for (int i = 99; i >= 0; --i) { dealloc(heap, blocks[i]); } // Test memory integrity with various allocation patterns int* nums[10]; for (int i = 0; i < 10; ++i) { nums[i] = (int*)alloc(heap, sizeof(int) * 10); for (int j = 0; j < 10; ++j) { nums[i][j] = i * 10 + j; } } for (int i = 0; i < 10; ++i) { for (int j = 0; j < 10; ++j) { assert(nums[i][j] == i * 10 + j, "Memory corruption detected"); } dealloc(heap, nums[i]); } reset_temporary_storage(); int* foo = (int*)alloc(get_temporary_allocator(), 72); *foo = 1337; void* bar = alloc(get_temporary_allocator(), 69); (void)bar; void* baz = alloc(get_temporary_allocator(), 420); (void)baz; assert(*foo == 1337, "Temp memory corruptada"); int* old_foo = foo; reset_temporary_storage(); foo = (int*)alloc(get_temporary_allocator(), 72); assert(old_foo == foo, "Temp allocator goof"); // Repeated Allocation and Free for (int i = 0; i < 10000; ++i) { void* temp = alloc(heap, 128); assert(temp != NULL && "Repeated allocation failed"); dealloc(heap, temp); } // Mixed Size Allocations void* mixed_blocks[200]; for (int i = 0; i < 200; ++i) { if (i % 2 == 0) { mixed_blocks[i] = alloc(heap, 128); } else { mixed_blocks[i] = alloc(heap, 1024 * 1024); // 1MB blocks } assert(mixed_blocks[i] != NULL && "Mixed size allocation failed"); } for (int i = 0; i < 200; ++i) { if (i % 2 == 0) { dealloc(heap, mixed_blocks[i]); } } for (int i = 0; i < 200; ++i) { if (i % 2 != 0) { dealloc(heap, mixed_blocks[i]); } } // Fragmentation Stress Test for (int i = 0; i < 50; ++i) { blocks[i] = alloc(heap, 256); assert(blocks[i] != NULL && "Failed to allocate small block for fragmentation test"); } for (int i = 0; i < 50; i += 2) { dealloc(heap, blocks[i]); } for (int i = 50; i < 100; ++i) { blocks[i] = alloc(heap, 128); assert(blocks[i] != NULL && "Failed to allocate small block in fragmented heap"); } for (int i = 50; i < 100; ++i) { dealloc(heap, blocks[i]); } for (int i = 1; i < 50; i += 2) { dealloc(heap, blocks[i]); } assert(bytes_match(check_bytes, check_bytes_copy, 1024), "Memory corrupt"); if (do_log_heap) log_heap(); } void test_thread_proc1(Thread* t) { os_sleep(5); print("Hello from thread %llu\n", t->id); os_sleep(5); print("Hello from thread %llu\n", t->id); os_sleep(5); print("Hello from thread %llu\n", t->id); os_sleep(5); print("Hello from thread %llu\n", t->id); os_sleep(5); print("Hello from thread %llu\n", t->id); } void test_threads() { Thread t; os_thread_init(&t, test_thread_proc1); os_thread_start(&t); os_sleep(20); print("This should be printed in middle of thread execution\n"); os_thread_join(&t); print("Thread is joined\n"); Mutex_Handle m = os_make_mutex(); os_lock_mutex(m); os_unlock_mutex(m); } void test_allocator_threaded(Thread *t) { Allocator heap = get_heap_allocator(); for (int i = 0; i < 1000; ++i) { void* temp = alloc(heap, 128); assert(temp != NULL && "Repeated allocation failed"); dealloc(heap, temp); } void* mixed_blocks[40]; for (int i = 0; i < 40; ++i) { if (i % 2 == 0) { mixed_blocks[i] = alloc(heap, 128); } else { mixed_blocks[i] = alloc(heap, 1024 * 1024); // 1MB blocks } assert(mixed_blocks[i] != NULL && "Mixed size allocation failed"); } for (int i = 0; i < 40; ++i) { if (i % 2 == 0) { dealloc(heap, mixed_blocks[i]); } } for (int i = 0; i < 40; ++i) { if (i % 2 != 0) { dealloc(heap, mixed_blocks[i]); } } } void test_strings() { Allocator heap = get_heap_allocator(); { // Test length_of_null_terminated_string assert(length_of_null_terminated_string("Test") == 4, "Failed: length_of_null_terminated_string"); assert(length_of_null_terminated_string("") == 0, "Failed: length_of_null_terminated_string"); // Test alloc_string and dealloc_string string alloc_str = alloc_string(heap, 10); assert(alloc_str.data != NULL, "Failed: alloc_string"); assert(alloc_str.count == 10, "Failed: alloc_string"); dealloc_string(heap, alloc_str); // Test string_concat string str1 = STR("Hello, "); string str2 = STR("World!"); string concat_str = string_concat(str1, str2, heap); assert(concat_str.count == str1.count + str2.count, "Failed: string_concat"); assert(memcmp(concat_str.data, "Hello, World!", concat_str.count) == 0, "Failed: string_concat"); dealloc_string(heap, concat_str); // Test convert_to_null_terminated_string char* cstr = convert_to_null_terminated_string(str1, heap); assert(strcmp(cstr, "Hello, ") == 0, "Failed: convert_to_null_terminated_string"); dealloc(heap, cstr); // Test temp_convert_to_null_terminated_string cstr = temp_convert_to_null_terminated_string(str2); assert(strcmp(cstr, "World!") == 0, "Failed: temp_convert_to_null_terminated_string"); // Test sprint string format_str = STR("Number: %d"); string formatted_str = sprint(heap, format_str, 42); char* formatted_cstr = convert_to_null_terminated_string(formatted_str, heap); assert(strcmp(formatted_cstr, "Number: 42") == 0, "Failed: sprint"); dealloc(heap, formatted_str.data); dealloc(heap, formatted_cstr); // Test tprint string temp_formatted_str = tprint(format_str, 100); formatted_cstr = temp_convert_to_null_terminated_string(temp_formatted_str); assert(strcmp(formatted_cstr, "Number: 100") == 0, "Failed: tprint"); // Test print and printf (visual inspection) print("Expected output: Hello, World!\n"); print("Hello, %s!\n", STR("World")); print("Expected output: Number: 1234\n"); print("Number: %d\n", 1234); print("Expected output: Number: 1234\n"); print("Number: %d\n", 1234); print("Expected output: Mixed values: 42 and 3.14\n"); print("Mixed values: %d and %.2f\n", 42, 3.14); // This should fail assert and print descriptive error //print("Expected output (printf): Hello, World!\n"); //print("Hello, %cs!\n", 5); // This should fail assert and print descriptive error // print("Expected output (printf): Hello, World!\n"); // print("Hello, %s!\n", "World"); print("Expected output (printf): Hello, World!\n"); print("Hello, %s!\n", STR("World")); print("Expected output (printf): Number: 5678\n"); print("Number: %d\n", 5678); print("Expected output (printf): Mixed values: 99 and 2.71\n"); print("Mixed values: %d and %.2f\n", 99, 2.71); // Test handling of empty strings string empty_str = STR(""); string concat_empty_str = string_concat(empty_str, empty_str, heap); assert(concat_empty_str.count == 0 && !concat_empty_str.data, "Failed: string_concat with empty strings"); // Test very large strings (performance test) string large_str1 = alloc_string(heap, 1024 * 1024); string large_str2 = alloc_string(heap, 1024 * 1024); string large_concat_str = string_concat(large_str1, large_str2, heap); assert(large_concat_str.count == 2 * 1024 * 1024, "Failed: large string_concat"); dealloc_string(heap, large_str1); dealloc_string(heap, large_str2); dealloc_string(heap, large_concat_str); // Test string with special characters string special_char_str = STR("Special chars: \n\t\r"); cstr = convert_to_null_terminated_string(special_char_str, heap); assert(strcmp(cstr, "Special chars: \n\t\r") == 0, "Failed: special character string"); dealloc(heap, cstr); string a = tprint("Hello, %cs!\n", "balls"); string balls1 = string_view(a, 7, 5); string balls2 = STR("balls"); assert(strings_match(balls1, balls2), "String match failed"); assert(!strings_match(balls1, a), "String match failed"); } // Test string_builder_init String_Builder builder; string_builder_init(&builder, heap); assert(builder.buffer != NULL, "Failed: string_builder_init"); assert(builder.buffer_capacity >= 128, "Failed: string_builder_init"); assert(builder.count == 0, "Failed: string_builder_init"); // Test string_builder_reserve string_builder_reserve(&builder, 256); assert(builder.buffer_capacity >= 256, "Failed: string_builder_reserve"); // Test string_builder_append string str1 = STR("Hello, "); string_builder_append(&builder, str1); assert(builder.count == str1.count, "Failed: string_builder_append"); assert(memcmp(builder.buffer, str1.data, str1.count) == 0, "Failed: string_builder_append"); string str2 = STR("World!"); string_builder_append(&builder, str2); assert(builder.count == str1.count + str2.count, "Failed: string_builder_append"); assert(memcmp(builder.buffer, "Hello, World!", builder.count) == 0, "Failed: string_builder_append"); // Test string_builder_prints string format_str = STR(" Number: %d"); string_builder_prints(&builder, format_str, 42); char* expected_result = "Hello, World! Number: 42"; assert(builder.count == strlen(expected_result), "Failed: string_builder_prints"); assert(memcmp(builder.buffer, expected_result, builder.count) == 0, "Failed: string_builder_prints"); // Test string_builder_printf string_builder_printf(&builder, " And a float: %.2f", 3.14); expected_result = "Hello, World! Number: 42 And a float: 3.14"; assert(builder.count == strlen(expected_result), "Failed: string_builder_printf"); assert(memcmp(builder.buffer, expected_result, builder.count) == 0, "Failed: string_builder_printf"); // Test string_builder_get_string string result_str = string_builder_get_string(builder); assert(result_str.count == builder.count, "Failed: string_builder_get_string"); assert(memcmp(result_str.data, builder.buffer, result_str.count) == 0, "Failed: string_builder_get_string"); // Cleanup dealloc(heap, builder.buffer); // Test handling of empty builder String_Builder empty_builder; string_builder_init(&empty_builder, heap); result_str = string_builder_get_string(empty_builder); assert(result_str.count == 0, "Failed: empty builder handling"); dealloc(heap, empty_builder.buffer); // Test appending large strings (performance test) String_Builder large_builder; string_builder_init(&large_builder, heap); string large_str = alloc_string(heap, 1024 * 1024); memset(large_str.data, 'A', 1024 * 1024); string_builder_append(&large_builder, large_str); assert(large_builder.count == 1024 * 1024, "Failed: large string_builder_append"); dealloc_string(heap, large_str); dealloc(heap, large_builder.buffer); // Test appending special characters String_Builder special_char_builder; string_builder_init(&special_char_builder, heap); string special_char_str = STR("Special chars: \n\t\r"); string_builder_append(&special_char_builder, special_char_str); assert(special_char_builder.count == special_char_str.count, "Failed: special character append"); assert(memcmp(special_char_builder.buffer, special_char_str.data, special_char_str.count) == 0, "Failed: special character append"); dealloc(heap, special_char_builder.buffer); // Test multiple appends String_Builder multi_append_builder; string_builder_init(&multi_append_builder, heap); string str_a = STR("First part"); string str_b = STR(" and second part"); string str_c = STR(" and third part."); string_builder_append(&multi_append_builder, str_a); string_builder_append(&multi_append_builder, str_b); string_builder_append(&multi_append_builder, str_c); expected_result = "First part and second part and third part."; assert(multi_append_builder.count == strlen(expected_result), "Failed: multiple appends"); assert(memcmp(multi_append_builder.buffer, expected_result, multi_append_builder.count) == 0, "Failed: multiple appends"); dealloc(heap, multi_append_builder.buffer); string cheese_hello = STR("HeCHEESElloCHEESE, WorCHEESEld!"); string hello = string_replace_all(cheese_hello, STR("CHEESE"), STR(""), heap); assert(strings_match(hello, STR("Hello, World!")), "Failed: string_replace"); string hello_balls = string_replace_all(hello, STR("Hello"), STR("Greetings"), heap); hello_balls = string_replace_all(hello_balls, STR("World"), STR("Balls"), heap); assert(strings_match(hello_balls, STR("Greetings, Balls!")), "Failed: string_replace"); } void test_file_io() { #if TARGET_OS == WINDOWS && !OOGABOOGA_LINK_EXTERNAL_INSTANCE // Test win32_fixed_utf8_to_null_terminated_wide string utf8_str = STR("Test"); u16 *wide_str = win32_fixed_utf8_to_null_terminated_wide(utf8_str, get_heap_allocator()); assert(wide_str != NULL, "Failed: win32_fixed_utf8_to_null_terminated_wide"); assert(wide_str[4] == 0, "Failed: win32_fixed_utf8_to_null_terminated_wide"); dealloc(get_heap_allocator(), wide_str); // Test temp_win32_fixed_utf8_to_null_terminated_wide wide_str = temp_win32_fixed_utf8_to_null_terminated_wide(utf8_str); assert(wide_str != NULL, "Failed: temp_win32_fixed_utf8_to_null_terminated_wide"); assert(wide_str[4] == 0, "Failed: temp_win32_fixed_utf8_to_null_terminated_wide"); #endif File file = OS_INVALID_FILE; os_file_close(file); // Test os_file_open and os_file_close file = os_file_open("test.txt", O_WRITE | O_CREATE); assert(file != OS_INVALID_FILE, "Failed: os_file_open (write/create)"); os_file_close(file); // Test os_file_write_string and os_file_read string hello_world_write = STR("Hello, World!"); file = os_file_open("test.txt", O_WRITE | O_CREATE); assert(file != OS_INVALID_FILE, "Failed: os_file_open (write/create)"); bool write_result = os_file_write_string(file, hello_world_write); assert(write_result, "Failed: os_file_write_string"); os_file_close(file); file = os_file_open("test.txt", O_READ); assert(file != OS_INVALID_FILE, "Failed: os_file_open (read)"); string hello_world_read = talloc_string(hello_world_write.count); bool read_result = os_file_read(file, hello_world_read.data, hello_world_read.count, &hello_world_read.count); assert(read_result, "Failed: os_file_read %d", GetLastError()); assert(strings_match(hello_world_read, hello_world_write), "Failed: os_file_read write/read mismatch"); os_file_close(file); // Test os_file_write_bytes file = os_file_open("test_bytes.txt", O_WRITE | O_CREATE); assert(file != OS_INVALID_FILE, "Failed: os_file_open (write/create)"); int int_data = 42; write_result = os_file_write_bytes(file, &int_data, sizeof(int)); assert(write_result, "Failed: os_file_write_bytes"); os_file_close(file); // Test os_read_entire_file and os_write_entire_file string write_data = STR("Entire file test"); bool write_entire_result = os_write_entire_file("entire_test.txt", write_data); assert(write_entire_result, "Failed: os_write_entire_file"); Allocator heap = get_heap_allocator(); string read_data; bool read_entire_result = os_read_entire_file("entire_test.txt", &read_data, heap); assert(read_entire_result, "Failed: os_read_entire_file"); assert(strings_match(read_data, write_data), "Failed: os_read_entire_file write/read mismatch"); assert(memcmp(read_data.data, write_data.data, write_data.count) == 0, "Failed: os_read_entire_file (content mismatch)"); dealloc(heap, read_data.data); // Test fprint File balls = os_file_open("balls.txt", O_WRITE | O_CREATE); assert(balls != OS_INVALID_FILE, "Failed: Could not create balls.txt"); fprint(balls, "Hello, %cs!", "Balls"); os_file_close(balls); string hello_balls; read_entire_result = os_read_entire_file("balls.txt", &hello_balls, heap); assert(read_entire_result, "Failed: could not read balls.txt"); assert(strings_match(hello_balls, STR("Hello, Balls!")), "Failed: balls read/write mismatch. Expected 'Hello, Balls!', got '%s'", hello_balls); u64 integers[4096]; for (u64 i = 0; i < 4096; i++) { integers[i] = get_random(); } string integers_data; integers_data.data = (u8*)integers; integers_data.count = 4096*sizeof(u64); bool ok = os_write_entire_file("integers", integers_data); assert(ok, "write integers fail"); string integers_read; ok = os_read_entire_file("integers", &integers_read, heap); assert(ok, "read integers fail"); u64 *new_integers = (u64*)integers_data.data; assert(integers_read.count == integers_data.count, "Failed: big file read/write mismatch. Read was %d and written was %d", integers_read.count, integers_data.count); assert(strings_match(integers_data, integers_read), "Failed: big file read/write mismatch"); assert(os_is_file("test.txt"), "Failed: test.txt not recognized as file"); assert(os_is_file("test_bytes.txt"), "Failed: test_bytes.txt not recognized as file"); assert(os_is_file("entire_test.txt"), "Failed: entire_test.txt not recognized as file"); assert(os_is_file("balls.txt"), "Failed: balls.txt not recognized as file"); assert(os_is_file("integers"), "Failed: integers not recognized as file"); bool dir_ok = os_make_directory("test_dir", false); assert(dir_ok, "Failed: os_make_directory"); assert(os_is_directory("test_dir"), "Failed: os_is_directory"); string dir_abs; bool abs_ok = os_get_absolute_path(STR("test_dir"), &dir_abs, heap); assert(abs_ok, "Failed: os_get_absolute_path"); assert(os_is_path_absolute(dir_abs), "Failed: os_is_path_absolute"); assert(!os_is_path_absolute(STR("test_dir")), "Failed: os_is_path_absolute"); string dir_rel; bool rel_ok = os_get_relative_path(STR("."), dir_abs, &dir_rel, heap); assert(rel_ok, "Failed: os_get_relative_path"); assert(os_do_paths_match(dir_rel, STR("test_dir")), "Failed: Resolved relative path does not match. Expected '.\test_dir', got %s.", dir_rel); dir_ok = os_make_directory("test_dir1/test_dir2/test_dir3", true); assert(dir_ok, "Failed: os_make_directory"); dir_ok = os_make_directory("test_dir1/test_dir2/test_dir4", true); assert(dir_ok, "Failed: os_make_directory"); assert(os_is_directory("test_dir1"), "Failed: os_is_directory"); assert(os_is_directory("test_dir1/test_dir2"), "Failed: os_is_directory"); assert(os_is_directory("test_dir1/test_dir2//test_dir3"), "Failed: os_is_directory"); assert(os_is_directory("test_dir1/test_dir2//test_dir4"), "Failed: os_is_directory"); // Clean up test files bool delete_ok = false; delete_ok = os_file_delete("test.txt"); assert(delete_ok, "Failed: could not delete test.txt"); delete_ok = os_file_delete("test_bytes.txt"); assert(delete_ok, "Failed: could not delete test_bytes.txt"); delete_ok = os_file_delete("entire_test.txt"); assert(delete_ok, "Failed: could not delete entire_test.txt"); delete_ok = os_file_delete("balls.txt"); assert(delete_ok, "Failed: could not delete balls.txt"); delete_ok = os_file_delete("integers"); assert(delete_ok, "Failed: could not delete integers"); delete_ok = os_delete_directory("test_dir", false); assert(delete_ok, "Failed: could not delete test_dir"); delete_ok = os_delete_directory("test_dir1", true); assert(delete_ok, "Failed: could not delete test_dir1 (recursive)"); } bool floats_roughly_match(float a, float b) { return fabs(a - b) < 0.01; } void test_simd() { f32 *a_f32 = alloc(get_heap_allocator(), 128*sizeof(f32)); f32 *b_f32 = alloc(get_heap_allocator(), 128*sizeof(f32)); f32 *result_f32 = alloc(get_heap_allocator(), 128*sizeof(f32)); s32 *a_i32 = alloc(get_heap_allocator(), 128*sizeof(f32)); s32 *b_i32 = alloc(get_heap_allocator(), 128*sizeof(f32)); s32 *result_i32 = alloc(get_heap_allocator(), 128*sizeof(f32)); (*(u64*)&a_f32) = ((*(u64*)&a_f32)+64) & ~(63); (*(u64*)&b_f32) = ((*(u64*)&b_f32)+64) & ~(63); (*(u64*)&result_f32) = ((*(u64*)&result_f32)+64) & ~(63); (*(u64*)&a_i32) = ((*(u64*)&a_i32)+64) & ~(63); (*(u64*)&b_i32) = ((*(u64*)&b_i32)+64) & ~(63); (*(u64*)&result_i32) = ((*(u64*)&result_i32)+64) & ~(63); assert((u64)a_f32%16 == 0); assert((u64)b_f32%16 == 0); assert((u64)result_f32%16 == 0); assert((u64)a_i32%16 == 0); assert((u64)b_i32%16 == 0); assert((u64)result_i32%16 == 0); assert((u64)a_f32%32 == 0); assert((u64)b_f32%32 == 0); assert((u64)result_f32%32 == 0); assert((u64)a_i32%32 == 0); assert((u64)b_i32%32 == 0); assert((u64)result_i32%32 == 0); assert((u64)a_f32%64 == 0); assert((u64)b_f32%64 == 0); assert((u64)result_f32%64 == 0); assert((u64)a_i32%64 == 0); assert((u64)b_i32%64 == 0); assert((u64)result_i32%64 == 0); for (int i = 0; i < 16; ++i) { a_f32[i] = i * 1.0f; b_f32[i] = (i + 1) * 2.0f; a_i32[i] = i; b_i32[i] = i + 1; } // Test function pointers setup query_cpu_capabilities(); // Test float32 add simd_add_float32_64(a_f32, b_f32, result_f32); assert(floats_roughly_match(result_f32[0], a_f32[0]+b_f32[0]), "SIMD add float32 64 failed"); simd_add_float32_128_aligned(a_f32, b_f32, result_f32); for (int i = 0; i < 4; ++i) { assert(floats_roughly_match(result_f32[i], a_f32[i] + b_f32[i]), "SIMD add float32 128 failed"); } simd_add_float32_256_aligned(a_f32, b_f32, result_f32); for (int i = 0; i < 8; ++i) { assert(floats_roughly_match(result_f32[i], a_f32[i] + b_f32[i]), "SIMD add float32 256 failed"); } simd_add_float32_512_aligned(a_f32, b_f32, result_f32); for (int i = 0; i < 16; ++i) { assert(floats_roughly_match(result_f32[i], a_f32[i] + b_f32[i]), "SIMD add float32 512 failed"); } // Test float32 subtract simd_sub_float32_64(a_f32, b_f32, result_f32); assert(floats_roughly_match(result_f32[0], a_f32[0] - b_f32[0]), "SIMD sub float32 64 failed"); simd_sub_float32_128_aligned(a_f32, b_f32, result_f32); for (int i = 0; i < 4; ++i) { assert(floats_roughly_match(result_f32[i], a_f32[i] - b_f32[i]), "SIMD sub float32 128 failed"); } simd_sub_float32_256_aligned(a_f32, b_f32, result_f32); for (int i = 0; i < 8; ++i) { assert(floats_roughly_match(result_f32[i], a_f32[i] - b_f32[i]), "SIMD sub float32 256 failed"); } simd_sub_float32_512_aligned(a_f32, b_f32, result_f32); for (int i = 0; i < 16; ++i) { assert(floats_roughly_match(result_f32[i], a_f32[i] - b_f32[i]), "SIMD sub float32 512 failed"); } // Test float32 multiply simd_mul_float32_64(a_f32, b_f32, result_f32); assert(floats_roughly_match(result_f32[0], a_f32[0]*b_f32[0]), "SIMD mul float32 64 failed"); simd_mul_float32_128_aligned(a_f32, b_f32, result_f32); for (int i = 0; i < 4; ++i) { assert(floats_roughly_match(result_f32[i], a_f32[i] * b_f32[i]), "SIMD mul float32 128 failed"); } simd_mul_float32_256_aligned(a_f32, b_f32, result_f32); for (int i = 0; i < 8; ++i) { assert(floats_roughly_match(result_f32[i], a_f32[i] * b_f32[i]), "SIMD mul float32 256 failed"); } simd_mul_float32_512_aligned(a_f32, b_f32, result_f32); for (int i = 0; i < 16; ++i) { assert(floats_roughly_match(result_f32[i], a_f32[i] * b_f32[i]), "SIMD mul float32 512 failed"); } // Test float32 divide simd_div_float32_64(a_f32, b_f32, result_f32); assert(floats_roughly_match(result_f32[0], a_f32[0]/b_f32[0]), "SIMD div float32 64 failed"); simd_div_float32_128_aligned(a_f32, b_f32, result_f32); for (int i = 0; i < 4; ++i) { assert(floats_roughly_match(result_f32[i], a_f32[i] / b_f32[i]), "SIMD div float32 128 failed"); } simd_div_float32_256_aligned(a_f32, b_f32, result_f32); for (int i = 0; i < 8; ++i) { assert(floats_roughly_match(result_f32[i], a_f32[i] / b_f32[i]), "SIMD div float32 256 failed, %.5f, %.5f, %.5f", result_f32[i], a_f32[i], a_f32[i] / b_f32[i]); } simd_div_float32_512_aligned(a_f32, b_f32, result_f32); for (int i = 0; i < 16; ++i) { assert(floats_roughly_match(result_f32[i], a_f32[i] / b_f32[i]), "SIMD div float32 512 failed"); } // Test int32 add simd_add_int32_128_aligned(a_i32, b_i32, result_i32); for (int i = 0; i < 4; ++i) { assert(result_i32[i] == a_i32[i] + b_i32[i], "SIMD add int32 128 failed"); } simd_add_int32_256_aligned(a_i32, b_i32, result_i32); for (int i = 0; i < 8; ++i) { assert(result_i32[i] == a_i32[i] + b_i32[i], "SIMD add int32 256 failed"); } simd_add_int32_512_aligned(a_i32, b_i32, result_i32); for (int i = 0; i < 16; ++i) { assert(result_i32[i] == a_i32[i] + b_i32[i], "SIMD add int32 512 failed"); } // Test int32 subtract simd_sub_int32_128_aligned(a_i32, b_i32, result_i32); for (int i = 0; i < 4; ++i) { assert(result_i32[i] == a_i32[i] - b_i32[i], "SIMD sub int32 128 failed"); } simd_sub_int32_256_aligned(a_i32, b_i32, result_i32); for (int i = 0; i < 8; ++i) { assert(result_i32[i] == a_i32[i] - b_i32[i], "SIMD sub int32 256 failed"); } simd_sub_int32_512_aligned(a_i32, b_i32, result_i32); for (int i = 0; i < 16; ++i) { assert(result_i32[i] == a_i32[i] - b_i32[i], "SIMD sub int32 512 failed"); } // Test int32 multiply simd_mul_int32_128_aligned(a_i32, b_i32, result_i32); for (int i = 0; i < 4; ++i) { assert(result_i32[i] == a_i32[i] * b_i32[i], "SIMD mul int32 128 failed"); } simd_mul_int32_256_aligned(a_i32, b_i32, result_i32); for (int i = 0; i < 8; ++i) { assert(result_i32[i] == a_i32[i] * b_i32[i], "SIMD mul int32 256 failed"); } simd_mul_int32_512_aligned(a_i32, b_i32, result_i32); for (int i = 0; i < 16; ++i) { assert(result_i32[i] == a_i32[i] * b_i32[i], "SIMD mul int32 512 failed"); } #define _TEST_NUM_SAMPLES ((100000 + 64) & ~(63)) assert(_TEST_NUM_SAMPLES % 16 == 0); float *samples_a = alloc(get_heap_allocator(), _TEST_NUM_SAMPLES*sizeof(float)+512); float *samples_b = alloc(get_heap_allocator(), _TEST_NUM_SAMPLES*sizeof(float)+512); samples_a = (float*)(((u64)samples_a+64)&~(63)); samples_b = (float*)(((u64)samples_b+64)&~(63)); memset(samples_a, 2, _TEST_NUM_SAMPLES*sizeof(float)); memset(samples_b, 2, _TEST_NUM_SAMPLES*sizeof(float)); u64 start = rdtsc(); for (u64 i = 0; i < _TEST_NUM_SAMPLES; i += 16) { simd_mul_float32_512_aligned(&samples_a[i], &samples_b[i], &samples_a[i]); } u64 end = rdtsc(); u64 cycles = end-start; print("simd 512 float32 mul took %llu cycles\n", cycles); memset(samples_a, 2, _TEST_NUM_SAMPLES*sizeof(float)); memset(samples_b, 2, _TEST_NUM_SAMPLES*sizeof(float)); start = rdtsc(); for (u64 i = 0; i < _TEST_NUM_SAMPLES; i += 8) { simd_mul_float32_256_aligned(&samples_a[i], &samples_b[i], &samples_a[i]); } end = rdtsc(); cycles = end-start; print("simd 256 float32 mul took %llu cycles\n", cycles); memset(samples_a, 2, _TEST_NUM_SAMPLES*sizeof(float)); memset(samples_b, 2, _TEST_NUM_SAMPLES*sizeof(float)); start = rdtsc(); for (u64 i = 0; i < _TEST_NUM_SAMPLES; i += 4) { simd_mul_float32_128_aligned(&samples_a[i], &samples_b[i], &samples_a[i]); } end = rdtsc(); cycles = end-start; print("simd 128 float32 mul took %llu cycles\n", cycles); memset(samples_a, 2, _TEST_NUM_SAMPLES*sizeof(float)); memset(samples_b, 2, _TEST_NUM_SAMPLES*sizeof(float)); start = rdtsc(); for (u64 i = 0; i < _TEST_NUM_SAMPLES; i += 2) { simd_mul_float32_64(&samples_a[i], &samples_b[i], &samples_a[i]); } end = rdtsc(); cycles = end-start; print("simd 64 float32 mul took %llu cycles\n", cycles); memset(samples_a, 2, _TEST_NUM_SAMPLES*sizeof(float)); memset(samples_b, 2, _TEST_NUM_SAMPLES*sizeof(float)); start = rdtsc(); for (u64 i = 0; i < _TEST_NUM_SAMPLES; i += 1) { samples_a[i] = samples_a[i] + samples_b[i]; } end = rdtsc(); cycles = end-start; print("NO SIMD float32 mul took %llu cycles\n", cycles); } // Indirect testing of some simd stuff void test_linmath() { // Test vector creation and access Vector2 v2_test = v2(1.0f, 2.0f); assert(v2_test.x == 1.0f && v2_test.y == 2.0f, "v2 creation incorrect"); Vector3 v3_test = v3(1.0f, 2.0f, 3.0f); assert(v3_test.x == 1.0f && v3_test.y == 2.0f && v3_test.z == 3.0f, "v3 creation incorrect"); Vector4 v4_test = v4(1.0f, 2.0f, 3.0f, 4.0f); assert(v4_test.x == 1.0f && v4_test.y == 2.0f && v4_test.z == 3.0f && v4_test.w == 4.0f, "v4 creation incorrect"); // Test vector operations Vector2 v2_a = v2(3.0f, 4.0f); Vector2 v2_b = v2(1.0f, 2.0f); Vector2 v2_result = v2_add(v2_a, v2_b); assert(v2_result.x == 4.0f && v2_result.y == 6.0f, "v2_add incorrect. %.3f, %.3f", v2_result.x, v2_result.y); v2_result = v2_sub(v2_a, v2_b); assert(v2_result.x == 2.0f && v2_result.y == 2.0f, "v2_sub incorrect"); v2_result = v2_mul(v2_a, v2_b); assert(v2_result.x == 3.0f && v2_result.y == 8.0f, "v2_mul incorrect"); v2_result = v2_div(v2_a, v2_b); assert(v2_result.x == 3.0f && v2_result.y == 2.0f, "v2_div incorrect"); v2_result = v2_mulf(v2_a, 2.0f); assert(v2_result.x == 6.0f && v2_result.y == 8.0f, "v2_mulf incorrect"); v2_result = v2_divf(v2_a, 2.0f); assert(v2_result.x == 1.5f && v2_result.y == 2.0f, "v2_divf incorrect"); // Test matrix operations Matrix4 m1 = m4_scalar(2.0f); assert(m1.data[0] == 2.0f && m1.data[5] == 2.0f && m1.data[10] == 2.0f && m1.data[15] == 2.0f, "m4_scalar incorrect"); Vector3 translation = v3(1.0f, 2.0f, 3.0f); Matrix4 m2 = m4_make_translation(translation); assert(m2.m[0][3] == 1.0f && m2.m[1][3] == 2.0f && m2.m[2][3] == 3.0f, "m4_make_translation incorrect"); Vector3 scale = v3(2.0f, 3.0f, 4.0f); Matrix4 m4 = m4_make_scale(scale); assert(m4.m[0][0] == 2.0f && m4.m[1][1] == 3.0f && m4.m[2][2] == 4.0f, "m4_make_scale incorrect"); // Test orthographic projection matrix Matrix4 ortho = m4_make_orthographic_projection(-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f); assert(ortho.m[0][0] == 1.0f && ortho.m[1][1] == 1.0f && ortho.m[2][2] == 1.0f, "m4_make_orthographic_projection incorrect"); // Test matrix multiplication Matrix4 m5 = m4_scalar(1.0f); m5.m[0][3] = 1.0f; m5.m[1][3] = 2.0f; m5.m[2][3] = 3.0f; Matrix4 result_matrix = m4_mul(m2, m5); assert(result_matrix.m[0][3] == 2.0f && result_matrix.m[1][3] == 4.0f && result_matrix.m[2][3] == 6.0f, "m4_mul incorrect"); // Test matrix inverse Matrix4 identity = m4_scalar(1.0f); Matrix4 inverse_matrix = m4_inverse(identity); for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { assert(inverse_matrix.m[i][j] == identity.m[i][j], "m4_inverse incorrect for identity matrix"); } } // Test Vector2 creation Vector2 v2_test1 = v2(1.0f, 2.0f); assert(v2_test1.x == 1.0f && v2_test1.y == 2.0f, "Vector2 creation failed"); Vector2 v2_test2 = v2(3.0f, 4.0f); assert(v2_test2.x == 3.0f && v2_test2.y == 4.0f, "Vector2 creation failed"); // Test Vector2 addition Vector2 v2_add_result = v2_add(v2_test1, v2_test2); assert(v2_add_result.x == 4.0f && v2_add_result.y == 6.0f, "Vector2 addition failed"); // Test Vector2 subtraction Vector2 v2_sub_result = v2_sub(v2_test2, v2_test1); assert(v2_sub_result.x == 2.0f && v2_sub_result.y == 2.0f, "Vector2 subtraction failed"); // Test Vector2 multiplication Vector2 v2_mul_result = v2_mul(v2_test1, v2_test2); assert(v2_mul_result.x == 3.0f && v2_mul_result.y == 8.0f, "Vector2 multiplication failed"); // Test Vector2 scalar multiplication Vector2 v2_mulf_result = v2_mulf(v2_test1, 2.0f); assert(v2_mulf_result.x == 2.0f && v2_mulf_result.y == 4.0f, "Vector2 scalar multiplication failed"); // Test Vector2 division Vector2 v2_div_result = v2_div(v2_test2, v2_test1); assert(v2_div_result.x == 3.0f && v2_div_result.y == 2.0f, "Vector2 division failed"); // Test Vector2 scalar division Vector2 v2_divf_result = v2_divf(v2_test2, 2.0f); assert(v2_divf_result.x == 1.5f && v2_divf_result.y == 2.0f, "Vector2 scalar division failed"); // Test Vector2 normalization Vector2 v2_norm_result = v2_normalize(v2(3.0f, 4.0f)); assert(fabs(v2_norm_result.x - 0.6f) < 1e-6 && fabs(v2_norm_result.y - 0.8f) < 1e-6, "Vector2 normalization failed"); // Test Vector2 rotation Vector2 pivot = v2(0.0f, 0.0f); Vector2 point = v2(1.0f, 0.0f); Vector2 v2_rot_result = v2_rotate_point_around_pivot(point, pivot, PI32 / 2.0f); assert(fabs(v2_rot_result.x) < 1e-6 && fabs(v2_rot_result.y - 1.0f) < 1e-6, "Vector2 rotation around pivot failed"); // Test Vector3 creation Vector3 v3_test1 = v3(1.0f, 2.0f, 3.0f); assert(v3_test1.x == 1.0f && v3_test1.y == 2.0f && v3_test1.z == 3.0f, "Vector3 creation failed"); Vector3 v3_test2 = v3(4.0f, 5.0f, 6.0f); assert(v3_test2.x == 4.0f && v3_test2.y == 5.0f && v3_test2.z == 6.0f, "Vector3 creation failed"); // Test Vector3 addition Vector3 v3_add_result = v3_add(v3_test1, v3_test2); assert(v3_add_result.x == 5.0f && v3_add_result.y == 7.0f && v3_add_result.z == 9.0f, "Vector3 addition failed"); // Test Vector3 subtraction Vector3 v3_sub_result = v3_sub(v3_test2, v3_test1); assert(v3_sub_result.x == 3.0f && v3_sub_result.y == 3.0f && v3_sub_result.z == 3.0f, "Vector3 subtraction failed"); // Test Vector3 multiplication Vector3 v3_mul_result = v3_mul(v3_test1, v3_test2); assert(v3_mul_result.x == 4.0f && v3_mul_result.y == 10.0f && v3_mul_result.z == 18.0f, "Vector3 multiplication failed"); // Test Vector3 scalar multiplication Vector3 v3_mulf_result = v3_mulf(v3_test1, 2.0f); assert(v3_mulf_result.x == 2.0f && v3_mulf_result.y == 4.0f && v3_mulf_result.z == 6.0f, "Vector3 scalar multiplication failed"); // Test Vector3 division Vector3 v3_div_result = v3_div(v3_test2, v3_test1); assert(v3_div_result.x == 4.0f && v3_div_result.y == 2.5f && v3_div_result.z == 2.0f, "Vector3 division failed"); // Test Vector3 scalar division Vector3 v3_divf_result = v3_divf(v3_test2, 2.0f); assert(v3_divf_result.x == 2.0f && v3_divf_result.y == 2.5f && v3_divf_result.z == 3.0f, "Vector3 scalar division failed"); // Test Vector4 creation Vector4 v4_test1 = v4(1.0f, 2.0f, 3.0f, 4.0f); assert(v4_test1.x == 1.0f && v4_test1.y == 2.0f && v4_test1.z == 3.0f && v4_test1.w == 4.0f, "Vector4 creation failed"); Vector4 v4_test2 = v4(5.0f, 6.0f, 7.0f, 8.0f); assert(v4_test2.x == 5.0f && v4_test2.y == 6.0f && v4_test2.z == 7.0f && v4_test2.w == 8.0f, "Vector4 creation failed"); // Test Vector4 addition Vector4 v4_add_result = v4_add(v4_test1, v4_test2); assert(v4_add_result.x == 6.0f && v4_add_result.y == 8.0f && v4_add_result.z == 10.0f && v4_add_result.w == 12.0f, "Vector4 addition failed"); // Test Vector4 subtraction Vector4 v4_sub_result = v4_sub(v4_test2, v4_test1); assert(v4_sub_result.x == 4.0f && v4_sub_result.y == 4.0f && v4_sub_result.z == 4.0f && v4_sub_result.w == 4.0f, "Vector4 subtraction failed"); // Test Vector4 multiplication Vector4 v4_mul_result = v4_mul(v4_test1, v4_test2); assert(v4_mul_result.x == 5.0f && v4_mul_result.y == 12.0f && v4_mul_result.z == 21.0f && v4_mul_result.w == 32.0f, "Vector4 multiplication failed"); // Test Vector4 scalar multiplication Vector4 v4_mulf_result = v4_mulf(v4_test1, 2.0f); assert(v4_mulf_result.x == 2.0f && v4_mulf_result.y == 4.0f && v4_mulf_result.z == 6.0f && v4_mulf_result.w == 8.0f, "Vector4 scalar multiplication failed"); // Test Vector4 division Vector4 v4_div_result = v4_div(v4_test2, v4_test1); assert(v4_div_result.x == 5.0f && v4_div_result.y == 3.0f && v4_div_result.w == 2.0f, "Vector4 division failed"); // Test Vector4 scalar division Vector4 v4_divf_result = v4_divf(v4_test2, 2.0f); assert(v4_divf_result.x == 2.5f && v4_divf_result.y == 3.0f && v4_divf_result.z == 3.5f && v4_divf_result.w == 4.0f, "Vector4 scalar division failed"); // Test mixed vector and scalar operations Vector2 mixed_v2 = v2(2.0f, 4.0f); Vector2 mixed_v2_result = v2_mulf(mixed_v2, 0.5f); assert(mixed_v2_result.x == 1.0f && mixed_v2_result.y == 2.0f, "Mixed Vector2 scalar multiplication failed"); Vector3 mixed_v3 = v3(3.0f, 6.0f, 9.0f); Vector3 mixed_v3_result = v3_divf(mixed_v3, 3.0f); assert(mixed_v3_result.x == 1.0f && mixed_v3_result.y == 2.0f && mixed_v3_result.z == 3.0f, "Mixed Vector3 scalar division failed"); Vector4 mixed_v4 = v4(4.0f, 8.0f, 12.0f, 16.0f); Vector4 mixed_v4_result = v4_mulf(mixed_v4, 0.25f); assert(mixed_v4_result.x == 1.0f && mixed_v4_result.y == 2.0f && mixed_v4_result.z == 3.0f && mixed_v4_result.w == 4.0f, "Mixed Vector4 scalar multiplication failed"); float v2_dot_product = v2_dot(v2(2, 7), v2(3, 2)); float v3_dot_product = v3_dot(v3(2, 7, 2), v3(3, 2, 9)); float v4_dot_product = v4_dot(v4(2, 7, 6, 1), v4(3, 2, 1, 4)); assert(floats_roughly_match(v2_dot_product, 20), "Failed: v2_dot"); assert(floats_roughly_match(v3_dot_product, 38), "Failed: v3_dot"); assert(floats_roughly_match(v4_dot_product, 30), "Failed: v4_dot"); } void test_intmath() { // Test vector creation and access Vector2i v2i_test = v2i(1, 2); assert(v2i_test.x == 1 && v2i_test.y == 2, "v2i creation incorrect"); Vector3i v3i_test = v3i(1, 2, 3); assert(v3i_test.x == 1 && v3i_test.y == 2 && v3i_test.z == 3, "v3i creation incorrect"); Vector4i v4i_test = v4i(1, 2, 3, 4); assert(v4i_test.x == 1 && v4i_test.y == 2 && v4i_test.z == 3 && v4i_test.w == 4, "v4i creation incorrect"); // Test vector2 operations Vector2i v2i_a = v2i(3, 4); Vector2i v2i_b = v2i(1, 2); Vector2i v2i_result = v2i_add(v2i_a, v2i_b); assert(v2i_result.x == 4 && v2i_result.y == 6, "v2i_add incorrect"); v2i_result = v2i_sub(v2i_a, v2i_b); assert(v2i_result.x == 2 && v2i_result.y == 2, "v2i_sub incorrect"); v2i_result = v2i_mul(v2i_a, v2i_b); assert(v2i_result.x == 3 && v2i_result.y == 8, "v2i_mul incorrect"); v2i_result = v2i_div(v2i_a, v2i_b); assert(v2i_result.x == 3 && v2i_result.y == 2, "v2i_div incorrect"); v2i_result = v2i_muli(v2i_a, 2); assert(v2i_result.x == 6 && v2i_result.y == 8, "v2i_muli incorrect"); v2i_result = v2i_divi(v2i_a, 2); assert(v2i_result.x == 1 && v2i_result.y == 2, "v2i_divi incorrect"); // Test vector2 operations Vector3i v3i_a = v3i(3, 4, 6); Vector3i v3i_b = v3i(1, 2, 3); Vector3i v3i_result = v3i_add(v3i_a, v3i_b); assert(v3i_result.x == 4 && v3i_result.y == 6 && v3i_result.z == 9, "v3i_add incorrect."); v3i_result = v3i_sub(v3i_a, v3i_b); assert(v3i_result.x == 2 && v3i_result.y == 2 && v3i_result.z == 3, "v3i_sub incorrect"); v3i_result = v3i_mul(v3i_a, v3i_b); assert(v3i_result.x == 3 && v3i_result.y == 8 && v3i_result.z == 18, "v3i_mul incorrect"); v3i_result = v3i_div(v3i_a, v3i_b); assert(v3i_result.x == 3 && v3i_result.y == 2 && v3i_result.z == 2, "v3i_div incorrect"); v3i_result = v3i_muli(v3i_a, 2); assert(v3i_result.x == 6 && v3i_result.y == 8 && v3i_result.z == 12, "v3i_muli incorrect"); v3i_result = v3i_divi(v3i_a, 2); assert(v3i_result.x == 1 && v3i_result.y == 2 && v3i_result.z == 3, "v3i_divi incorrect"); Vector4i v4i_a = v4i(3, 4, 6, 8); Vector4i v4i_b = v4i(1, 2, 3, 4); Vector4i v4i_result = v4i_add(v4i_a, v4i_b); assert(v4i_result.x == 4 && v4i_result.y == 6 && v4i_result.z == 9 && v4i_result.w == 12, "v4i_add incorrect."); v4i_result = v4i_sub(v4i_a, v4i_b); assert(v4i_result.x == 2 && v4i_result.y == 2 && v4i_result.z == 3 && v4i_result.w == 4, "v4i_sub incorrect"); v4i_result = v4i_mul(v4i_a, v4i_b); assert(v4i_result.x == 3 && v4i_result.y == 8 && v4i_result.z == 18 && v4i_result.w == 32, "v4i_mul incorrect"); v4i_result = v4i_div(v4i_a, v4i_b); assert(v4i_result.x == 3 && v4i_result.y == 2 && v4i_result.z == 2 && v4i_result.w == 2, "v4i_div incorrect"); v4i_result = v4i_muli(v4i_a, 2); assert(v4i_result.x == 6 && v4i_result.y == 8 && v4i_result.z == 12 && v4i_result.w == 16, "v4i_muli incorrect"); v4i_result = v4i_divi(v4i_a, 2); assert(v4i_result.x == 1 && v4i_result.y == 2 && v4i_result.z == 3 && v4i_result.w == 4, "v4i_divi incorrect"); } void test_hash_table() { Hash_Table table = make_hash_table(string, int, get_heap_allocator()); 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"); 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); int new_value1 = 70; newly_added = hash_table_set(&table, key1, new_value1); assert(newly_added == false, "Failed: Key should not be newly added"); 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); bool contains = hash_table_contains(&table, key1); assert(contains == true, "Failed: Hash table should contain key1"); string key2 = STR("Non-existing key"); contains = hash_table_contains(&table, key2); assert(contains == false, "Failed: Hash table should not contain key2"); hash_table_reset(&table); found_value = hash_table_find(&table, key1); assert(found_value == NULL, "Failed: Hash table should be empty after reset"); 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"); } #define NUM_BINS 100 #define NUM_SAMPLES 100000000 void test_random_distribution() { int bins[NUM_BINS] = {0}; seed_for_random = rdtsc(); for (int i = 0; i < NUM_SAMPLES; i++) { f32 rand_val = get_random_float32(); int bin = (int)(rand_val * NUM_BINS); bins[bin]++; } int min_bin = INT32_MAX; int max_bin = 0; print("Histogram of Random Values:\n"); for (int i = 0; i < NUM_BINS; i++) { print("Bin %d: %d\n", i, bins[i]); min_bin = min(min_bin, bins[i]); max_bin = max(max_bin, bins[i]); } print("Min: %d, max: %d\n", min_bin, max_bin); } #define MUTEX_TEST_TASK_COUNT 1000 typedef struct Mutex_Test_Shared_Data { int counter; bool any_active_thread; Mutex mutex; } Mutex_Test_Shared_Data; void mutex_test_increment_counter(Thread* t) { Mutex_Test_Shared_Data* data = (Mutex_Test_Shared_Data*)t->data; for (int i = 0; i < MUTEX_TEST_TASK_COUNT; i++) { mutex_acquire_or_wait(&data->mutex); assert(!data->any_active_thread, "Failed: More than one thread is in critical section!"); data->any_active_thread = true; data->counter++; data->any_active_thread = false; mutex_release(&data->mutex); } } void test_mutex() { Mutex m; // Test initialization mutex_init(&m); assert(m.spin_time_microseconds == MUTEX_DEFAULT_SPIN_TIME_MICROSECONDS, "Failed: Default spin time incorrect"); assert(!m.spinlock_acquired, "Failed: Spinlock should not be acquired after initialization"); // Test acquire and release without contention mutex_acquire_or_wait(&m); assert(m.spinlock_acquired, "Failed: Mutex should be acquired after mutex_acquire_or_wait"); mutex_release(&m); assert(!m.spinlock_acquired, "Failed: Spinlock should not be acquired after mutex_release"); // Clean up mutex_destroy(&m); Mutex_Test_Shared_Data data; data.counter = 0; data.any_active_thread = false; mutex_init(&data.mutex); Allocator allocator = get_heap_allocator(); const int num_threads = 100; Thread *threads = alloc(allocator, sizeof(Thread)*num_threads); for (u64 i = 0; i < num_threads; i++) { os_thread_init(&threads[i], mutex_test_increment_counter); threads[i].data = &data; } for (u64 i = 0; i < num_threads; i++) { os_thread_start(&threads[i]); } for (u64 i = 0; i < num_threads; i++) { os_thread_join(&threads[i]); } assert(data.counter == num_threads * MUTEX_TEST_TASK_COUNT, "Failed: Counter does not match expected value after threading tasks"); mutex_destroy(&data.mutex); } #ifndef OOGABOOGA_HEADLESS int compare_draw_quads(const void *a, const void *b) { return ((Draw_Quad*)a)->z-((Draw_Quad*)b)->z; } void test_sort() { int num_samples = 100; u64 id_bits = 21; u64 item_count = 5000; f64 seconds = 0; u64 cycles = 0; Draw_Quad *items = alloc(get_heap_allocator(), (item_count * 2) * sizeof(Draw_Quad)); Draw_Quad *buffer = items + item_count; for (int a = 0; a < num_samples; a++) { for (u64 i = 0; i < item_count; i++) { if (i % 2 == 0) items[i].z = get_random_int_in_range(0, pow(2, id_bits) / 2); else items[i].z = i; } u64 item_size = sizeof(Draw_Quad); u64 sort_value_offset_in_item = offsetof(Draw_Quad, z); float64 start_seconds = os_get_current_time_in_seconds(); u64 start_cycles = rdtsc(); radix_sort(items, buffer, item_count, item_size, sort_value_offset_in_item, id_bits); u64 end_cycles = rdtsc(); float64 end_seconds = os_get_current_time_in_seconds(); for (u64 i = 1; i < item_count; i++) { assert(items[i].z >= items[i-1].z, "Failed: not correctly sorted"); } seconds += end_seconds - start_seconds; cycles += end_cycles - start_cycles; } print("Radix sort took on average %llu cycles and %.2f ms\n", cycles / num_samples, (seconds * 1000.0) / (float64)num_samples); seconds = 0; cycles = 0; for (int a = 0; a < num_samples; a++) { for (u64 i = 0; i < item_count; i++) { if (i % 2 == 0) items[i].z = get_random_int_in_range(0, pow(2, id_bits) / 2); else items[i].z = i; } u64 item_size = sizeof(Draw_Quad); u64 sort_value_offset_in_item = offsetof(Draw_Quad, z); float64 start_seconds = os_get_current_time_in_seconds(); u64 start_cycles = rdtsc(); merge_sort(items, buffer, item_count, item_size, compare_draw_quads); u64 end_cycles = rdtsc(); float64 end_seconds = os_get_current_time_in_seconds(); for (u64 i = 1; i < item_count; i++) { assert(items[i].z >= items[i-1].z, "Failed: not correctly sorted"); } seconds += end_seconds - start_seconds; cycles += end_cycles - start_cycles; } print("Merge sort took on average %llu cycles and %.2f ms\n", cycles / num_samples, (seconds * 1000.0) / (float64)num_samples); } #endif /* OOGABOOGA_HEADLESS */ typedef struct Test_Thing { int foo; float bar; } Test_Thing; void test_growing_array() { Test_Thing *things = 0; growing_array_init((void**)&things, sizeof(Test_Thing), get_heap_allocator()); Test_Thing new_thing; new_thing.foo = 5; new_thing.bar = 420.69; growing_array_add((void**)&things, &new_thing); assert(growing_array_get_valid_count(things) == 1, "Failed: growing_array_get_valid_count"); new_thing.foo = 1; new_thing.bar = 123.45; growing_array_add((void**)&things, &new_thing); assert(growing_array_get_valid_count(things) == 2, "Failed: growing_array_get_valid_count"); assert(things[0].foo == 5 && floats_roughly_match(things[0].bar, 420.69), "Failed: growing_array_add"); assert(things[1].foo == 1 && floats_roughly_match(things[1].bar, 123.45), "Failed: growing_array_add"); growing_array_ordered_remove_by_index((void**)&things, 0); assert(things[0].foo == 1 && floats_roughly_match(things[0].bar, 123.45), "Failed: growing_array_ordered_remove_by_index"); assert(growing_array_get_valid_count(things) == 1, "Failed: growing_array_get_valid_count"); new_thing.foo = 5; new_thing.bar = 420.69; growing_array_add((void**)&things, &new_thing); assert(things[1].foo == 5 && floats_roughly_match(things[1].bar, 420.69), "Failed: growing_array_add"); assert(growing_array_get_valid_count(things) == 2, "Failed: growing_array_get_valid_count"); growing_array_unordered_remove_by_index((void**)&things, 0); assert(things[0].foo == 5 && floats_roughly_match(things[0].bar, 420.69), "Failed: growing_array_unordered_remove_by_index"); assert(growing_array_get_valid_count(things) == 1, "Failed: growing_array_get_valid_count"); for (u32 i = 0; i < 100; i += 1) { new_thing.foo = i; new_thing.bar = i * 4.0; growing_array_add((void**)&things, &new_thing); } assert(growing_array_get_valid_count(things) == 101, "Failed: growing_array_get_valid_count"); // Unordered remove by pointer Test_Thing *thing = &things[50]; Test_Thing copy = *thing; bool found = growing_array_unordered_remove_by_pointer((void**)&things, thing); assert(found, "Failed: growing_array_unordered_remove_by_pointer"); assert(!bytes_match(©, thing, sizeof(Test_Thing)), "Failed: growing_array_unordered_remove_by_pointer"); // Ordered remove by pointer thing = &things[50]; copy = *thing; found = growing_array_ordered_remove_by_pointer((void**)&things, thing); assert(found, "Failed: growing_array_unordered_remove_by_pointer"); assert(!bytes_match(©, thing, sizeof(Test_Thing)), "Failed: growing_array_unordered_remove_by_pointer"); assert(growing_array_get_valid_count(things) == 99, "Failed: growing_array_get_valid_count"); } void oogabooga_run_tests() { print("Testing growing array... "); test_growing_array(); print("OK!\n"); print("Testing allocator... "); test_allocator(true); print("OK!\n"); print("Testing threads... "); test_threads(); print("OK!\n"); print("Testing strings... "); test_strings(); print("OK!\n"); print("Testing file IO... "); test_file_io(); print("OK!\n"); print("Testing linmath... "); test_linmath(); print("OK!\n"); print("Testing intmath... "); test_intmath(); print("OK!\n"); print("Testing simd... "); test_simd(); print("OK!\n"); print("Testing hash table... "); test_hash_table(); print("OK!\n"); print("Testing random distribution... "); test_random_distribution(); print("OK!\n"); print("Testing mutex... "); test_mutex(); print("OK!\n"); #ifndef OOGABOOGA_HEADLESS print("Testing radix sort... "); test_sort(); print("OK!\n"); #endif print("All tests ok!\n"); }