From aff923d470a951de7134ec0db4d0333c1713e9e3 Mon Sep 17 00:00:00 2001 From: yukkop Date: Fri, 21 Mar 2025 15:29:00 +0000 Subject: [PATCH] refactor(hmpl): rewrite via arenas --- package/c/chectic/chectic.c | 22 ------- package/c/chectic/chectic.h | 66 +++++++++++++------ package/c/chectic/test/arena.c | 12 +++- package/c/hmpl/hmpl.c | 113 ++++++++++++++++----------------- 4 files changed, 112 insertions(+), 101 deletions(-) diff --git a/package/c/chectic/chectic.c b/package/c/chectic/chectic.c index d69e3f3..9bafa4b 100644 --- a/package/c/chectic/chectic.c +++ b/package/c/chectic/chectic.c @@ -74,25 +74,3 @@ char* log_message(LogLevel level, char *file, int line, const char *format, ...) return timeStr; } - -// ----------- -// -- arena -- -// ----------- - -Arena arena_init(size_t size) { - Arena arena; - arena.begin = malloc(size); - memset(arena.begin, 0, size); - arena.current = arena.begin; - arena.capacity = size; - - return arena; -} - -void arena_reset(Arena *arena) { - arena->current = arena->begin; -} - -void arena_free(Arena *arena) { - free(arena->begin); -} diff --git a/package/c/chectic/chectic.h b/package/c/chectic/chectic.h index d661397..e5516e9 100644 --- a/package/c/chectic/chectic.h +++ b/package/c/chectic/chectic.h @@ -105,35 +105,61 @@ typedef struct { *(arena) = arena_init(ARENA_DEFAULT_SIZE); \ } \ size_t current__ = (size_t)(arena)->current - (size_t)(arena)->begin; \ - if ((arena)->capacity <= current__ || (arena)->capacity - current__ < (size)) {\ - raise_debug("Arena from %d with capacity %d allocated on %d cannot be allocated on %d", \ - (arena)->begin, (arena)->capacity, \ - (arena)->current - (arena)->begin, (size)); \ + if ((arena)->capacity <= current__ || (arena)->capacity - current__ < (size)) { \ + raise_debug("Arena %p (capacity %zu) used %zu cannot allocate %zu bytes", \ + (arena)->begin, (arena)->capacity, current__, (size)); \ } else { \ - raise_debug("Arena from %d with capacity %d allocated on %d will allocate on %d", \ - (arena)->begin, (arena)->capacity, \ - (arena)->begin, (arena)->capacity, (size)); \ + raise_debug("Arena %p (capacity %zu) used %zu will allocate %zu bytes", \ + (arena)->begin, (arena)->capacity, current__, (size)); \ mem__ = (arena)->current; \ (arena)->current = (char*)(arena)->current + (size); \ } \ + raise_debug("Allocated at %p", mem__); \ mem__; \ }) -Arena arena_init(size_t size); - -void arena_reset(Arena *arena); - -void arena_free(Arena *arena); - -#define arena_alloc(arena, size) __extension__ ({ \ - void *mem__ = arena_alloc_or_null((arena), (size)); \ - if (!mem__) { \ - raise_exception("Arena out of memory"); \ - exit(1); \ - } \ - mem__; \ +#define arena_init(size) __extension__ ({ \ + Arena arena__; \ + arena__.begin = malloc(size); \ + memset(arena__.begin, 0, size); \ + arena__.current = arena__.begin; \ + arena__.capacity = size; \ + raise_debug("Initialized arena at %p with capacity %zu", arena__.begin, size); \ + arena__; \ }) +#define arena_reset(arena) __extension__ ({ \ + (arena)->current = (arena)->begin; \ + raise_debug("Arena %p reset", (arena)->begin); \ +}) + +#define arena_free(arena) __extension__ ({ \ + raise_debug("Freeing arena at %p", (arena)->begin); \ + free((arena)->begin); \ +}) + +#define arena_alloc(arena, size) __extension__ ({ \ + void *mem__ = arena_alloc_or_null((arena), (size)); \ + if (!mem__) { \ + raise_debug("Arena out of memory when trying to allocate %zu bytes", (size)); \ + raise_exception("Arena out of memory"); \ + exit(1); \ + } \ + mem__; \ +}) + +#define arena_strdup(arena, s) __extension__ ({ \ + const char *s__ = (s); \ + char *result__; \ + if (s__) { \ + size_t len__ = strlen(s__) + 1; \ + result__ = (char *)arena_alloc(arena, len__); \ + memcpy(result__, s__, len__); \ + } else { \ + result__ = NULL; \ + } \ + result__; \ +}) // TODO: mmap // TODO: dynamic array style diff --git a/package/c/chectic/test/arena.c b/package/c/chectic/test/arena.c index 5f275ac..f29b3dc 100644 --- a/package/c/chectic/test/arena.c +++ b/package/c/chectic/test/arena.c @@ -47,14 +47,24 @@ void test_arena_null_init() { arena_free(&arena); } +void test_arena_strdup() { + Arena arena = arena_init(64); + const char *orig = "Hello, Arena!"; + char *copy = arena_strdup(&arena, orig); + assert(copy != NULL); + assert(strcmp(copy, orig) == 0); + arena_free(&arena); +} + int main() { set_output_color_mode(COLOR_MODE_DISABLE); - logger_level(LOG_LEVEL_DEBUG); \ + logger_level(LOG_LEVEL_DEBUG); test_arena_init(); test_arena_alloc(); test_arena_alloc_or_null_out_of_memory(); test_arena_reset(); test_arena_null_init(); + test_arena_strdup(); printf("All tests passed.\n"); } diff --git a/package/c/hmpl/hmpl.c b/package/c/hmpl/hmpl.c index 3e784bc..8322587 100644 --- a/package/c/hmpl/hmpl.c +++ b/package/c/hmpl/hmpl.c @@ -5,29 +5,29 @@ #include "chectic.h" #include "cjson/cJSON.h" +static Arena arena; + char *eval(cJSON *context, const char *key) { if (!context || !key) return NULL; - char *key_copy = strdup(key); + char *key_copy = arena_strdup(&arena, key); char *token, *rest = key_copy; cJSON *res = context; - while ((token = strtok_r(rest, ".", &rest))) { res = cJSON_GetObjectItemCaseSensitive(res, token); - if (!res) { - free(key_copy); + if (!res) return NULL; - } } - free(key_copy); - if (cJSON_IsString(res) && res->valuestring) - return strdup(res->valuestring); + return arena_strdup(&arena, res->valuestring); else if (cJSON_IsNumber(res)) { char buf[64]; snprintf(buf, sizeof(buf), "%g", res->valuedouble); - return strdup(buf); + return arena_strdup(&arena, buf); } - return cJSON_PrintUnformatted(res); + char *temp = cJSON_PrintUnformatted(res); + char *result = arena_strdup(&arena, temp); + free(temp); + return result; } void substring(const char *src, char *dest, size_t start, size_t len) { @@ -48,14 +48,10 @@ char* replace_substring(const char* src, int start, int end, const char* replace int src_len = strlen(src); int rep_len = strlen(replacement); int new_len = src_len - (end - start + 1) + rep_len; - - char* new_str = malloc(new_len + 1); - if (!new_str) return NULL; - - memcpy(new_str, src, start); // copy before + char* new_str = arena_alloc(&arena, new_len + 1); + memcpy(new_str, src, start); // copy before memcpy(new_str + start, replacement, rep_len); // insert replacement - strcpy(new_str + start + rep_len, src + end + 1); // copy after - + strcpy(new_str + start + rep_len, src + end + 1); // copy after return new_str; } @@ -77,32 +73,27 @@ void render_template_placeholders(char **text_ptr, cJSON *context, const char *p raise_debug("start: %d", key_start); char *placeholder_end = strstr(placeholder_start, "}}"); - if (!placeholder_end) { + if (!placeholder_end) raise_exception("Malformed template: missing closing braces for placeholder start"); - } int key_length = (placeholder_end - current_text) - key_start; - char *placeholder_key = malloc(key_length + 1); - if (!placeholder_key) - raise_exception("Memory allocation error"); + char *placeholder_key = arena_alloc(&arena, key_length + 1); substring(current_text, placeholder_key, key_start, key_length); raise_debug("key: %s", placeholder_key); char *replacement = eval(context, placeholder_key); raise_debug("%s = eval(context, %s)", replacement ? replacement : "NULL", placeholder_key); - free(placeholder_key); if (!replacement) { - offset = (placeholder_end - current_text) + 2; // skip past the placeholder + offset = (placeholder_end - current_text) + 2; continue; } - int placeholder_end_index = (placeholder_end - current_text) + 2; // include "}}" + int placeholder_end_index = (placeholder_end - current_text) + 2; + char *new_text = replace_substring(current_text, + start_index, + placeholder_end_index - 1, + replacement); - char *new_text = - replace_substring(current_text, start_index, placeholder_end_index - 1, replacement); - free(replacement); - free(current_text); // free the old text - *text_ptr = new_text; - offset = start_index; // resume search from the replaced location + offset = start_index; } } @@ -111,35 +102,41 @@ void render_template(char **text, cJSON *context) { } int main(int argc, char *argv[]) { - init_logger(); - raise_info("start"); + init_logger(); + raise_info("start"); - char *text = NULL; - const char *json_input = (argc > 1 ? argv[1] : "{}"); - cJSON *context = cJSON_Parse(json_input); - if (!context) { - fprintf(stderr, "Error parsing JSON\n"); - return 1; + arena = arena_init(1024 * 1024); + + raise_info("read the arguments"); + char *text = NULL; + const char *json_input = (argc > 1 ? argv[1] : "{}"); + cJSON *context = cJSON_Parse(json_input); + if (!context) { + fprintf(stderr, "Error parsing JSON\n"); + return 1; + } + + if (argc > 2) { + text = arena_strdup(&arena, argv[2]); + } else if (!isatty(fileno(stdin))) { + size_t size = 0; + char *heap_text = NULL; + ssize_t len = getdelim(&heap_text, &size, '\0', stdin); + if (len < 0) { + perror("read stdin"); + cJSON_Delete(context); + return 1; } + text = arena_strdup(&arena, heap_text); + free(heap_text); // free temporary heap allocation + } else { + text = arena_strdup(&arena, ""); + } - if (argc > 2) { - text = strdup(argv[2]); - } else if (!isatty(fileno(stdin))) { - size_t size = 0; - ssize_t len = getdelim(&text, &size, '\0', stdin); - if (len < 0) { - perror("read stdin"); - cJSON_Delete(context); - return 1; - } - } else { - text = strdup(""); - } + render_template(&text, context); + printf("%s", text); - render_template(&text, context); - printf("%s", text); - - free(text); - cJSON_Delete(context); - return 0; + arena_free(&arena); + cJSON_Delete(context); + return 0; }