Fix bugs in making linked buffers

This commit is contained in:
doyle 2019-08-24 13:38:58 +10:00
parent bfd3829259
commit acdf4974f9
2 changed files with 162 additions and 116 deletions

View File

@ -196,8 +196,8 @@ STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char comma, char peri
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
#define DQN_ABS(val) (((val) < 0) ? (-(val)) : (val)) #define DQN_ABS(val) (((val) < 0) ? (-(val)) : (val))
#define DQN_SQUARED(val) ((val) * (val)) #define DQN_SQUARED(val) ((val) * (val))
#define DQN_MIN(a, b) (a < b) ? (a) : (b) #define DQN_MIN(a, b) ((a < b) ? (a) : (b))
#define DQN_MAX(a, b) (a > b) ? (a) : (b) #define DQN_MAX(a, b) ((a > b) ? (a) : (b))
#define DQN_SWAP(a, b) \ #define DQN_SWAP(a, b) \
do \ do \
{ \ { \
@ -573,24 +573,26 @@ struct MemArenaScopedRegion
// NOTE: String Builder // NOTE: String Builder
// //
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
struct StringBuilderBuffer struct Dqn_StringBuilderBuffer
{ {
char *mem; char *mem;
MemSize size; MemSize size;
MemSize used; MemSize used;
StringBuilderBuffer *next; Dqn_StringBuilderBuffer *next;
}; };
struct StringBuilder usize constexpr DQN_STRING_BUILDER_MIN_MEM_BUF_ALLOC_SIZE = DQN_KILOBYTES(4);
template <size_t N = DQN_KILOBYTES(16)>
struct Dqn_StringBuilder
{ {
void *(*my_calloc)(size_t bytes); void *(*my_malloc)(size_t bytes) = malloc; // Set to nullptr to disable heap allocation
void (*my_free) (void *ptr, size_t bytes_to_free); void (*my_free) (void *ptr) = free; // Set to nullptr to disable heap allocation
char fixed_mem[DQN_KILOBYTES(16)]; char fixed_mem[N];
MemSize fixed_mem_used; MemSize fixed_mem_used;
StringBuilderBuffer *next_mem_buf; Dqn_StringBuilderBuffer *next_mem_buf;
StringBuilderBuffer *last_mem_buf; Dqn_StringBuilderBuffer *last_mem_buf;
isize string_len; isize string_len;
}; };
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
@ -702,7 +704,7 @@ char *Asprintf(T *arena, char const *fmt, ...)
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
// //
// NOTE: FixedArray // NOTE: Dqn_FixedArray
// //
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
template <typename T> void EraseStableFromCArray(T *array, isize len, isize max, isize index) template <typename T> void EraseStableFromCArray(T *array, isize len, isize max, isize index)
@ -715,7 +717,7 @@ template <typename T> void EraseStableFromCArray(T *array, isize len, isize max,
} }
#define FIXED_ARRAY_TEMPLATE_DECL template <typename T, int MAX_> #define FIXED_ARRAY_TEMPLATE_DECL template <typename T, int MAX_>
FIXED_ARRAY_TEMPLATE_DECL struct FixedArray FIXED_ARRAY_TEMPLATE_DECL struct Dqn_FixedArray
{ {
T data[MAX_]; T data[MAX_];
isize len; isize len;
@ -729,19 +731,19 @@ FIXED_ARRAY_TEMPLATE_DECL struct FixedArray
T const *operator+ (isize i) const { DQN_ASSERT_MSG(i >= 0 && i < len, "%d >= 0 && %d < %d", i, len); return data + i; } T const *operator+ (isize i) const { DQN_ASSERT_MSG(i >= 0 && i < len, "%d >= 0 && %d < %d", i, len); return data + i; }
T *operator+ (isize i) { DQN_ASSERT_MSG(i >= 0 && i < len, "%d >= 0 && %d < %d", i, len); return data + i; } T *operator+ (isize i) { DQN_ASSERT_MSG(i >= 0 && i < len, "%d >= 0 && %d < %d", i, len); return data + i; }
}; };
FIXED_ARRAY_TEMPLATE_DECL FixedArray<T, MAX_> FixedArray_Init (T const *item, int num) { FixedArray<T, MAX_> result = {}; FixedArray_Add(&result, item, num); return result; } FIXED_ARRAY_TEMPLATE_DECL Dqn_FixedArray<T, MAX_> Dqn_FixedArray_Init (T const *item, int num) { Dqn_FixedArray<T, MAX_> result = {}; Dqn_FixedArray_Add(&result, item, num); return result; }
FIXED_ARRAY_TEMPLATE_DECL T *FixedArray_Add (FixedArray<T, MAX_> *a, T const *items, isize num) { DQN_ASSERT(a->len + num <= MAX_); T *result = static_cast<T *>(Dqn_MemCopy(a->data + a->len, items, sizeof(T) * num)); a->len += num; return result; } FIXED_ARRAY_TEMPLATE_DECL T *Dqn_FixedArray_Add (Dqn_FixedArray<T, MAX_> *a, T const *items, isize num) { DQN_ASSERT(a->len + num <= MAX_); T *result = static_cast<T *>(Dqn_MemCopy(a->data + a->len, items, sizeof(T) * num)); a->len += num; return result; }
FIXED_ARRAY_TEMPLATE_DECL T *FixedArray_Add (FixedArray<T, MAX_> *a, T const item) { DQN_ASSERT(a->len < MAX_); a->data[a->len++] = item; return &a->data[a->len - 1]; } FIXED_ARRAY_TEMPLATE_DECL T *Dqn_FixedArray_Add (Dqn_FixedArray<T, MAX_> *a, T const item) { DQN_ASSERT(a->len < MAX_); a->data[a->len++] = item; return &a->data[a->len - 1]; }
FIXED_ARRAY_TEMPLATE_DECL T *FixedArray_Make (FixedArray<T, MAX_> *a, isize num) { DQN_ASSERT(a->len + num <= MAX_); T *result = a->data + a->len; a->len += num; return result;} FIXED_ARRAY_TEMPLATE_DECL T *Dqn_FixedArray_Make (Dqn_FixedArray<T, MAX_> *a, isize num) { DQN_ASSERT(a->len + num <= MAX_); T *result = a->data + a->len; a->len += num; return result;}
FIXED_ARRAY_TEMPLATE_DECL void FixedArray_Clear (FixedArray<T, MAX_> *a) { a->len = 0; } FIXED_ARRAY_TEMPLATE_DECL void Dqn_FixedArray_Clear (Dqn_FixedArray<T, MAX_> *a) { a->len = 0; }
FIXED_ARRAY_TEMPLATE_DECL void FixedArray_EraseStable (FixedArray<T, MAX_> *a, isize index) { EraseStableFromCArray<T>(a->data, a->len--, a->Max(), index); } FIXED_ARRAY_TEMPLATE_DECL void Dqn_FixedArray_EraseStable (Dqn_FixedArray<T, MAX_> *a, isize index) { EraseStableFromCArray<T>(a->data, a->len--, a->Max(), index); }
FIXED_ARRAY_TEMPLATE_DECL void FixedArray_EraseUnstable(FixedArray<T, MAX_> *a, isize index) { DQN_ASSERT(index >= 0 && index < a->len); if (--a->len == 0) return; a->data[index] = a->data[a->len]; } FIXED_ARRAY_TEMPLATE_DECL void Dqn_FixedArray_EraseUnstable(Dqn_FixedArray<T, MAX_> *a, isize index) { DQN_ASSERT(index >= 0 && index < a->len); if (--a->len == 0) return; a->data[index] = a->data[a->len]; }
FIXED_ARRAY_TEMPLATE_DECL void FixedArray_Pop (FixedArray<T, MAX_> *a, isize num) { DQN_ASSERT(a->len - num >= 0); a->len -= num; } FIXED_ARRAY_TEMPLATE_DECL void Dqn_FixedArray_Pop (Dqn_FixedArray<T, MAX_> *a, isize num) { DQN_ASSERT(a->len - num >= 0); a->len -= num; }
FIXED_ARRAY_TEMPLATE_DECL T *FixedArray_Peek (FixedArray<T, MAX_> *a) { T *result = (a->len == 0) ? nullptr : a->data + (a->len - 1); return result; } FIXED_ARRAY_TEMPLATE_DECL T *Dqn_FixedArray_Peek (Dqn_FixedArray<T, MAX_> *a) { T *result = (a->len == 0) ? nullptr : a->data + (a->len - 1); return result; }
FIXED_ARRAY_TEMPLATE_DECL isize FixedArray_GetIndex (FixedArray<T, MAX_> *a, T const *entry) { isize result = a->end() - entry; return result; } FIXED_ARRAY_TEMPLATE_DECL isize Dqn_FixedArray_GetIndex (Dqn_FixedArray<T, MAX_> *a, T const *entry) { isize result = a->end() - entry; return result; }
template <typename T, int MAX_, typename EqualityProc> template <typename T, int MAX_, typename EqualityProc>
T *FixedArray_Find(FixedArray<T, MAX_> *a, EqualityProc IsEqual) T *Dqn_FixedArray_Find(Dqn_FixedArray<T, MAX_> *a, EqualityProc IsEqual)
{ {
for (T &entry : (*a)) for (T &entry : (*a))
{ {
@ -751,7 +753,7 @@ T *FixedArray_Find(FixedArray<T, MAX_> *a, EqualityProc IsEqual)
return nullptr; return nullptr;
} }
FIXED_ARRAY_TEMPLATE_DECL T *FixedArray_Find(FixedArray<T, MAX_> *a, T *entry) FIXED_ARRAY_TEMPLATE_DECL T *Dqn_FixedArray_Find(Dqn_FixedArray<T, MAX_> *a, T *entry)
{ {
for (T &entry : (*a)) for (T &entry : (*a))
{ {
@ -766,11 +768,11 @@ FIXED_ARRAY_TEMPLATE_DECL T *FixedArray_Find(FixedArray<T, MAX_> *a, T *entry)
// NOTE: FixedStack // NOTE: FixedStack
// //
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
template <typename T, int MAX_> using FixedStack = FixedArray<T, MAX_>; template <typename T, int MAX_> using FixedStack = Dqn_FixedArray<T, MAX_>;
template <typename T, int MAX_> T FixedStack_Pop (FixedStack<T, MAX_> *array) { T result = *FixedArray_Peek(array); FixedArray_Pop(array, 1); return result; } template <typename T, int MAX_> T FixedStack_Pop (FixedStack<T, MAX_> *array) { T result = *Dqn_FixedArray_Peek(array); Dqn_FixedArray_Pop(array, 1); return result; }
template <typename T, int MAX_> T *FixedStack_Peek (FixedStack<T, MAX_> *array) { return FixedArray_Peek(array); } template <typename T, int MAX_> T *FixedStack_Peek (FixedStack<T, MAX_> *array) { return Dqn_FixedArray_Peek(array); }
template <typename T, int MAX_> T *FixedStack_Push (FixedStack<T, MAX_> *array, T item) { return FixedArray_Add(array, item); } template <typename T, int MAX_> T *FixedStack_Push (FixedStack<T, MAX_> *array, T item) { return Dqn_FixedArray_Add(array, item); }
template <typename T, int MAX_> void FixedStack_Clear(FixedStack<T, MAX_> *array) { FixedArray_Clear(array); } template <typename T, int MAX_> void FixedStack_Clear(FixedStack<T, MAX_> *array) { Dqn_FixedArray_Clear(array); }
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
// //
@ -1169,7 +1171,8 @@ MemArenaScopedRegion::~MemArenaScopedRegion()
this->arena->curr_mem_block->used = this->curr_mem_block_used; this->arena->curr_mem_block->used = this->curr_mem_block_used;
} }
FILE_SCOPE char *StringBuilderGetWriteBufferAndIncrementUsage(StringBuilder *builder, usize size_required) template <size_t N>
FILE_SCOPE char *Dqn_StringBuilder__GetWriteBufferAndUpdateUsage(Dqn_StringBuilder<N> *builder, usize size_required)
{ {
char *result = builder->fixed_mem + builder->fixed_mem_used; char *result = builder->fixed_mem + builder->fixed_mem_used;
usize space = ArrayCount(builder->fixed_mem) - builder->fixed_mem_used; usize space = ArrayCount(builder->fixed_mem) - builder->fixed_mem_used;
@ -1177,7 +1180,7 @@ FILE_SCOPE char *StringBuilderGetWriteBufferAndIncrementUsage(StringBuilder *bui
if (builder->last_mem_buf) if (builder->last_mem_buf)
{ {
StringBuilderBuffer *last_buf = builder->last_mem_buf; Dqn_StringBuilderBuffer *last_buf = builder->last_mem_buf;
result = last_buf->mem + last_buf->used; result = last_buf->mem + last_buf->used;
space = last_buf->size - last_buf->used; space = last_buf->size - last_buf->used;
usage = &last_buf->used; usage = &last_buf->used;
@ -1185,10 +1188,15 @@ FILE_SCOPE char *StringBuilderGetWriteBufferAndIncrementUsage(StringBuilder *bui
if (space < size_required) if (space < size_required)
{ {
DQN_ASSERT(builder->my_malloc);
if (!builder->my_malloc)
return nullptr;
// NOTE: Need to allocate new buf // NOTE: Need to allocate new buf
usize allocation_size = sizeof(*builder->last_mem_buf) + DQN_MIN(size_required, ArrayCount(builder->fixed_mem)); usize allocation_size = sizeof(*builder->last_mem_buf) + DQN_MAX(size_required, DQN_STRING_BUILDER_MIN_MEM_BUF_ALLOC_SIZE);
void *memory = builder->my_calloc(allocation_size); void *memory = builder->my_malloc(allocation_size);
auto *new_buf = reinterpret_cast<StringBuilderBuffer *>(memory); auto *new_buf = reinterpret_cast<Dqn_StringBuilderBuffer *>(memory);
*new_buf = {};
new_buf->mem = static_cast<char *>(memory) + sizeof(*new_buf); new_buf->mem = static_cast<char *>(memory) + sizeof(*new_buf);
new_buf->size = allocation_size; new_buf->size = allocation_size;
result = new_buf->mem; result = new_buf->mem;
@ -1217,11 +1225,12 @@ FILE_SCOPE char *StringBuilderGetWriteBufferAndIncrementUsage(StringBuilder *bui
return result; return result;
} }
FILE_SCOPE void StringBuilderBuildOutput(StringBuilder const *builder, char *dest, isize dest_size) template <usize N>
FILE_SCOPE void Dqn_StringBuilder__BuildOutput(Dqn_StringBuilder<N> const *builder, char *dest, isize dest_size)
{ {
// NOTE: No data appended to builder, just allocate am empty string. But // NOTE: No data appended to builder, just allocate am empty string. But
// always allocate, so we avoid adding making nullptr part of the possible // always allocate, so we avoid adding making nullptr part of the possible
// return values and makes using StringBuilder more complex. // return values and makes using Dqn_StringBuilder more complex.
if (dest_size == 1) if (dest_size == 1)
{ {
dest[0] = 0; dest[0] = 0;
@ -1237,12 +1246,12 @@ FILE_SCOPE void StringBuilderBuildOutput(StringBuilder const *builder, char *des
isize remaining_space = end - buf_ptr; isize remaining_space = end - buf_ptr;
DQN_ASSERT(remaining_space >= 0); DQN_ASSERT(remaining_space >= 0);
for (StringBuilderBuffer *string_buf = builder->next_mem_buf; for (Dqn_StringBuilderBuffer *string_buf = builder->next_mem_buf;
string_buf && remaining_space > 0; string_buf && remaining_space > 0;
string_buf = string_buf->next) string_buf = string_buf->next)
{ {
usize bytes_to_copy = DQN_MIN(string_buf->used, (usize)remaining_space); buf_ptr--; // We always copy the null terminator from the buffers, so if we know we have another buffer to copy from, remove the null terminator
memcpy(buf_ptr, string_buf->mem, bytes_to_copy); memcpy(buf_ptr, string_buf->mem, string_buf->used);
buf_ptr += string_buf->used; buf_ptr += string_buf->used;
remaining_space = end - buf_ptr; remaining_space = end - buf_ptr;
@ -1258,68 +1267,75 @@ FILE_SCOPE void StringBuilderBuildOutput(StringBuilder const *builder, char *des
// //
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
// The necessary length to build the string, it returns the length including the null-terminator // The necessary length to build the string, it returns the length including the null-terminator
isize StringBuilder_BuildLen(StringBuilder const *builder) template <usize N>
isize Dqn_StringBuilder_BuildLen(Dqn_StringBuilder<N> const *builder)
{ {
isize result = builder->string_len + 1; isize result = builder->string_len + 1;
return result; return result;
} }
void StringBuilder_BuildInBuffer(StringBuilder const *builder, char *dest, usize dest_size) template <usize N>
void Dqn_StringBuilder_BuildInBuffer(Dqn_StringBuilder<N> const *builder, char *dest, usize dest_size)
{ {
StringBuilderBuildOutput(builder, dest, dest_size); Dqn_StringBuilder__BuildOutput(builder, dest, dest_size);
} }
// len: Return the length of the allocated string including the null-terminator // len: Return the length of the allocated string including the null-terminator
char *StringBuilder_BuildFromMalloc(StringBuilder *builder, isize *len = nullptr) template <usize N>
char *Dqn_StringBuilder_BuildFromMalloc(Dqn_StringBuilder<N> *builder, isize *len = nullptr)
{ {
isize len_w_null_terminator = StringBuilder_BuildLen(builder); isize len_w_null_terminator = Dqn_StringBuilder_BuildLen(builder);
auto *result = static_cast<char *>(malloc(len_w_null_terminator)); auto *result = static_cast<char *>(malloc(len_w_null_terminator));
if (len) *len = len_w_null_terminator; if (len) *len = len_w_null_terminator;
StringBuilderBuildOutput(builder, result, len_w_null_terminator); Dqn_StringBuilder__BuildOutput(builder, result, len_w_null_terminator);
return result; return result;
} }
char *StringBuilder_BuildFromArena(StringBuilder *builder, MemArena *arena, isize *len = nullptr) template <usize N>
char *Dqn_StringBuilder_BuildFromArena(Dqn_StringBuilder<N> *builder, MemArena *arena, isize *len = nullptr)
{ {
isize len_w_null_terminator = StringBuilder_BuildLen(builder); isize len_w_null_terminator = Dqn_StringBuilder_BuildLen(builder);
char *result = MEM_ARENA_ALLOC_ARRAY(arena, char, len_w_null_terminator); char *result = MEM_ARENA_ALLOC_ARRAY(arena, char, len_w_null_terminator);
if (len) *len = len_w_null_terminator; if (len) *len = len_w_null_terminator;
StringBuilderBuildOutput(builder, result, len_w_null_terminator); Dqn_StringBuilder__BuildOutput(builder, result, len_w_null_terminator);
return result; return result;
} }
void StringBuilder_VFmtAppend(StringBuilder *builder, char const *fmt, va_list va) template <usize N>
void Dqn_StringBuilder_VFmtAppend(Dqn_StringBuilder<N> *builder, char const *fmt, va_list va)
{ {
if (!fmt) return; if (!fmt) return;
isize require = stbsp_vsnprintf(nullptr, 0, fmt, va) + 1; isize require = stbsp_vsnprintf(nullptr, 0, fmt, va) + 1;
char *buf = StringBuilderGetWriteBufferAndIncrementUsage(builder, require); char *buf = Dqn_StringBuilder__GetWriteBufferAndUpdateUsage(builder, require);
stbsp_vsnprintf(buf, static_cast<int>(require), fmt, va); stbsp_vsnprintf(buf, static_cast<int>(require), fmt, va);
builder->string_len += (require - 1); // -1 to exclude null terminator builder->string_len += (require - 1); // -1 to exclude null terminator
} }
void StringBuilder_FmtAppend(StringBuilder *builder, char const *fmt, ...) template <usize N>
void Dqn_StringBuilder_FmtAppend(Dqn_StringBuilder<N> *builder, char const *fmt, ...)
{ {
va_list va; va_list va;
va_start(va, fmt); va_start(va, fmt);
StringBuilder_VFmtAppend(builder, fmt, va); Dqn_StringBuilder_VFmtAppend(builder, fmt, va);
va_end(va); va_end(va);
} }
void StringBuilder_Append(StringBuilder *builder, char const *str, isize len = -1) template <usize N>
void Dqn_StringBuilder_Append(Dqn_StringBuilder<N> *builder, char const *str, isize len = -1)
{ {
if (!str) return; if (!str) return;
if (len == -1) len = (isize)strlen(str); if (len == -1) len = (isize)strlen(str);
isize len_w_null_terminator = len + 1; isize len_w_null_terminator = len + 1;
char *buf = StringBuilderGetWriteBufferAndIncrementUsage(builder, len_w_null_terminator); char *buf = Dqn_StringBuilder__GetWriteBufferAndUpdateUsage(builder, len_w_null_terminator);
Dqn_MemCopy(buf, str, len); Dqn_MemCopy(buf, str, len);
builder->string_len += len; builder->string_len += len;
buf[len] = 0; buf[len] = 0;
} }
template <usize N>
void StringBuilder_AppendChar(StringBuilder *builder, char ch) void Dqn_StringBuilder_AppendChar(Dqn_StringBuilder<N> *builder, char ch)
{ {
char *buf = StringBuilderGetWriteBufferAndIncrementUsage(builder, 1 + 1 /*null terminator*/); char *buf = Dqn_StringBuilder__GetWriteBufferAndUpdateUsage(builder, 1 + 1 /*null terminator*/);
*buf++ = ch; *buf++ = ch;
builder->string_len++; builder->string_len++;
buf[1] = 0; buf[1] = 0;

View File

@ -103,23 +103,23 @@ void TestState_PrintResult(TestState const *result)
FILE_SCOPE void UnitTests() FILE_SCOPE void UnitTests()
{ {
// ---------------------------------------------------------------------------------------------
//
// NOTE: StringBuilder
//
// ---------------------------------------------------------------------------------------------
TestingState testing_state = {}; TestingState testing_state = {};
// ---------------------------------------------------------------------------------------------
//
// NOTE: Dqn_StringBuilder
//
// ---------------------------------------------------------------------------------------------
{ {
TEST_DECLARE_GROUP_SCOPED(testing_state, "StringBuilder"); TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_StringBuilder");
// NOTE: StringBuilder_Append // 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 malloc");
StringBuilder builder = {}; Dqn_StringBuilder<> builder = {};
StringBuilder_Append(&builder, "Abc", 1); Dqn_StringBuilder_Append(&builder, "Abc", 1);
StringBuilder_Append(&builder, "cd"); Dqn_StringBuilder_Append(&builder, "cd");
isize len = 0; isize len = 0;
char *result = StringBuilder_BuildFromMalloc(&builder, &len); char *result = Dqn_StringBuilder_BuildFromMalloc(&builder, &len);
DQN_DEFER { free(result); }; DQN_DEFER { free(result); };
char constexpr EXPECT_STR[] = "Acd"; char constexpr EXPECT_STR[] = "Acd";
@ -129,11 +129,11 @@ FILE_SCOPE void UnitTests()
{ {
TEST_START_SCOPE(testing_state, "Append empty string and build using malloc"); TEST_START_SCOPE(testing_state, "Append empty string and build using malloc");
StringBuilder builder = {}; Dqn_StringBuilder<> builder = {};
StringBuilder_Append(&builder, ""); Dqn_StringBuilder_Append(&builder, "");
StringBuilder_Append(&builder, ""); Dqn_StringBuilder_Append(&builder, "");
isize len = 0; isize len = 0;
char *result = StringBuilder_BuildFromMalloc(&builder, &len); char *result = Dqn_StringBuilder_BuildFromMalloc(&builder, &len);
DQN_DEFER { free(result); }; DQN_DEFER { free(result); };
char constexpr EXPECT_STR[] = ""; char constexpr EXPECT_STR[] = "";
@ -143,11 +143,11 @@ FILE_SCOPE void UnitTests()
{ {
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 malloc");
StringBuilder builder = {}; Dqn_StringBuilder<> builder = {};
StringBuilder_Append(&builder, "Acd"); Dqn_StringBuilder_Append(&builder, "Acd");
StringBuilder_Append(&builder, ""); Dqn_StringBuilder_Append(&builder, "");
isize len = 0; isize len = 0;
char *result = StringBuilder_BuildFromMalloc(&builder, &len); char *result = Dqn_StringBuilder_BuildFromMalloc(&builder, &len);
DQN_DEFER { free(result); }; DQN_DEFER { free(result); };
char constexpr EXPECT_STR[] = "Acd"; char constexpr EXPECT_STR[] = "Acd";
@ -157,27 +157,42 @@ FILE_SCOPE void UnitTests()
{ {
TEST_START_SCOPE(testing_state, "Append nullptr and build using malloc"); TEST_START_SCOPE(testing_state, "Append nullptr and build using malloc");
StringBuilder builder = {}; Dqn_StringBuilder<> builder = {};
StringBuilder_Append(&builder, nullptr, 5); Dqn_StringBuilder_Append(&builder, nullptr, 5);
isize len = 0; isize len = 0;
char *result = StringBuilder_BuildFromMalloc(&builder, &len); char *result = Dqn_StringBuilder_BuildFromMalloc(&builder, &len);
DQN_DEFER { free(result); }; DQN_DEFER { free(result); };
char constexpr EXPECT_STR[] = ""; char constexpr EXPECT_STR[] = "";
TEST_EXPECT_MSG(testing_state, len == CharCountI(EXPECT_STR) + 1 /*null terminator*/, "len: %zd", len); TEST_EXPECT_MSG(testing_state, len == CharCountI(EXPECT_STR) + 1 /*null terminator*/, "len: %zd", len);
TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, len) == 0, "result: %s", result); 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");
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 constexpr EXPECT_STR[] = "Aztec";
TEST_EXPECT_MSG(testing_state, len == CharCountI(EXPECT_STR) + 1 /*null terminator*/, "len: %zd", len);
TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, len) == 0, "result: %s", result);
}
} }
// NOTE: StringBuilder_AppendChar // 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 malloc");
StringBuilder builder = {}; Dqn_StringBuilder<> builder = {};
StringBuilder_AppendChar(&builder, 'a'); Dqn_StringBuilder_AppendChar(&builder, 'a');
StringBuilder_AppendChar(&builder, 'b'); Dqn_StringBuilder_AppendChar(&builder, 'b');
isize len = 0; isize len = 0;
char *result = StringBuilder_BuildFromMalloc(&builder, &len); char *result = Dqn_StringBuilder_BuildFromMalloc(&builder, &len);
DQN_DEFER { free(result); }; DQN_DEFER { free(result); };
char constexpr EXPECT_STR[] = "ab"; char constexpr EXPECT_STR[] = "ab";
@ -185,79 +200,94 @@ FILE_SCOPE void UnitTests()
TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, len) == 0, "result: %s", result); TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, len) == 0, "result: %s", result);
} }
// NOTE: StringBuilder_FmtAppend // NOTE: Dqn_StringBuilder_FmtAppend
{ {
TEST_START_SCOPE(testing_state, "Append format string and build using malloc"); {
StringBuilder builder = {}; TEST_START_SCOPE(testing_state, "Append format string and build using malloc");
StringBuilder_FmtAppend(&builder, "Number: %d, String: %s, ", 4, "Hello Sailor"); Dqn_StringBuilder<> builder = {};
StringBuilder_FmtAppend(&builder, "Extra Stuff"); Dqn_StringBuilder_FmtAppend(&builder, "Number: %d, String: %s, ", 4, "Hello Sailor");
isize len = 0; Dqn_StringBuilder_FmtAppend(&builder, "Extra Stuff");
char *result = StringBuilder_BuildFromMalloc(&builder, &len); isize len = 0;
DQN_DEFER { free(result); }; char *result = Dqn_StringBuilder_BuildFromMalloc(&builder, &len);
DQN_DEFER { free(result); };
char constexpr EXPECT_STR[] = "Number: 4, String: Hello Sailor, Extra Stuff"; char constexpr EXPECT_STR[] = "Number: 4, String: Hello Sailor, Extra Stuff";
TEST_EXPECT_MSG(testing_state, len == CharCountI(EXPECT_STR) + 1 /*null terminator*/, "len: %zd", len); TEST_EXPECT_MSG(testing_state, len == CharCountI(EXPECT_STR) + 1 /*null terminator*/, "len: %zd", len);
TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, len) == 0, "result: %s", result); 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");
Dqn_StringBuilder<> builder = {};
Dqn_StringBuilder_FmtAppend(&builder, nullptr);
isize len = 0;
char *result = Dqn_StringBuilder_BuildFromMalloc(&builder, &len);
DQN_DEFER { free(result); };
char constexpr EXPECT_STR[] = "";
TEST_EXPECT_MSG(testing_state, len == CharCountI(EXPECT_STR) + 1 /*null terminator*/, "len: %zd", len);
TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, len) == 0, "result: %s", result);
}
} }
} }
// --------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------
// //
// NOTE: FixedArray // NOTE: Dqn_FixedArray
// //
// --------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------
{ {
TEST_DECLARE_GROUP_SCOPED(testing_state, "FixedArray"); TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_FixedArray");
// NOTE: FixedArray_Init // NOTE: Dqn_FixedArray_Init
{ {
TEST_START_SCOPE(testing_state, "Initialise from raw array"); TEST_START_SCOPE(testing_state, "Initialise from raw array");
int raw_array[] = {1, 2}; int raw_array[] = {1, 2};
auto array = FixedArray_Init<int, 4>(raw_array, (int)ArrayCount(raw_array)); auto array = Dqn_FixedArray_Init<int, 4>(raw_array, (int)ArrayCount(raw_array));
TEST_EXPECT(testing_state, array.len == 2); TEST_EXPECT(testing_state, array.len == 2);
TEST_EXPECT(testing_state, array[0] == 1); TEST_EXPECT(testing_state, array[0] == 1);
TEST_EXPECT(testing_state, array[1] == 2); TEST_EXPECT(testing_state, array[1] == 2);
} }
// NOTE: FixedArray_EraseStable // NOTE: Dqn_FixedArray_EraseStable
{ {
TEST_START_SCOPE(testing_state, "Erase stable 1 element from array"); TEST_START_SCOPE(testing_state, "Erase stable 1 element from array");
int raw_array[] = {1, 2, 3}; int raw_array[] = {1, 2, 3};
auto array = FixedArray_Init<int, 4>(raw_array, (int)ArrayCount(raw_array)); auto array = Dqn_FixedArray_Init<int, 4>(raw_array, (int)ArrayCount(raw_array));
FixedArray_EraseStable(&array, 1); Dqn_FixedArray_EraseStable(&array, 1);
TEST_EXPECT(testing_state, array.len == 2); TEST_EXPECT(testing_state, array.len == 2);
TEST_EXPECT(testing_state, array[0] == 1); TEST_EXPECT(testing_state, array[0] == 1);
TEST_EXPECT(testing_state, array[1] == 3); TEST_EXPECT(testing_state, array[1] == 3);
} }
// NOTE: FixedArray_EraseUnstable // NOTE: Dqn_FixedArray_EraseUnstable
{ {
TEST_START_SCOPE(testing_state, "Erase unstable 1 element from array"); TEST_START_SCOPE(testing_state, "Erase unstable 1 element from array");
int raw_array[] = {1, 2, 3}; int raw_array[] = {1, 2, 3};
auto array = FixedArray_Init<int, 4>(raw_array, (int)ArrayCount(raw_array)); auto array = Dqn_FixedArray_Init<int, 4>(raw_array, (int)ArrayCount(raw_array));
FixedArray_EraseUnstable(&array, 0); Dqn_FixedArray_EraseUnstable(&array, 0);
TEST_EXPECT(testing_state, array.len == 2); TEST_EXPECT(testing_state, array.len == 2);
TEST_EXPECT(testing_state, array[0] == 3); TEST_EXPECT(testing_state, array[0] == 3);
TEST_EXPECT(testing_state, array[1] == 2); TEST_EXPECT(testing_state, array[1] == 2);
} }
// NOTE: FixedArray_Add // NOTE: Dqn_FixedArray_Add
{ {
TEST_START_SCOPE(testing_state, "Add 1 element to array"); TEST_START_SCOPE(testing_state, "Add 1 element to array");
int const ITEM = 2; int const ITEM = 2;
int raw_array[] = {1}; int raw_array[] = {1};
auto array = FixedArray_Init<int, 4>(raw_array, (int)ArrayCount(raw_array)); auto array = Dqn_FixedArray_Init<int, 4>(raw_array, (int)ArrayCount(raw_array));
FixedArray_Add(&array, ITEM); Dqn_FixedArray_Add(&array, ITEM);
TEST_EXPECT(testing_state, array.len == 2); TEST_EXPECT(testing_state, array.len == 2);
TEST_EXPECT(testing_state, array[0] == 1); TEST_EXPECT(testing_state, array[0] == 1);
TEST_EXPECT(testing_state, array[1] == ITEM); TEST_EXPECT(testing_state, array[1] == ITEM);
} }
// NOTE: FixedArray_Clear // NOTE: Dqn_FixedArray_Clear
{ {
TEST_START_SCOPE(testing_state, "Clear array"); TEST_START_SCOPE(testing_state, "Clear array");
int raw_array[] = {1}; int raw_array[] = {1};
auto array = FixedArray_Init<int, 4>(raw_array, (int)ArrayCount(raw_array)); auto array = Dqn_FixedArray_Init<int, 4>(raw_array, (int)ArrayCount(raw_array));
FixedArray_Clear(&array); Dqn_FixedArray_Clear(&array);
TEST_EXPECT(testing_state, array.len == 0); TEST_EXPECT(testing_state, array.len == 0);
} }
} }