diff --git a/Code/Dqn.h b/Code/Dqn.h index 11eae74..3bc7c74 100644 --- a/Code/Dqn.h +++ b/Code/Dqn.h @@ -613,6 +613,12 @@ struct Dqn_Allocator Dqn_Allocator_Type type; void *data; + isize bytes_allocated; + isize total_bytes_allocated; + + isize allocations; + isize total_allocations; + // NOTE: Only required if type == Dqn_Allocator_Type::Custom Dqn_Allocator_AllocateProc *allocate; Dqn_Allocator_ReallocProc *realloc; @@ -718,7 +724,13 @@ DQN_HEADER_COPY_PROTOTYPE(Dqn_Allocator, inline Dqn_Allocator_Arena(Dqn_MemArena DQN_HEADER_COPY_BEGIN struct Dqn_String { - char *str; + union { + // NOTE: To appease GCC, Clang can't assign C string literal to char * + // Only UB if you try modify a string originally declared const + char const *str_; + char *str; + }; + Dqn_isize len; char const *begin() const { return str; } char const *end () const { return str + len; } @@ -855,7 +867,7 @@ DQN_HEADER_COPY_PROTOTYPE(template char *, Dqn_StringBuilder_Build { Dqn_isize len_w_null_terminator = Dqn_StringBuilder_BuildLen(builder); auto *result = DQN_CAST(char *)Dqn_Allocator_Allocate(allocator, sizeof(char) * len_w_null_terminator); - if (len) *len = len_w_null_terminator; + if (len) *len = (len_w_null_terminator - 1); Dqn_StringBuilder__BuildOutput(builder, result, len_w_null_terminator); return result; } @@ -863,7 +875,7 @@ DQN_HEADER_COPY_PROTOTYPE(template char *, Dqn_StringBuilder_Build DQN_HEADER_COPY_PROTOTYPE(template Dqn_String, Dqn_StringBuilder_BuildString(Dqn_StringBuilder *builder, Dqn_Allocator *allocator)) { Dqn_String result = {}; - result.str = Dqn_StringBuilder_Build(builder, allocator, &result.len); + result.str = Dqn_StringBuilder_Build(builder, allocator, &result.len); return result; } @@ -888,6 +900,8 @@ DQN_HEADER_COPY_PROTOTYPE(template void, Dqn_StringBuilder_Append( { if (!str) return; if (len == -1) len = DQN_CAST(Dqn_isize)strlen(str); + if (len == 0) return; + Dqn_isize len_w_null_terminator = len + 1; char *buf = Dqn_StringBuilder__GetWriteBufferAndUpdateUsage(builder, len_w_null_terminator); memcpy(buf, str, len); @@ -895,6 +909,16 @@ DQN_HEADER_COPY_PROTOTYPE(template void, Dqn_StringBuilder_Append( buf[len] = 0; } +DQN_HEADER_COPY_PROTOTYPE(template void, Dqn_StringBuilder_AppendString(Dqn_StringBuilder *builder, Dqn_String const string)) +{ + if (!string.str || string.len == 0) return; + Dqn_isize len_w_null_terminator = string.len + 1; + char *buf = Dqn_StringBuilder__GetWriteBufferAndUpdateUsage(builder, len_w_null_terminator); + memcpy(buf, string.str, string.len); + builder->string_len += string.len; + buf[string.len] = 0; +} + DQN_HEADER_COPY_PROTOTYPE(template void, Dqn_StringBuilder_AppendChar(Dqn_StringBuilder *builder, char ch)) { char *buf = Dqn_StringBuilder__GetWriteBufferAndUpdateUsage(builder, 1 + 1 /*null terminator*/); @@ -1459,6 +1483,11 @@ DQN_HEADER_COPY_PROTOTYPE(void *, Dqn_Allocator_Allocate(Dqn_Allocator *allocato break; } + if (result) + { + allocator->allocations++; + allocator->total_bytes_allocated += size; + } return result; } @@ -1466,6 +1495,7 @@ DQN_HEADER_COPY_PROTOTYPE(void *, Dqn_Allocator_Realloc(Dqn_Allocator *allocator { DQN_IF_ASSERT(old_size >= 0) old_size = 0; DQN_IF_ASSERT(new_size >= 0) new_size = 0; + DQN_ASSERT(new_size > old_size); void *result = nullptr; switch (allocator->type) @@ -1503,6 +1533,11 @@ DQN_HEADER_COPY_PROTOTYPE(void *, Dqn_Allocator_Realloc(Dqn_Allocator *allocator break; } + if (result) + { + allocator->total_bytes_allocated += new_size; + allocator->total_allocations++; + } return result; } @@ -1529,6 +1564,13 @@ DQN_HEADER_COPY_PROTOTYPE(void, Dqn_Allocator_Free(Dqn_Allocator *allocator, voi case Dqn_Allocator_Type::Arena: break; } + + if (ptr) + { + allocator->total_allocations++; + allocator->allocations--; + DQN_ASSERT(allocator->allocations >= 0); + } } // @ ------------------------------------------------------------------------------------------------- diff --git a/Code/Dqn_UnitTests.cpp b/Code/Dqn_UnitTests.cpp index a9fd70d..0714621 100644 --- a/Code/Dqn_UnitTests.cpp +++ b/Code/Dqn_UnitTests.cpp @@ -4,19 +4,20 @@ struct TestState { - Dqn_MemArena arena; - int indent_level; - Dqn_Slice name; - Dqn_Slice fail_expr; - Dqn_Slice fail_msg; - bool scope_started; + int indent_level; + Dqn_String name; + Dqn_String fail_expr; + Dqn_String fail_msg; + bool scope_started; }; struct TestingState { - int num_tests_in_group; - int num_tests_ok_in_group; - TestState test; + int num_tests_in_group; + int num_tests_ok_in_group; + TestState test; + Dqn_MemArena arena_; + Dqn_Allocator allocator; }; #define ANSI_COLOR_RED "\x1b[31m" @@ -32,9 +33,11 @@ struct TestingState { \ if (testing_state.test.fail_expr.len == 0) testing_state.num_tests_ok_in_group++; \ TestState_PrintResult(&testing_state.test); \ - testing_state.test = {}; \ + Dqn_MemArena_ResetUsage(&testing_state.arena_, Dqn_ZeroMem::No); \ + testing_state.allocator = Dqn_Allocator_Arena(&testing_state.arena_); \ + testing_state.test = {}; \ }; \ - testing_state.test.name = Dqn_AsprintfSlice(&testing_state.test.arena, test_name); \ + testing_state.test.name = Dqn_Asprintf(&testing_state.allocator, test_name); \ testing_state.test.scope_started = true; \ testing_state.num_tests_in_group++ @@ -50,8 +53,8 @@ struct TestingState DQN_ASSERT(testing_state.test.scope_started); \ if (!(expr)) \ { \ - testing_state.test.fail_expr = Dqn_AsprintfSlice(&testing_state.test.arena, #expr); \ - testing_state.test.fail_msg = Dqn_AsprintfSlice(&testing_state.test.arena, msg, ##__VA_ARGS__); \ + testing_state.test.fail_expr = Dqn_Asprintf(&testing_state.allocator, #expr); \ + testing_state.test.fail_msg = Dqn_Asprintf(&testing_state.allocator, msg, ##__VA_ARGS__); \ } #define TEST_EXPECT(testing_state, expr) TEST_EXPECT_MSG(testing_state, expr, "") @@ -115,7 +118,7 @@ FILE_SCOPE void UnitTests() TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Allocator"); { TEST_START_SCOPE(testing_state, "HeapAllocator - Allocate Small"); - Dqn_Allocator allocator = Dqn_Allocator_HeapAllocator(); + Dqn_Allocator allocator = Dqn_Allocator_Heap(); char constexpr EXPECT[] = "hello_world"; char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, Dqn_ArrayCount(EXPECT)); DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; @@ -125,7 +128,7 @@ FILE_SCOPE void UnitTests() { TEST_START_SCOPE(testing_state, "XHeapAllocator - Allocate Small"); - Dqn_Allocator allocator = Dqn_Allocator_XHeapAllocator(); + Dqn_Allocator allocator = Dqn_Allocator_XHeap(); char constexpr EXPECT[] = "hello_world"; char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, Dqn_ArrayCount(EXPECT)); DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; @@ -136,7 +139,7 @@ FILE_SCOPE void UnitTests() { TEST_START_SCOPE(testing_state, "ArenaAllocator - Allocate Small"); Dqn_MemArena arena = {}; - Dqn_Allocator allocator = Dqn_Allocator_ArenaAllocator(&arena); + Dqn_Allocator allocator = Dqn_Allocator_Arena(&arena); char constexpr EXPECT[] = "hello_world"; char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, Dqn_ArrayCount(EXPECT)); DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; @@ -412,120 +415,121 @@ FILE_SCOPE void UnitTests() // --------------------------------------------------------------------------------------------- { TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_StringBuilder"); + Dqn_Allocator allocator = Dqn_Allocator_Heap(); // NOTE: Dqn_StringBuilder_Append { { - TEST_START_SCOPE(testing_state, "Append variable length strings and build using malloc"); + TEST_START_SCOPE(testing_state, "Append variable length strings and build using heap allocator"); Dqn_StringBuilder<> builder = {}; Dqn_StringBuilder_Append(&builder, "Abc", 1); Dqn_StringBuilder_Append(&builder, "cd"); isize len = 0; - char *result = Dqn_StringBuilder_BuildFromMalloc(&builder, &len); - DQN_DEFER { free(result); }; + char *result = Dqn_StringBuilder_Build(&builder, &allocator, &len); + DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; char constexpr EXPECT_STR[] = "Acd"; - TEST_EXPECT_MSG(testing_state, len == Dqn_CharCountI(EXPECT_STR) + 1 /*null terminator*/, "len: %zd", len); + TEST_EXPECT_MSG(testing_state, len == Dqn_CharCountI(EXPECT_STR), "len: %zd", len); TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, len) == 0, "result: %s", result); } { - TEST_START_SCOPE(testing_state, "Append empty string and build using malloc"); + TEST_START_SCOPE(testing_state, "Append empty string and build using heap allocator"); Dqn_StringBuilder<> builder = {}; Dqn_StringBuilder_Append(&builder, ""); Dqn_StringBuilder_Append(&builder, ""); isize len = 0; - char *result = Dqn_StringBuilder_BuildFromMalloc(&builder, &len); - DQN_DEFER { free(result); }; + char *result = Dqn_StringBuilder_Build(&builder, &allocator, &len); + DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; char constexpr EXPECT_STR[] = ""; - TEST_EXPECT_MSG(testing_state, len == Dqn_CharCountI(EXPECT_STR) + 1 /*null terminator*/, "len: %zd", len); + TEST_EXPECT_MSG(testing_state, len == Dqn_CharCountI(EXPECT_STR), "len: %zd", len); TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, len) == 0, "result: %s", result); } { - TEST_START_SCOPE(testing_state, "Append empty string onto string and build using malloc"); + TEST_START_SCOPE(testing_state, "Append empty string onto string and build using heap allocator"); Dqn_StringBuilder<> builder = {}; Dqn_StringBuilder_Append(&builder, "Acd"); Dqn_StringBuilder_Append(&builder, ""); isize len = 0; - char *result = Dqn_StringBuilder_BuildFromMalloc(&builder, &len); - DQN_DEFER { free(result); }; + char *result = Dqn_StringBuilder_Build(&builder, &allocator, &len); + DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; char constexpr EXPECT_STR[] = "Acd"; - TEST_EXPECT_MSG(testing_state, len == Dqn_CharCountI(EXPECT_STR) + 1 /*null terminator*/, "len: %zd", len); + TEST_EXPECT_MSG(testing_state, len == Dqn_CharCountI(EXPECT_STR), "len: %zd", len); TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, len) == 0, "result: %s", result); } { - TEST_START_SCOPE(testing_state, "Append nullptr and build using malloc"); + TEST_START_SCOPE(testing_state, "Append nullptr and build using heap allocator"); Dqn_StringBuilder<> builder = {}; Dqn_StringBuilder_Append(&builder, nullptr, 5); isize len = 0; - char *result = Dqn_StringBuilder_BuildFromMalloc(&builder, &len); - DQN_DEFER { free(result); }; + char *result = Dqn_StringBuilder_Build(&builder, &allocator, &len); + DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; char constexpr EXPECT_STR[] = ""; - TEST_EXPECT_MSG(testing_state, len == Dqn_CharCountI(EXPECT_STR) + 1 /*null terminator*/, "len: %zd", len); + TEST_EXPECT_MSG(testing_state, len == Dqn_CharCountI(EXPECT_STR), "len: %zd", len); TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, len) == 0, "result: %s", result); } { - TEST_START_SCOPE(testing_state, "Append and require new linked buffer and build using malloc"); + TEST_START_SCOPE(testing_state, "Append and require new linked buffer and build using heap allocator"); Dqn_StringBuilder<2> builder = {}; Dqn_StringBuilder_Append(&builder, "A"); Dqn_StringBuilder_Append(&builder, "z"); // Should force a new memory block Dqn_StringBuilder_Append(&builder, "tec"); isize len = 0; - char *result = Dqn_StringBuilder_BuildFromMalloc(&builder, &len); - DQN_DEFER { free(result); }; + char *result = Dqn_StringBuilder_Build(&builder, &allocator, &len); + DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; char constexpr EXPECT_STR[] = "Aztec"; - TEST_EXPECT_MSG(testing_state, len == Dqn_CharCountI(EXPECT_STR) + 1 /*null terminator*/, "len: %zd", len); + TEST_EXPECT_MSG(testing_state, len == Dqn_CharCountI(EXPECT_STR), "len: %zd", len); TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, len) == 0, "result: %s", result); } } // NOTE: Dqn_StringBuilder_AppendChar { - TEST_START_SCOPE(testing_state, "Append char and build using malloc"); + TEST_START_SCOPE(testing_state, "Append char and build using heap allocator"); Dqn_StringBuilder<> builder = {}; Dqn_StringBuilder_AppendChar(&builder, 'a'); Dqn_StringBuilder_AppendChar(&builder, 'b'); isize len = 0; - char *result = Dqn_StringBuilder_BuildFromMalloc(&builder, &len); - DQN_DEFER { free(result); }; + char *result = Dqn_StringBuilder_Build(&builder, &allocator, &len); + DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; char constexpr EXPECT_STR[] = "ab"; - TEST_EXPECT_MSG(testing_state, len == Dqn_CharCountI(EXPECT_STR) + 1 /*null terminator*/, "len: %zd", len); + TEST_EXPECT_MSG(testing_state, len == Dqn_CharCountI(EXPECT_STR), "len: %zd", len); TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, len) == 0, "result: %s", result); } // NOTE: Dqn_StringBuilder_FmtAppend { { - TEST_START_SCOPE(testing_state, "Append format string and build using malloc"); + TEST_START_SCOPE(testing_state, "Append format string and build using heap allocator"); Dqn_StringBuilder<> builder = {}; Dqn_StringBuilder_FmtAppend(&builder, "Number: %d, String: %s, ", 4, "Hello Sailor"); Dqn_StringBuilder_FmtAppend(&builder, "Extra Stuff"); isize len = 0; - char *result = Dqn_StringBuilder_BuildFromMalloc(&builder, &len); - DQN_DEFER { free(result); }; + char *result = Dqn_StringBuilder_Build(&builder, &allocator, &len); + DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; char constexpr EXPECT_STR[] = "Number: 4, String: Hello Sailor, Extra Stuff"; - TEST_EXPECT_MSG(testing_state, len == Dqn_CharCountI(EXPECT_STR) + 1 /*null terminator*/, "len: %zd", len); + TEST_EXPECT_MSG(testing_state, len == Dqn_CharCountI(EXPECT_STR), "len: %zd", len); TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, len) == 0, "result: %s", result); } { - TEST_START_SCOPE(testing_state, "Append nullptr format string and build using malloc"); + TEST_START_SCOPE(testing_state, "Append nullptr format string and build using heap allocator"); Dqn_StringBuilder<> builder = {}; Dqn_StringBuilder_FmtAppend(&builder, nullptr); isize len = 0; - char *result = Dqn_StringBuilder_BuildFromMalloc(&builder, &len); - DQN_DEFER { free(result); }; + char *result = Dqn_StringBuilder_Build(&builder, &allocator, &len); + DQN_DEFER { Dqn_Allocator_Free(&allocator, result); }; char constexpr EXPECT_STR[] = ""; - TEST_EXPECT_MSG(testing_state, len == Dqn_CharCountI(EXPECT_STR) + 1 /*null terminator*/, "len: %zd", len); + TEST_EXPECT_MSG(testing_state, len == Dqn_CharCountI(EXPECT_STR), "len: %zd", len); TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, len) == 0, "result: %s", result); } }