Add Allocator stats, StringBuilder len doesn't include null terminator

This commit is contained in:
doyle 2020-02-23 01:19:59 +11:00
parent 65cbe4b493
commit 2f7ce338a8
2 changed files with 97 additions and 51 deletions

View File

@ -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 <Dqn_usize N> 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 <Dqn_usize N> char *, Dqn_StringBuilder_Build
DQN_HEADER_COPY_PROTOTYPE(template <Dqn_usize N> Dqn_String, Dqn_StringBuilder_BuildString(Dqn_StringBuilder<N> *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 <Dqn_usize N> 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 <Dqn_usize N> void, Dqn_StringBuilder_Append(
buf[len] = 0;
}
DQN_HEADER_COPY_PROTOTYPE(template <Dqn_usize N> void, Dqn_StringBuilder_AppendString(Dqn_StringBuilder<N> *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 <Dqn_usize N> void, Dqn_StringBuilder_AppendChar(Dqn_StringBuilder<N> *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);
}
}
// @ -------------------------------------------------------------------------------------------------

View File

@ -4,19 +4,20 @@
struct TestState
{
Dqn_MemArena arena;
int indent_level;
Dqn_Slice<char> name;
Dqn_Slice<char> fail_expr;
Dqn_Slice<char> 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);
}
}