351 lines
9.2 KiB
C
351 lines
9.2 KiB
C
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#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;
|
|
}
|