feat(ltm): initial write function

ltm
Jef Roosens 2023-12-16 20:42:07 +01:00
parent 750bee27c7
commit c26c8cf18a
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
5 changed files with 130 additions and 7 deletions

View File

@ -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 { typedef enum ltm_err {
ltm_err_ok = 0, ltm_err_ok = 0,
ltm_err_invalid_template, ltm_err_invalid_template,
ltm_err_failed_alloc, ltm_err_failed_alloc,
ltm_err_not_found, ltm_err_not_found,
ltm_err_wrong_block_type, ltm_err_wrong_block_type,
ltm_err_done,
} ltm_err; } ltm_err;
#endif #endif

View File

@ -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_err ltm_instance_block_add_nested(ltm_instance **out,
ltm_instance *instance, const char *name); 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 #endif

View File

@ -115,19 +115,23 @@ ltm_err ltm_instance_block_add_var(ltm_instance *instance, const char *name,
ltm_instance_block_type type, void *data, ltm_instance_block_type type, void *data,
size_t len) { size_t len) {
const ltm_template *template = instance->template; const ltm_template *template = instance->template;
bool matched = false;
ltm_template_block_name *block_name = NULL;
size_t i = 0; size_t i = 0;
for (i = 0; i < template->names.len && !matched; i++) {
ltm_template_block_name *block_name = &template->names.arr[i]; for (i = 0; i < template->names.len; i++) {
matched = strncmp(name, block_name->name.s, block_name->name.len) == 0; 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; 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) { if (template_block->type != ltm_template_block_type_var) {
return ltm_err_wrong_block_type; return ltm_err_wrong_block_type;
@ -191,3 +195,66 @@ ltm_err ltm_instance_block_add_nested(ltm_instance **out,
return ltm_err_ok; 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;
}

View File

@ -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 }
};

View File

@ -1,6 +1,6 @@
#include "ltm/common.h"
#include "test.h" #include "test.h"
#include "ltm/common.h"
#include "ltm/template.h" #include "ltm/template.h"
#include "ltm/template_internal.h" #include "ltm/template_internal.h"