Compare commits

..

No commits in common. "c7a1fec6c276e6d14f95fce6afd6efab3218b9ef" and "959be1026497b50b89501e39d9fd55feb21edc51" have entirely different histories.

7 changed files with 20 additions and 321 deletions

View File

@ -17,16 +17,10 @@
} \ } \
} }
#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_wrong_block_type,
ltm_err_done,
} ltm_err; } ltm_err;
#endif #endif

View File

@ -28,12 +28,6 @@ ltm_err ltm_template_compile(ltm_template **out, const char *template);
ltm_err ltm_template_compile_n(ltm_template **out, const char *template, ltm_err ltm_template_compile_n(ltm_template **out, const char *template,
size_t len); 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. * Represents a specific instance of a template.
*/ */
@ -45,15 +39,8 @@ typedef struct ltm_instance ltm_instance;
ltm_err ltm_template_instantiate(ltm_instance **out, ltm_err ltm_template_instantiate(ltm_instance **out,
const ltm_template *template); 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 { typedef enum ltm_instance_block_type {
ltm_instance_block_type_buf = 0, ltm_instance_block_type_buf = 0,
ltm_instance_block_type_buf_owned,
ltm_instance_block_type_nested,
} ltm_instance_block_type; } ltm_instance_block_type;
/** /**
@ -70,23 +57,4 @@ 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

@ -1,7 +1,3 @@
#include <stdbool.h>
#include <string.h>
#include "ltm/common.h"
#include "ltm/template.h" #include "ltm/template.h"
#include "ltm/template_internal.h" #include "ltm/template_internal.h"
@ -29,32 +25,6 @@ ltm_err ltm_instance_init(ltm_instance **out) {
return ltm_err_ok; 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, ltm_err ltm_template_instantiate(ltm_instance **out,
const ltm_template *template) { const ltm_template *template) {
ltm_instance *instance; ltm_instance *instance;
@ -62,20 +32,15 @@ ltm_err ltm_template_instantiate(ltm_instance **out,
instance->template = template; instance->template = template;
ltm_instance_block ***vars = NULL; ltm_instance_block ***vars =
malloc(template->names.len * sizeof(ltm_instance_block **));
if (template->names.len > 0) { if (vars == NULL) {
vars = malloc(template->names.len * sizeof(ltm_instance_block **)); return ltm_err_failed_alloc;
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; for (size_t block_index = 0; block_index < template->blocks.len;
block_index++) { block_index++) {
ltm_template_block *template_block = &template->blocks.arr[block_index]; ltm_template_block *template_block = &template->blocks.arr[block_index];
@ -83,7 +48,7 @@ ltm_err ltm_template_instantiate(ltm_instance **out,
switch (template_block->type) { switch (template_block->type) {
case ltm_template_block_type_literal: { case ltm_template_block_type_literal: {
ltm_instance_block *block; ltm_instance_block *block;
LTM_RES2(ltm_instance_block_init(&block), ltm_instance_free(instance)); LTM_RES(ltm_instance_block_init(&block));
block->type = ltm_instance_block_type_buf; block->type = ltm_instance_block_type_buf;
block->data.ptr = template_block->data.ptr; block->data.ptr = template_block->data.ptr;
@ -102,7 +67,6 @@ ltm_err ltm_template_instantiate(ltm_instance **out,
case ltm_template_block_type_nested: case ltm_template_block_type_nested:
*vars = &instance->blocks.tail->next; *vars = &instance->blocks.tail->next;
vars++; vars++;
break;
} }
} }
@ -110,151 +74,3 @@ ltm_err ltm_template_instantiate(ltm_instance **out,
return ltm_err_ok; 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;
ltm_template_block_name *block_name = NULL;
size_t i = 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 (i == template->names.len) {
return ltm_err_not_found;
}
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;
}
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;
}
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

@ -13,30 +13,6 @@ ltm_err ltm_template_init(ltm_template **out) {
return ltm_err_ok; 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, ltm_err ltm_template_name_append(ltm_template *template, const char *s,
size_t len, size_t index) { size_t len, size_t index) {
ltm_template_block_name *names = ltm_template_block_name *names =

View File

@ -104,26 +104,20 @@ ltm_err ltm_template_compile_n(ltm_template **out, const char *s, size_t len) {
while (ltm_template_next_placeholder(&ph, s, len)) { while (ltm_template_next_placeholder(&ph, s, len)) {
// Add part before placeholder as literal // Add part before placeholder as literal
if (!in_nested && (ph.start != s)) { if (!in_nested && (ph.start != s)) {
LTM_RES2(ltm_template_block_append(template, LTM_RES(ltm_template_block_append(
ltm_template_block_type_literal, template, ltm_template_block_type_literal, (void *)s, ph.start - s));
(void *)s, ph.start - s),
ltm_template_free(template));
} }
switch (ph.type) { switch (ph.type) {
// Invalid placeholders can be detected as early as possible // Invalid placeholders can be detected as early as possible
case ltm_placeholder_type_invalid: case ltm_placeholder_type_invalid:
ltm_template_free(template);
return ltm_err_invalid_template; return ltm_err_invalid_template;
case ltm_placeholder_type_var: case ltm_placeholder_type_var:
if (!in_nested) { if (!in_nested) {
LTM_RES2(ltm_template_block_append( LTM_RES(ltm_template_block_append(template, ltm_template_block_type_var,
template, ltm_template_block_type_var, NULL, 0), NULL, 0));
ltm_template_free(template)); LTM_RES(ltm_template_name_append(template, ph.name.s, ph.name.len,
LTM_RES2(ltm_template_name_append(template, ph.name.s, ph.name.len, template->blocks.len - 1));
template->blocks.len - 1),
ltm_template_free(template));
} }
break; break;
case ltm_placeholder_type_nested_start: case ltm_placeholder_type_nested_start:
@ -139,8 +133,6 @@ ltm_err ltm_template_compile_n(ltm_template **out, const char *s, size_t len) {
case ltm_placeholder_type_nested_end: case ltm_placeholder_type_nested_end:
// This means there's more nested end placeholders than nested starts // This means there's more nested end placeholders than nested starts
if (cur_nested_depth == 0) { if (cur_nested_depth == 0) {
ltm_template_free(template);
return ltm_err_invalid_template; return ltm_err_invalid_template;
} }
@ -149,22 +141,17 @@ ltm_err ltm_template_compile_n(ltm_template **out, const char *s, size_t len) {
if (in_nested && (cur_nested_depth == nested_depth)) { if (in_nested && (cur_nested_depth == nested_depth)) {
size_t nested_len = ph.start - nested_start; size_t nested_len = ph.start - nested_start;
ltm_template *nested_template; ltm_template *nested_template;
LTM_RES2( LTM_RES(
ltm_template_compile_n(&nested_template, nested_start, nested_len), ltm_template_compile_n(&nested_template, nested_start, nested_len));
ltm_template_free(template));
LTM_RES(ltm_template_block_append( LTM_RES(ltm_template_block_append(
template, ltm_template_block_type_nested, nested_template, 0)); template, ltm_template_block_type_nested, nested_template, 0));
LTM_RES2(ltm_template_name_append(template, nested_name, LTM_RES(ltm_template_name_append(template, nested_name, nested_name_len,
nested_name_len, template->blocks.len - 1));
template->blocks.len - 1),
ltm_template_free(template));
in_nested = false; in_nested = false;
} }
// We encountered a nested end without a start // We encountered a nested end without a start
else if (!in_nested) { else if (!in_nested) {
ltm_template_free(template);
return ltm_err_invalid_template; return ltm_err_invalid_template;
} }
break; break;
@ -176,16 +163,13 @@ ltm_err ltm_template_compile_n(ltm_template **out, const char *s, size_t len) {
// Unfinished nested // Unfinished nested
if (in_nested) { if (in_nested) {
ltm_template_free(template);
return ltm_err_invalid_template; return ltm_err_invalid_template;
} }
// Add remaining trailing literal // Add remaining trailing literal
if (len > 0) { if (len > 0) {
LTM_RES2(ltm_template_block_append( LTM_RES(ltm_template_block_append(template, ltm_template_block_type_literal,
template, ltm_template_block_type_literal, (void *)s, len), (void *)s, len));
ltm_template_free(template));
} }
*out = template; *out = template;

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"
@ -23,8 +23,6 @@ void test_single_placeholder() {
TEST_CHECK(template->names.arr[0].name.s == s + 10); 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].name.len == 5);
TEST_CHECK(template->names.arr[0].index == 1); TEST_CHECK(template->names.arr[0].index == 1);
ltm_template_free(template);
} }
void test_single_placeholder_trailing() { void test_single_placeholder_trailing() {
@ -50,8 +48,6 @@ void test_single_placeholder_trailing() {
TEST_CHECK(template->names.arr[0].name.s == s + 10); 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].name.len == 5);
TEST_CHECK(template->names.arr[0].index == 1); TEST_CHECK(template->names.arr[0].index == 1);
ltm_template_free(template);
} }
void test_single_nested() { void test_single_nested() {
@ -87,8 +83,6 @@ void test_single_nested() {
TEST_CHECK(nested_template->names.arr[0].name.s == s + 33); 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].name.len == 3);
TEST_CHECK(nested_template->names.arr[0].index == 1); TEST_CHECK(nested_template->names.arr[0].index == 1);
ltm_template_free(template);
} }
void test_unclosed_placeholder() { void test_unclosed_placeholder() {

View File

@ -1,33 +0,0 @@
#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 }
};