From e76d553f1a59c4ca728ba96e74ea21f1cf19ec71 Mon Sep 17 00:00:00 2001 From: Ivar Fatland Date: Sat, 6 Dec 2025 21:37:23 +0100 Subject: [PATCH] added basic string builder --- cig.h | 27 +++++++++++ scanner.c | 2 +- string_builder.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 string_builder.c diff --git a/cig.h b/cig.h index 60d377e..e68af4a 100644 --- a/cig.h +++ b/cig.h @@ -250,6 +250,32 @@ bool scan_identifier(scanner_t *s); bool scan_string_literal(scanner_t *s); bool scanner_print_errors(scanner_t *s, FILE *fp); +// string builder ////////////////////////////////////////////////////////////// +typedef struct string_builder_node { + struct string_builder_node *next; + size_t length; + const char *string; +} string_builder_node_t; + +typedef struct string_builder { + string_builder_node_t *head; + string_builder_node_t *tail; + allocator_t allocator; +} string_builder_t; + +string_builder_t sb_create(allocator_t builder_allocator); +// assumes the provided string will live until sb_build is called. +void sb_add_string(string_builder_t *this, const char *string); +void sb_add_i64(string_builder_t *this, int64_t i64); +void sb_add_i32(string_builder_t *this, int32_t i32); +void sb_add_i16(string_builder_t *this, int16_t i16); +void sb_add_i8(string_builder_t *this, int8_t i8); +void sb_add_u64(string_builder_t *this, uint64_t u64); +void sb_add_u32(string_builder_t *this, uint32_t u32); +void sb_add_u16(string_builder_t *this, uint16_t u16); +void sb_add_u8(string_builder_t *this, uint8_t u8); +const char *sb_build(string_builder_t *this, allocator_t output_allocator); + #ifdef CIG_IMPL void *allocator_alloc_func(allocator_t this, size_t bytes, const char *file, int line) { @@ -279,6 +305,7 @@ void allocator_reset(allocator_t this) { #include "dyn_array.c" #include "cli.c" #include "scanner.c" +#include "string_builder.c" #endif // CIG_IMPL #endif // CIG_H diff --git a/scanner.c b/scanner.c index 07f7569..e7722af 100644 --- a/scanner.c +++ b/scanner.c @@ -34,7 +34,7 @@ void scanner_error(scanner_t *s, const char *message) { } // need 40 bytes for pair of largest possible 64 bit values, then some for // ':' and ' ' characters. Just rounded up to 64 because reasons. - size_t length = strlen(s->name) + strlen(message) + 64; + size_t length = strlen(s->name) + 1 + strlen(message) + 1 + 64; char *buf = allocator_alloc(s->allocator, length); snprintf( buf, diff --git a/string_builder.c b/string_builder.c new file mode 100644 index 0000000..7e7d780 --- /dev/null +++ b/string_builder.c @@ -0,0 +1,114 @@ +#include "cig.h" +#include +#include +#include +#include + +string_builder_t sb_create(allocator_t builder_allocator) { + return (string_builder_t) { + .allocator=builder_allocator, + .head=NULL, + .tail=NULL + }; +} + +void sb_add_string(string_builder_t *this, const char *string) { + string_builder_node_t *node = allocator_alloc(this->allocator, sizeof(string_builder_node_t)); + node->length = strlen(string); + node->string = string; + node->next = NULL; + if (this->tail == NULL) { + assert(this->head == NULL); + this->head = node; + this->tail = node; + } else { + this->tail->next = node; + this->tail = node; + } +} + +void sb_add_i64(string_builder_t *this, int64_t i64) { + // 21 bytes is the max needed ever for a 64 bit integer, even unsigned. + const unsigned long max_size = 21; + char *buffer = allocator_alloc(this->allocator, max_size); + snprintf(buffer, max_size, "%"PRIi64, i64); + sb_add_string(this, buffer); +} + +void sb_add_i32(string_builder_t *this, int32_t i32) { + const unsigned long max_size = 21; + char *buffer = allocator_alloc(this->allocator, max_size); + snprintf(buffer, max_size, "%"PRIi32, i32); + sb_add_string(this, buffer); +} + +void sb_add_i16(string_builder_t *this, int16_t i16) { + const unsigned long max_size = 21; + char *buffer = allocator_alloc(this->allocator, max_size); + snprintf(buffer, max_size, "%"PRIi16, i16); + sb_add_string(this, buffer); +} + +void sb_add_i8(string_builder_t *this, int8_t i8) { + const unsigned long max_size = 21; + char *buffer = allocator_alloc(this->allocator, max_size); + snprintf(buffer, max_size, "%"PRIi8, i8); + sb_add_string(this, buffer); +} + +void sb_add_u64(string_builder_t *this, uint64_t u64) { + const unsigned long max_size = 21; + char *buffer = allocator_alloc(this->allocator, max_size); + snprintf(buffer, max_size, "%"PRIu64, u64); + sb_add_string(this, buffer); +} + +void sb_add_u32(string_builder_t *this, uint32_t u32) { + const unsigned long max_size = 21; + char *buffer = allocator_alloc(this->allocator, max_size); + snprintf(buffer, max_size, "%"PRIu32, u32); + sb_add_string(this, buffer); +} + +void sb_add_u16(string_builder_t *this, uint16_t u16) { + const unsigned long max_size = 21; + char *buffer = allocator_alloc(this->allocator, max_size); + snprintf(buffer, max_size, "%"PRIu16, u16); + sb_add_string(this, buffer); +} + +void sb_add_u8(string_builder_t *this, uint8_t u8) { + const unsigned long max_size = 21; + char *buffer = allocator_alloc(this->allocator, max_size); + snprintf(buffer, max_size, "%"PRIu8, u8); + sb_add_string(this, buffer); +} + +const char *sb_build( + string_builder_t *this, + allocator_t output_allocator +) { + size_t total_length = 0; + for ( + string_builder_node_t *node = this->head; + node != NULL; + node = node->next + ) { + total_length += node->length; + } + total_length++; // null character + char *buffer = allocator_alloc(output_allocator, total_length * sizeof(char)); + size_t i = 0; + for ( + string_builder_node_t *node = this->head; + node != NULL; + node = node->next + ) { + for (const char *c = node->string; (*c) != '\0'; c++) { + buffer[i] = *c; + i++; + } + } + buffer[total_length-1] = '\0'; + return buffer; +}