diff --git a/ltm/include/ltm/common.h b/ltm/include/ltm/common.h index ef86779..11e6fd6 100644 --- a/ltm/include/ltm/common.h +++ b/ltm/include/ltm/common.h @@ -21,6 +21,8 @@ typedef enum ltm_err { ltm_err_ok = 0, ltm_err_invalid_template, ltm_err_failed_alloc, + ltm_err_not_found, + ltm_err_wrong_block_type, } ltm_err; #endif diff --git a/ltm/include/ltm/template.h b/ltm/include/ltm/template.h index 445fbf9..d08ea09 100644 --- a/ltm/include/ltm/template.h +++ b/ltm/include/ltm/template.h @@ -28,6 +28,12 @@ ltm_err ltm_template_compile(ltm_template **out, const char *template); ltm_err ltm_template_compile_n(ltm_template **out, const char *template, size_t len); +/** + * Free the template instance. After freeing a template, no instances associated + * with it are safe to use. + */ +void ltm_template_free(ltm_template *template); + /** * Represents a specific instance of a template. */ @@ -39,8 +45,15 @@ typedef struct ltm_instance ltm_instance; ltm_err ltm_template_instantiate(ltm_instance **out, const ltm_template *template); +/** + * Free the given instance, as well as all nested instances. + */ +void ltm_instance_free(ltm_instance *instance); + typedef enum ltm_instance_block_type { ltm_instance_block_type_buf = 0, + ltm_instance_block_type_buf_owned, + ltm_instance_block_type_nested, } ltm_instance_block_type; /** diff --git a/ltm/src/ltm_instance.c b/ltm/src/ltm_instance.c index 6f9af67..2754c30 100644 --- a/ltm/src/ltm_instance.c +++ b/ltm/src/ltm_instance.c @@ -1,3 +1,7 @@ +#include +#include + +#include "ltm/common.h" #include "ltm/template.h" #include "ltm/template_internal.h" @@ -25,6 +29,32 @@ ltm_err ltm_instance_init(ltm_instance **out) { return ltm_err_ok; } +void ltm_instance_free(ltm_instance *instance) { + if (instance->vars != NULL) { + free(instance->vars); + } + + ltm_instance_block *block = instance->blocks.head; + + while (block != NULL) { + switch (block->type) { + case ltm_instance_block_type_buf_owned: + free(block->data.ptr); + break; + case ltm_instance_block_type_nested: + ltm_instance_free(block->data.ptr); + break; + case ltm_instance_block_type_buf:; + } + + ltm_instance_block *temp = block->next; + free(block); + block = temp; + } + + free(instance); +} + ltm_err ltm_template_instantiate(ltm_instance **out, const ltm_template *template) { ltm_instance *instance; @@ -32,15 +62,20 @@ ltm_err ltm_template_instantiate(ltm_instance **out, instance->template = template; - ltm_instance_block ***vars = - malloc(template->names.len * sizeof(ltm_instance_block **)); + ltm_instance_block ***vars = NULL; - if (vars == NULL) { - return ltm_err_failed_alloc; + if (template->names.len > 0) { + vars = malloc(template->names.len * sizeof(ltm_instance_block **)); + + if (vars == NULL) { + ltm_instance_free(instance); + + return ltm_err_failed_alloc; + } + + instance->vars = vars; } - instance->vars = vars; - for (size_t block_index = 0; block_index < template->blocks.len; block_index++) { ltm_template_block *template_block = &template->blocks.arr[block_index]; @@ -48,7 +83,7 @@ ltm_err ltm_template_instantiate(ltm_instance **out, switch (template_block->type) { case ltm_template_block_type_literal: { ltm_instance_block *block; - LTM_RES(ltm_instance_block_init(&block)); + LTM_RES2(ltm_instance_block_init(&block), ltm_instance_free(instance)); block->type = ltm_instance_block_type_buf; block->data.ptr = template_block->data.ptr; @@ -67,6 +102,7 @@ ltm_err ltm_template_instantiate(ltm_instance **out, case ltm_template_block_type_nested: *vars = &instance->blocks.tail->next; vars++; + break; } } @@ -74,3 +110,84 @@ ltm_err ltm_template_instantiate(ltm_instance **out, return ltm_err_ok; } + +ltm_err ltm_instance_block_add_var(ltm_instance *instance, const char *name, + ltm_instance_block_type type, void *data, + size_t len) { + const ltm_template *template = instance->template; + bool matched = false; + + size_t i = 0; + for (i = 0; i < template->names.len && !matched; i++) { + ltm_template_block_name *block_name = &template->names.arr[i]; + matched = strncmp(name, block_name->name.s, block_name->name.len) == 0; + } + + if (!matched) { + return ltm_err_not_found; + } + + ltm_template_block *template_block = &template->blocks.arr[i]; + + if (template_block->type != ltm_template_block_type_var) { + return ltm_err_wrong_block_type; + } + + ltm_instance_block *block; + LTM_RES(ltm_instance_block_init(&block)); + + block->type = type; + block->data.ptr = data; + block->data.len = len; + + // We insert the block in the linked list and replace its next pointer as the + // new attachment point for this variable + block->next = *instance->vars[i]; + *instance->vars[i] = block; + instance->vars[i] = &block->next; + + return ltm_err_ok; +} + +ltm_err ltm_instance_block_add_nested(ltm_instance **out, + ltm_instance *instance, + const char *name) { + const ltm_template *template = instance->template; + bool matched = false; + + size_t i = 0; + for (i = 0; i < template->names.len && !matched; i++) { + ltm_template_block_name *block_name = &template->names.arr[i]; + matched = strncmp(name, block_name->name.s, block_name->name.len) == 0; + } + + if (!matched) { + return ltm_err_not_found; + } + + ltm_template_block *template_block = &template->blocks.arr[i]; + + if (template_block->type != ltm_template_block_type_nested) { + return ltm_err_wrong_block_type; + } + + ltm_instance_block *block; + LTM_RES(ltm_instance_block_init(&block)); + + ltm_instance *nested; + LTM_RES2(ltm_template_instantiate(&nested, template_block->data.ptr), + free(block)); + + block->type = ltm_instance_block_type_nested; + block->data.ptr = nested; + + // We insert the block in the linked list and replace its next pointer as the + // new attachment point for this variable + block->next = *instance->vars[i]; + *instance->vars[i] = block; + instance->vars[i] = &block->next; + + *out = nested; + + return ltm_err_ok; +} diff --git a/ltm/src/ltm_template.c b/ltm/src/ltm_template.c index 13ab14a..03dcbd3 100644 --- a/ltm/src/ltm_template.c +++ b/ltm/src/ltm_template.c @@ -13,6 +13,30 @@ ltm_err ltm_template_init(ltm_template **out) { return ltm_err_ok; } +void ltm_template_free(ltm_template *template) { + if (template->blocks.len > 0) { + for (size_t i = 0; i < template->blocks.len; i++) { + ltm_template_block *block = &template->blocks.arr[i]; + + switch (block->type) { + case ltm_template_block_type_nested: + ltm_template_free(block->data.ptr); + break; + case ltm_template_block_type_var: + case ltm_template_block_type_literal:; + } + } + + free(template->blocks.arr); + } + + if (template->names.len > 0) { + free(template->names.arr); + } + + free(template); +} + ltm_err ltm_template_name_append(ltm_template *template, const char *s, size_t len, size_t index) { ltm_template_block_name *names = diff --git a/ltm/src/ltm_template_compile.c b/ltm/src/ltm_template_compile.c index 7093093..b69aa4b 100644 --- a/ltm/src/ltm_template_compile.c +++ b/ltm/src/ltm_template_compile.c @@ -104,20 +104,26 @@ ltm_err ltm_template_compile_n(ltm_template **out, const char *s, size_t len) { while (ltm_template_next_placeholder(&ph, s, len)) { // Add part before placeholder as literal if (!in_nested && (ph.start != s)) { - LTM_RES(ltm_template_block_append( - template, ltm_template_block_type_literal, (void *)s, ph.start - s)); + LTM_RES2(ltm_template_block_append(template, + ltm_template_block_type_literal, + (void *)s, ph.start - s), + ltm_template_free(template)); } switch (ph.type) { // Invalid placeholders can be detected as early as possible case ltm_placeholder_type_invalid: + ltm_template_free(template); + return ltm_err_invalid_template; case ltm_placeholder_type_var: if (!in_nested) { - LTM_RES(ltm_template_block_append(template, ltm_template_block_type_var, - NULL, 0)); - LTM_RES(ltm_template_name_append(template, ph.name.s, ph.name.len, - template->blocks.len - 1)); + LTM_RES2(ltm_template_block_append( + template, ltm_template_block_type_var, NULL, 0), + ltm_template_free(template)); + LTM_RES2(ltm_template_name_append(template, ph.name.s, ph.name.len, + template->blocks.len - 1), + ltm_template_free(template)); } break; case ltm_placeholder_type_nested_start: @@ -133,6 +139,8 @@ ltm_err ltm_template_compile_n(ltm_template **out, const char *s, size_t len) { case ltm_placeholder_type_nested_end: // This means there's more nested end placeholders than nested starts if (cur_nested_depth == 0) { + ltm_template_free(template); + return ltm_err_invalid_template; } @@ -141,17 +149,22 @@ ltm_err ltm_template_compile_n(ltm_template **out, const char *s, size_t len) { if (in_nested && (cur_nested_depth == nested_depth)) { size_t nested_len = ph.start - nested_start; ltm_template *nested_template; - LTM_RES( - ltm_template_compile_n(&nested_template, nested_start, nested_len)); + LTM_RES2( + ltm_template_compile_n(&nested_template, nested_start, nested_len), + ltm_template_free(template)); LTM_RES(ltm_template_block_append( template, ltm_template_block_type_nested, nested_template, 0)); - LTM_RES(ltm_template_name_append(template, nested_name, nested_name_len, - template->blocks.len - 1)); + LTM_RES2(ltm_template_name_append(template, nested_name, + nested_name_len, + template->blocks.len - 1), + ltm_template_free(template)); in_nested = false; } // We encountered a nested end without a start else if (!in_nested) { + ltm_template_free(template); + return ltm_err_invalid_template; } break; @@ -163,13 +176,16 @@ ltm_err ltm_template_compile_n(ltm_template **out, const char *s, size_t len) { // Unfinished nested if (in_nested) { + ltm_template_free(template); + return ltm_err_invalid_template; } // Add remaining trailing literal if (len > 0) { - LTM_RES(ltm_template_block_append(template, ltm_template_block_type_literal, - (void *)s, len)); + LTM_RES2(ltm_template_block_append( + template, ltm_template_block_type_literal, (void *)s, len), + ltm_template_free(template)); } *out = template; diff --git a/ltm/test/compile.c b/ltm/test/compile.c index c7c7151..db02e13 100644 --- a/ltm/test/compile.c +++ b/ltm/test/compile.c @@ -23,6 +23,8 @@ void test_single_placeholder() { TEST_CHECK(template->names.arr[0].name.s == s + 10); TEST_CHECK(template->names.arr[0].name.len == 5); TEST_CHECK(template->names.arr[0].index == 1); + + ltm_template_free(template); } void test_single_placeholder_trailing() { @@ -48,6 +50,8 @@ void test_single_placeholder_trailing() { TEST_CHECK(template->names.arr[0].name.s == s + 10); TEST_CHECK(template->names.arr[0].name.len == 5); TEST_CHECK(template->names.arr[0].index == 1); + + ltm_template_free(template); } void test_single_nested() { @@ -83,6 +87,8 @@ void test_single_nested() { TEST_CHECK(nested_template->names.arr[0].name.s == s + 33); TEST_CHECK(nested_template->names.arr[0].name.len == 3); TEST_CHECK(nested_template->names.arr[0].index == 1); + + ltm_template_free(template); } void test_unclosed_placeholder() {