Write PoC simpler header generator code

This commit is contained in:
doyle 2019-08-24 15:43:14 +10:00
parent cee2573b00
commit 95272e77ee
2 changed files with 343 additions and 171 deletions

View File

@ -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 <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;
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<Dqn_StringBuilderBuffer *>(memory);
*new_buf = {};
new_buf->mem = static_cast<char *>(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 <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
// 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 <usize N> isize,
Dqn_StringBuilder_BuildLen(Dqn_StringBuilder<N> const *builder))
{
isize result = builder->string_len + 1;
return result;
}
DQN_HEADER_COPY_PROTOTYPE(
template <usize N> void,
Dqn_StringBuilder_BuildInBuffer(Dqn_StringBuilder<N> 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 <usize N>
char *Dqn_StringBuilder_BuildFromMalloc(Dqn_StringBuilder<N> *builder, isize *len = nullptr)
{
isize len_w_null_terminator = Dqn_StringBuilder_BuildLen(builder);
auto *result = static_cast<char *>(malloc(len_w_null_terminator));
if (len) *len = len_w_null_terminator;
Dqn_StringBuilder__BuildOutput(builder, result, len_w_null_terminator);
return result;
}
template <usize N>
char *Dqn_StringBuilder_BuildFromArena(Dqn_StringBuilder<N> *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 <usize N>
void Dqn_StringBuilder_VFmtAppend(Dqn_StringBuilder<N> *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<int>(require), fmt, va);
builder->string_len += (require - 1); // -1 to exclude null terminator
}
template <usize N>
void Dqn_StringBuilder_FmtAppend(Dqn_StringBuilder<N> *builder, char const *fmt, ...)
{
va_list va;
va_start(va, fmt);
Dqn_StringBuilder_VFmtAppend(builder, fmt, va);
va_end(va);
}
template <usize N>
void Dqn_StringBuilder_Append(Dqn_StringBuilder<N> *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 <usize N>
void Dqn_StringBuilder_AppendChar(Dqn_StringBuilder<N> *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 <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;
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<Dqn_StringBuilderBuffer *>(memory);
*new_buf = {};
new_buf->mem = static_cast<char *>(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 <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
// 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 <usize N>
isize Dqn_StringBuilder_BuildLen(Dqn_StringBuilder<N> const *builder)
{
isize result = builder->string_len + 1;
return result;
}
template <usize N>
void Dqn_StringBuilder_BuildInBuffer(Dqn_StringBuilder<N> 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 <usize N>
char *Dqn_StringBuilder_BuildFromMalloc(Dqn_StringBuilder<N> *builder, isize *len = nullptr)
{
isize len_w_null_terminator = Dqn_StringBuilder_BuildLen(builder);
auto *result = static_cast<char *>(malloc(len_w_null_terminator));
if (len) *len = len_w_null_terminator;
Dqn_StringBuilder__BuildOutput(builder, result, len_w_null_terminator);
return result;
}
template <usize N>
char *Dqn_StringBuilder_BuildFromArena(Dqn_StringBuilder<N> *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 <usize N>
void Dqn_StringBuilder_VFmtAppend(Dqn_StringBuilder<N> *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<int>(require), fmt, va);
builder->string_len += (require - 1); // -1 to exclude null terminator
}
template <usize N>
void Dqn_StringBuilder_FmtAppend(Dqn_StringBuilder<N> *builder, char const *fmt, ...)
{
va_list va;
va_start(va, fmt);
Dqn_StringBuilder_VFmtAppend(builder, fmt, va);
va_end(va);
}
template <usize N>
void Dqn_StringBuilder_Append(Dqn_StringBuilder<N> *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 <usize N>
void Dqn_StringBuilder_AppendChar(Dqn_StringBuilder<N> *builder, char ch)
{
char *buf = Dqn_StringBuilder__GetWriteBufferAndUpdateUsage(builder, 1 + 1 /*null terminator*/);
*buf++ = ch;
builder->string_len++;
buf[1] = 0;
}
// -------------------------------------------------------------------------------------------------
//
// NOTE: Vectors

View File

@ -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<isize>(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<int>(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<int>(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;
}