From c26c8cf18ae27cac97697dfa4d86c93f1d5ed347 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Sat, 16 Dec 2023 20:42:07 +0100 Subject: [PATCH] feat(ltm): initial write function --- ltm/include/ltm/common.h | 4 ++ ltm/include/ltm/template.h | 19 ++++++ ltm/src/ltm_instance.c | 79 ++++++++++++++++++++-- ltm/test/instance.c | 33 +++++++++ ltm/test/{compile.c => template_compile.c} | 2 +- 5 files changed, 130 insertions(+), 7 deletions(-) create mode 100644 ltm/test/instance.c rename ltm/test/{compile.c => template_compile.c} (100%) diff --git a/ltm/include/ltm/common.h b/ltm/include/ltm/common.h index 11e6fd6..6851359 100644 --- a/ltm/include/ltm/common.h +++ b/ltm/include/ltm/common.h @@ -17,12 +17,16 @@ } \ } +#define LTM_MIN(x, y) ((x) < (y) ? (x) : (y)) +#define LTM_MAX(x, y) ((x) > (y) ? (x) : (y)) + 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_done, } ltm_err; #endif diff --git a/ltm/include/ltm/template.h b/ltm/include/ltm/template.h index d08ea09..ed802f1 100644 --- a/ltm/include/ltm/template.h +++ b/ltm/include/ltm/template.h @@ -70,4 +70,23 @@ ltm_err ltm_instance_block_add_var(ltm_instance *instance, const char *name, ltm_err ltm_instance_block_add_nested(ltm_instance **out, ltm_instance *instance, const char *name); +/** + * Calculate the size of the resulting output of this instance. + */ +size_t ltm_instance_size(const ltm_instance *instance); + +/** + * Write at most `len` bytes to the given buffer. + * + * @param written outputs how many bytes were written to `buf` + * @param buf buffer to write to + * @param len length of the buffer + * @param instance instance to write + * @return `ltm_err_ok` if write was successful but there's more to be written, + * `ltm_err_done` if everything has been written successfully, or some other + * error code + */ +ltm_err ltm_instance_write(size_t *written, char *buf, size_t len, + ltm_instance *instance); + #endif diff --git a/ltm/src/ltm_instance.c b/ltm/src/ltm_instance.c index 2754c30..0db85c2 100644 --- a/ltm/src/ltm_instance.c +++ b/ltm/src/ltm_instance.c @@ -115,19 +115,23 @@ 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; + ltm_template_block_name *block_name = NULL; 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; + + for (i = 0; i < template->names.len; i++) { + block_name = &template->names.arr[i]; + + if (strncmp(name, block_name->name.s, block_name->name.len) == 0) { + break; + } } - if (!matched) { + if (i == template->names.len) { return ltm_err_not_found; } - ltm_template_block *template_block = &template->blocks.arr[i]; + ltm_template_block *template_block = &template->blocks.arr[block_name->index]; if (template_block->type != ltm_template_block_type_var) { return ltm_err_wrong_block_type; @@ -191,3 +195,66 @@ ltm_err ltm_instance_block_add_nested(ltm_instance **out, return ltm_err_ok; } + +size_t ltm_instance_size(const ltm_instance *instance) { + size_t total = 0; + ltm_instance_block *block = instance->blocks.head; + + while (block != NULL) { + switch (block->type) { + case ltm_instance_block_type_buf: + case ltm_instance_block_type_buf_owned: + total += block->data.len; + break; + case ltm_instance_block_type_nested: + total += ltm_instance_size(block->data.ptr); + break; + } + + block = block->next; + } + + return total; +} + +ltm_err ltm_instance_write(size_t *written, char *buf, size_t len, + ltm_instance *instance) { + while ((*written < len) && (instance->blocks.current != NULL)) { + ltm_instance_block *current = instance->blocks.current; + + switch (current->type) { + case ltm_instance_block_type_buf: + case ltm_instance_block_type_buf_owned: { + size_t cap = + LTM_MIN(current->data.len - instance->written, len - *written); + memcpy(&buf[*written], current->data.ptr, cap); + *written += cap; + instance->written += cap; + + if (instance->written == current->data.len) { + instance->blocks.current = current->next; + instance->written = 0; + } + } break; + case ltm_instance_block_type_nested: { + size_t nested_written = 0; + ltm_err res = ltm_instance_write(&nested_written, &buf[*written], + len - *written, current->data.ptr); + *written += nested_written; + + switch (res) { + case ltm_err_done: + instance->blocks.current = current->next; + instance->written = 0; + break; + case ltm_err_ok: + break; + default: + return res; + } + } break; + } + } + + return instance->blocks.current == NULL ? ltm_err_done : ltm_err_ok; +} diff --git a/ltm/test/instance.c b/ltm/test/instance.c new file mode 100644 index 0000000..b2a978b --- /dev/null +++ b/ltm/test/instance.c @@ -0,0 +1,33 @@ +#include "test.h" + +#include "ltm/common.h" +#include "ltm/template.h" +#include "ltm/template_internal.h" + +void test_single_placeholder() { + const char *s = "Hello, {{ world }}!"; + + ltm_template *template; + TEST_ASSERT(ltm_template_compile(&template, s) == ltm_err_ok); + + ltm_instance *instance; + TEST_CHECK(ltm_template_instantiate(&instance, template) == ltm_err_ok); + + TEST_CHECK(ltm_instance_block_add_var(instance, "world", ltm_instance_block_type_buf, "World", 5) == ltm_err_ok); + + TEST_CHECK(ltm_instance_size(instance) == 13); + + char buf[13]; + size_t written = 0; + TEST_CHECK(ltm_instance_write(&written, buf, 13, instance) == ltm_err_done); + TEST_CHECK(written == 13); + TEST_CHECK(strncmp(buf, "Hello, World!", 13) == 0); + + ltm_instance_free(instance); + ltm_template_free(template); +} + +TEST_LIST = { + { "instance single placeholder", test_single_placeholder }, + { NULL, NULL } +}; diff --git a/ltm/test/compile.c b/ltm/test/template_compile.c similarity index 100% rename from ltm/test/compile.c rename to ltm/test/template_compile.c index db02e13..127b5fc 100644 --- a/ltm/test/compile.c +++ b/ltm/test/template_compile.c @@ -1,6 +1,6 @@ -#include "ltm/common.h" #include "test.h" +#include "ltm/common.h" #include "ltm/template.h" #include "ltm/template_internal.h"