#include #include #include #include "ltm/common.h" #include "ltm/template.h" #include "ltm/template_internal.h" ltm_err ltm_instance_block_init(ltm_instance_block **out) { ltm_instance_block *block = calloc(1, sizeof(ltm_instance_block)); if (block == NULL) { return ltm_err_failed_alloc; } *out = block; return ltm_err_ok; } ltm_err ltm_instance_init(ltm_instance **out) { ltm_instance *instance = calloc(1, sizeof(ltm_instance)); if (instance == NULL) { return ltm_err_failed_alloc; } *out = instance; 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_file_owned: fclose(block->data.ptr); break; case ltm_instance_block_type_nested: ltm_instance_free(block->data.ptr); break; case ltm_instance_block_type_file: case ltm_instance_block_type_fn: 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; LTM_RES(ltm_instance_init(&instance)); instance->template = template; ltm_instance_block ***vars = NULL; 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; } for (size_t block_index = 0; block_index < template->blocks.len; block_index++) { ltm_template_block *template_block = &template->blocks.arr[block_index]; switch (template_block->type) { case ltm_template_block_type_literal: { ltm_instance_block *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; block->data.len = template_block->data.len; if (instance->blocks.head == NULL) { instance->blocks.head = block; instance->blocks.tail = block; instance->blocks.current = block; } else { instance->blocks.tail->next = block; instance->blocks.tail = block; } } break; case ltm_template_block_type_var: case ltm_template_block_type_nested: // Account for the possibility a template starts with a placeholder *vars = instance->blocks.tail != NULL ? &instance->blocks.tail->next : &instance->blocks.head; vars++; break; } } *out = instance; return ltm_err_ok; } ltm_err ltm_instance_block_add(ltm_instance_block **out, ltm_instance *instance, const char *name, ltm_instance_block_type type) { const ltm_template *template = instance->template; ltm_template_block_name *block_name = NULL; size_t i = 0; while (i < template->names.len) { block_name = &template->names.arr[i]; if (strncmp(name, block_name->name.s, block_name->name.len) == 0) { break; } i++; } if (i == template->names.len) { return ltm_err_not_found; } ltm_template_block *template_block = &template->blocks.arr[block_name->index]; bool correct_type = false; switch (type) { case ltm_instance_block_type_nested: correct_type = template_block->type == ltm_template_block_type_nested; break; case ltm_instance_block_type_buf: case ltm_instance_block_type_buf_owned: case ltm_instance_block_type_file: case ltm_instance_block_type_file_owned: case ltm_instance_block_type_fn: correct_type = template_block->type == ltm_template_block_type_var; break; } if (!correct_type) { return ltm_err_wrong_block_type; } ltm_instance_block *block; LTM_RES(ltm_instance_block_init(&block)); block->type = type; // Insert the block in the linked list block->next = *instance->vars[i]; *instance->vars[i] = block; instance->vars[i] = &block->next; *out = block; 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) { ltm_instance_block *block; LTM_RES(ltm_instance_block_add(&block, instance, name, type)); block->data.ptr = data; block->data.len = len; return ltm_err_ok; } ltm_err ltm_instance_block_add_var_fn(ltm_instance *instance, const char *name, ltm_data_fn fn, void *data, size_t len) { ltm_instance_block *block; LTM_RES(ltm_instance_block_add(&block, instance, name, ltm_instance_block_type_fn)); block->fn = fn; block->data.ptr = data; block->data.len = len; 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; ltm_template_block_name *block_name = NULL; size_t i = 0; while (i < template->names.len) { block_name = &template->names.arr[i]; if (strncmp(name, block_name->name.s, block_name->name.len) == 0) { break; } i++; } 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_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: case ltm_instance_block_type_file: case ltm_instance_block_type_file_owned: case ltm_instance_block_type_fn: 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) { *written = 0; 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], &((char *)current->data.ptr)[instance->written], 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_file: case ltm_instance_block_type_file_owned: { size_t cap = LTM_MIN(current->data.len - instance->written, len - *written); size_t read = fread(&buf[*written], 1, cap, current->data.ptr); if ((read == 0) && (ferror(current->data.ptr) != 0)) { return ltm_err_failed_io; } *written += read; instance->written += read; if (instance->written == current->data.len) { instance->blocks.current = current->next; instance->written = 0; } } break; case ltm_instance_block_type_fn: { size_t cap = LTM_MIN(current->data.len - instance->written, len - *written); size_t fn_written = 0; LTM_RES(current->fn(&fn_written, &buf[*written], cap, current->data.ptr)); *written += fn_written; instance->written += fn_written; 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; }