From 95272e77eefeb5c423d02644c0068191b18e950a Mon Sep 17 00:00:00 2001 From: doyle Date: Sat, 24 Aug 2019 15:43:14 +1000 Subject: [PATCH] Write PoC simpler header generator code --- Code/Dqn.h | 345 +++++++++++++++++++++-------------------- Code/Dqn_UnitTests.cpp | 169 ++++++++++++++++++++ 2 files changed, 343 insertions(+), 171 deletions(-) diff --git a/Code/Dqn.h b/Code/Dqn.h index 268f9b9..d9414ea 100644 --- a/Code/Dqn.h +++ b/Code/Dqn.h @@ -1,6 +1,7 @@ #if defined(DQN_IMPLEMENTATION) #define STB_SPRINTF_IMPLEMENTATION #endif + // ------------------------------------------------------------------------------------------------- // // NOTE: stb_sprintf @@ -595,6 +596,179 @@ struct Dqn_StringBuilder isize string_len; }; +template +FILE_SCOPE char *Dqn_StringBuilder__GetWriteBufferAndUpdateUsage(Dqn_StringBuilder *builder, usize size_required) +{ + char *result = builder->fixed_mem + builder->fixed_mem_used; + usize space = Dqn_ArrayCount(builder->fixed_mem) - builder->fixed_mem_used; + usize *usage = &builder->fixed_mem_used; + + if (builder->last_mem_buf) + { + Dqn_StringBuilderBuffer *last_buf = builder->last_mem_buf; + result = last_buf->mem + last_buf->used; + space = last_buf->size - last_buf->used; + usage = &last_buf->used; + } + + if (space < size_required) + { + DQN_ASSERT(builder->my_malloc); + if (!builder->my_malloc) + return nullptr; + + // NOTE: Need to allocate new buf + usize allocation_size = sizeof(*builder->last_mem_buf) + DQN_MAX(size_required, DQN_STRING_BUILDER_MIN_MEM_BUF_ALLOC_SIZE); + void *memory = builder->my_malloc(allocation_size); + auto *new_buf = reinterpret_cast(memory); + *new_buf = {}; + new_buf->mem = static_cast(memory) + sizeof(*new_buf); + new_buf->size = allocation_size; + result = new_buf->mem; + usage = &new_buf->used; + + if (builder->last_mem_buf) + { + builder->last_mem_buf->next = new_buf; + } + else + { + builder->next_mem_buf = new_buf; + builder->last_mem_buf = new_buf; + } + } + + if (size_required > 0 && *usage > 0 && result[-1] == 0) + { + // NOTE: Not first time writing into buffer using sprintf, sprintf always writes a null terminator, so we must + // subtract one + (*usage)--; + result--; + } + + *usage += size_required; + return result; +} + +template +FILE_SCOPE void Dqn_StringBuilder__BuildOutput(Dqn_StringBuilder const *builder, char *dest, isize dest_size) +{ + // NOTE: No data appended to builder, just allocate am empty string. But + // always allocate, so we avoid adding making nullptr part of the possible + // return values and makes using Dqn_StringBuilder more complex. + if (dest_size == 1) + { + dest[0] = 0; + return; + } + + char const *end = dest + dest_size; + char *buf_ptr = dest; + + memcpy(buf_ptr, builder->fixed_mem, builder->fixed_mem_used); + buf_ptr += builder->fixed_mem_used; + + isize remaining_space = end - buf_ptr; + DQN_ASSERT(remaining_space >= 0); + + for (Dqn_StringBuilderBuffer *string_buf = builder->next_mem_buf; + string_buf && remaining_space > 0; + string_buf = string_buf->next) + { + 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, string_buf->used); + buf_ptr += string_buf->used; + + remaining_space = end - buf_ptr; + DQN_ASSERT(remaining_space >= 0); + } + DQN_ASSERT(buf_ptr == dest + dest_size); +} + +// ------------------------------------------------------------------------------------------------- +// +// NOTE: String Builder +// +// ------------------------------------------------------------------------------------------------- +DQN_HEADER_COPY_PROTOTYPE_AND_COMMENT( +"The necessary length to build the string, it returns the length including the null-terminator", +template isize, +Dqn_StringBuilder_BuildLen(Dqn_StringBuilder const *builder)) +{ + isize result = builder->string_len + 1; + return result; +} + +DQN_HEADER_COPY_PROTOTYPE( +template void, +Dqn_StringBuilder_BuildInBuffer(Dqn_StringBuilder const *builder, char *dest, usize dest_size)) +{ + Dqn_StringBuilder__BuildOutput(builder, dest, dest_size); +} + +// len: Return the length of the allocated string including the null-terminator +template +char *Dqn_StringBuilder_BuildFromMalloc(Dqn_StringBuilder *builder, isize *len = nullptr) +{ + isize len_w_null_terminator = Dqn_StringBuilder_BuildLen(builder); + auto *result = static_cast(malloc(len_w_null_terminator)); + if (len) *len = len_w_null_terminator; + Dqn_StringBuilder__BuildOutput(builder, result, len_w_null_terminator); + return result; +} + +template +char *Dqn_StringBuilder_BuildFromArena(Dqn_StringBuilder *builder, Dqn_MemArena *arena, isize *len = nullptr) +{ + isize len_w_null_terminator = Dqn_StringBuilder_BuildLen(builder); + char *result = MEM_ARENA_ALLOC_ARRAY(arena, char, len_w_null_terminator); + if (len) *len = len_w_null_terminator; + Dqn_StringBuilder__BuildOutput(builder, result, len_w_null_terminator); + return result; +} + +template +void Dqn_StringBuilder_VFmtAppend(Dqn_StringBuilder *builder, char const *fmt, va_list va) +{ + if (!fmt) return; + isize require = stbsp_vsnprintf(nullptr, 0, fmt, va) + 1; + char *buf = Dqn_StringBuilder__GetWriteBufferAndUpdateUsage(builder, require); + stbsp_vsnprintf(buf, static_cast(require), fmt, va); + builder->string_len += (require - 1); // -1 to exclude null terminator +} + +template +void Dqn_StringBuilder_FmtAppend(Dqn_StringBuilder *builder, char const *fmt, ...) +{ + va_list va; + va_start(va, fmt); + Dqn_StringBuilder_VFmtAppend(builder, fmt, va); + va_end(va); +} + +template +void Dqn_StringBuilder_Append(Dqn_StringBuilder *builder, char const *str, isize len = -1) +{ + if (!str) return; + if (len == -1) len = (isize)strlen(str); + isize len_w_null_terminator = len + 1; + char *buf = Dqn_StringBuilder__GetWriteBufferAndUpdateUsage(builder, len_w_null_terminator); + Dqn_MemCopy(buf, str, len); + builder->string_len += len; + buf[len] = 0; +} + +template +void Dqn_StringBuilder_AppendChar(Dqn_StringBuilder *builder, char ch) +{ + char *buf = Dqn_StringBuilder__GetWriteBufferAndUpdateUsage(builder, 1 + 1 /*null terminator*/); + *buf++ = ch; + builder->string_len++; + buf[1] = 0; +} + + + // ------------------------------------------------------------------------------------------------- // // NOTE: (Memory) Slices @@ -1171,177 +1345,6 @@ Dqn_MemArenaScopedRegion::~Dqn_MemArenaScopedRegion() this->arena->curr_mem_block->used = this->curr_mem_block_used; } -template -FILE_SCOPE char *Dqn_StringBuilder__GetWriteBufferAndUpdateUsage(Dqn_StringBuilder *builder, usize size_required) -{ - char *result = builder->fixed_mem + builder->fixed_mem_used; - usize space = Dqn_ArrayCount(builder->fixed_mem) - builder->fixed_mem_used; - usize *usage = &builder->fixed_mem_used; - - if (builder->last_mem_buf) - { - Dqn_StringBuilderBuffer *last_buf = builder->last_mem_buf; - result = last_buf->mem + last_buf->used; - space = last_buf->size - last_buf->used; - usage = &last_buf->used; - } - - if (space < size_required) - { - DQN_ASSERT(builder->my_malloc); - if (!builder->my_malloc) - return nullptr; - - // NOTE: Need to allocate new buf - usize allocation_size = sizeof(*builder->last_mem_buf) + DQN_MAX(size_required, DQN_STRING_BUILDER_MIN_MEM_BUF_ALLOC_SIZE); - void *memory = builder->my_malloc(allocation_size); - auto *new_buf = reinterpret_cast(memory); - *new_buf = {}; - new_buf->mem = static_cast(memory) + sizeof(*new_buf); - new_buf->size = allocation_size; - result = new_buf->mem; - usage = &new_buf->used; - - if (builder->last_mem_buf) - { - builder->last_mem_buf->next = new_buf; - } - else - { - builder->next_mem_buf = new_buf; - builder->last_mem_buf = new_buf; - } - } - - if (size_required > 0 && *usage > 0 && result[-1] == 0) - { - // NOTE: Not first time writing into buffer using sprintf, sprintf always writes a null terminator, so we must - // subtract one - (*usage)--; - result--; - } - - *usage += size_required; - return result; -} - -template -FILE_SCOPE void Dqn_StringBuilder__BuildOutput(Dqn_StringBuilder const *builder, char *dest, isize dest_size) -{ - // NOTE: No data appended to builder, just allocate am empty string. But - // always allocate, so we avoid adding making nullptr part of the possible - // return values and makes using Dqn_StringBuilder more complex. - if (dest_size == 1) - { - dest[0] = 0; - return; - } - - char const *end = dest + dest_size; - char *buf_ptr = dest; - - memcpy(buf_ptr, builder->fixed_mem, builder->fixed_mem_used); - buf_ptr += builder->fixed_mem_used; - - isize remaining_space = end - buf_ptr; - DQN_ASSERT(remaining_space >= 0); - - for (Dqn_StringBuilderBuffer *string_buf = builder->next_mem_buf; - string_buf && remaining_space > 0; - string_buf = string_buf->next) - { - 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, string_buf->used); - buf_ptr += string_buf->used; - - remaining_space = end - buf_ptr; - DQN_ASSERT(remaining_space >= 0); - } - DQN_ASSERT(buf_ptr == dest + dest_size); -} - - -// ------------------------------------------------------------------------------------------------- -// -// NOTE: String Builder -// -// ------------------------------------------------------------------------------------------------- -// The necessary length to build the string, it returns the length including the null-terminator -template -isize Dqn_StringBuilder_BuildLen(Dqn_StringBuilder const *builder) -{ - isize result = builder->string_len + 1; - return result; -} - -template -void Dqn_StringBuilder_BuildInBuffer(Dqn_StringBuilder const *builder, char *dest, usize dest_size) -{ - Dqn_StringBuilder__BuildOutput(builder, dest, dest_size); -} - -// len: Return the length of the allocated string including the null-terminator -template -char *Dqn_StringBuilder_BuildFromMalloc(Dqn_StringBuilder *builder, isize *len = nullptr) -{ - isize len_w_null_terminator = Dqn_StringBuilder_BuildLen(builder); - auto *result = static_cast(malloc(len_w_null_terminator)); - if (len) *len = len_w_null_terminator; - Dqn_StringBuilder__BuildOutput(builder, result, len_w_null_terminator); - return result; -} - -template -char *Dqn_StringBuilder_BuildFromArena(Dqn_StringBuilder *builder, Dqn_MemArena *arena, isize *len = nullptr) -{ - isize len_w_null_terminator = Dqn_StringBuilder_BuildLen(builder); - char *result = MEM_ARENA_ALLOC_ARRAY(arena, char, len_w_null_terminator); - if (len) *len = len_w_null_terminator; - Dqn_StringBuilder__BuildOutput(builder, result, len_w_null_terminator); - return result; -} - -template -void Dqn_StringBuilder_VFmtAppend(Dqn_StringBuilder *builder, char const *fmt, va_list va) -{ - if (!fmt) return; - isize require = stbsp_vsnprintf(nullptr, 0, fmt, va) + 1; - char *buf = Dqn_StringBuilder__GetWriteBufferAndUpdateUsage(builder, require); - stbsp_vsnprintf(buf, static_cast(require), fmt, va); - builder->string_len += (require - 1); // -1 to exclude null terminator -} - -template -void Dqn_StringBuilder_FmtAppend(Dqn_StringBuilder *builder, char const *fmt, ...) -{ - va_list va; - va_start(va, fmt); - Dqn_StringBuilder_VFmtAppend(builder, fmt, va); - va_end(va); -} - -template -void Dqn_StringBuilder_Append(Dqn_StringBuilder *builder, char const *str, isize len = -1) -{ - if (!str) return; - if (len == -1) len = (isize)strlen(str); - isize len_w_null_terminator = len + 1; - char *buf = Dqn_StringBuilder__GetWriteBufferAndUpdateUsage(builder, len_w_null_terminator); - Dqn_MemCopy(buf, str, len); - builder->string_len += len; - buf[len] = 0; -} - -template -void Dqn_StringBuilder_AppendChar(Dqn_StringBuilder *builder, char ch) -{ - char *buf = Dqn_StringBuilder__GetWriteBufferAndUpdateUsage(builder, 1 + 1 /*null terminator*/); - *buf++ = ch; - builder->string_len++; - buf[1] = 0; -} - - // ------------------------------------------------------------------------------------------------- // // NOTE: Vectors diff --git a/Code/Dqn_UnitTests.cpp b/Code/Dqn_UnitTests.cpp index 2086124..2aa0654 100644 --- a/Code/Dqn_UnitTests.cpp +++ b/Code/Dqn_UnitTests.cpp @@ -1,3 +1,7 @@ +#define DQN_HEADER_COPY_PROTOTYPE_AND_COMMENT(func_comment, func_return, func_name_and_types) func_return func_name_and_types +#define DQN_HEADER_COPY_PROTOTYPE(func_return, func_name_and_types) func_return func_name_and_types + +#define _CRT_SECURE_NO_WARNINGS #include "Dqn.h" struct TestState @@ -417,10 +421,175 @@ FILE_SCOPE void UnitTests() } } +char *Dqn_ReadFileWithArena(Dqn_MemArena *arena, char const *file, isize *file_size) +{ + FILE *file_handle = fopen(file, "rb"); + fseek(file_handle, 0, SEEK_END); + usize file_size_ = ftell(file_handle); + rewind(file_handle); + + auto *result = (char *)MEM_ARENA_ALLOC(arena, file_size_ + 1); + DQN_ASSERT(result); + result[file_size_] = 0; + + if (fread(result, file_size_, 1, file_handle) != 1) + { + fprintf(stderr, "Failed to fread: %zu bytes into buffer from file: %s\n", file_size_, file); + return nullptr; + } + + if (file_size) *file_size = file_size_; + return result; +} + +char *Dqn_StrFind(char *buf, char const *find, isize buf_len = -1, isize find_len = -1) +{ + if (find_len == 0) return nullptr; + if (buf_len < 0) buf_len = (isize)strlen(buf); + if (find_len < 0) find_len = (isize)strlen(find); + + char *buf_end = buf + buf_len; + char *result = nullptr; + for (; *buf; ++buf) + { + isize remaining = static_cast(buf_end - buf); + if (remaining < find_len) break; + + if (strncmp(buf, find, find_len) == 0) + { + result = buf; + break; + } + } + return result; +} + +char *Dqn_StrSkipWhitespace(char *buf) +{ + while (buf && (buf[0] == '\r' || buf[0] == '\t' || buf[0] == '\n' || buf[0] == ' ')) + buf++; + return buf; +} + +char *ParseFunctionReturnType(char *ptr, isize *len) +{ + char *result = ptr; + isize result_len = 0; + for (int scope = 0; ptr; ptr++) // NOTE: Parse the function return type + { + if (ptr[0] == '<' || ptr[0] == '>') + { + if (ptr[0] == '<') scope++; + else scope--; + continue; + } + else if (ptr[0] == ',') + { + if (scope != 0) continue; + result_len = static_cast(ptr - result); + break; + } + } + + if (len) *len = result_len; + return result; +} + +char *ParseFunctionNameAndParameters(char *ptr, isize *len) +{ + char *result = ptr; + int result_len = 0; + for (int scope = 0; ptr; ptr++) // NOTE: Parse the function name and parameters + { + if (ptr[0] == '(') scope++; + else if (ptr[0] == ')') + { + if (scope-- != 0) continue; + result_len = static_cast(ptr - result); + break; + } + } + + *len = result_len; + return result; +} + +char *ParseFunctionComment(char *ptr, isize *len) +{ + while (ptr[0] != '"') ptr++; + ptr++; + + char *result = ptr; + isize result_len = 0; + for (;;) + { + while (ptr[0] != '"') + ptr++; + + if (ptr[-1] != '\\') + { + result_len = ptr - result; + break; + } + } + + *len = result_len; + return result; + +} + int main(char *argv[], int argc) { (void)argv; (void)argc; UnitTests(); + + Dqn_MemArena arena = {}; + isize buf_size = 0; + char *buf = Dqn_ReadFileWithArena(&arena, "../Code/Dqn.h", &buf_size); + if (!buf) + return -1; + + char constexpr HEADER_COPY_PROTOTYPE[] = "DQN_HEADER_COPY_PROTOTYPE"; + char constexpr HEADER_COPY_PROTOTYPE_AND_COMMENT[] = "DQN_HEADER_COPY_PROTOTYPE_AND_COMMENT"; + + char *ptr = buf; + char *ptr_end = buf + buf_size; + isize ptr_len = buf_size; + for (char *token = Dqn_StrFind(ptr, HEADER_COPY_PROTOTYPE, ptr_len, Dqn_CharCountI(HEADER_COPY_PROTOTYPE)); + token; + ptr_len = ptr_end - ptr, token = Dqn_StrFind(ptr, HEADER_COPY_PROTOTYPE, ptr_len)) + { + ptr = token; + bool prototype_and_comment = (strncmp(token, HEADER_COPY_PROTOTYPE_AND_COMMENT, Dqn_CharCountI(HEADER_COPY_PROTOTYPE_AND_COMMENT)) == 0); + if (prototype_and_comment) + { + ptr += Dqn_CharCount(HEADER_COPY_PROTOTYPE_AND_COMMENT) + 1 /*macro start parenthesis*/; + + isize comment_len = 0; + char *comment = ParseFunctionComment(ptr, &comment_len); + ptr = comment + comment_len; + while (ptr[0] != ',') ptr++; + ptr++; + + fprintf(stdout, "%.*s", (int)comment_len, comment); + } + else + { + ptr += Dqn_CharCount(HEADER_COPY_PROTOTYPE) + 1 /*macro start parenthesis*/; + } + + isize func_type_len = 0; + char *func_type = ParseFunctionReturnType(ptr, &func_type_len); + + ptr = func_type + func_type_len + 1; // Ptr is at macro comma, skip the comma + ptr = Dqn_StrSkipWhitespace(ptr); + isize func_name_len = 0; + char *func_name = ParseFunctionNameAndParameters(ptr, &func_name_len); + + ptr = func_name + func_name_len + 1; // Ptr is at macro closing paren, skip the paren + fprintf(stdout, "%.*s %.*s", (int)func_type_len, func_type, (int)func_name_len, func_name); + } + return 0; }