aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDanny van Kooten <dannyvankooten@users.noreply.github.com>2020-03-17 15:12:06 +0100
committerDanny van Kooten <dannyvankooten@users.noreply.github.com>2020-03-17 15:12:06 +0100
commit92be46c849a53968e469e544f84820e2fedf6dac (patch)
tree962c43a687808a2fd0e92679784f36c8675bc94d
parent4624d6d369e8e5ce9a887acfe27be90fedf3d1ed (diff)
downloadunja-92be46c849a53968e469e544f84820e2fedf6dac.tar.gz
unja-92be46c849a53968e469e544f84820e2fedf6dac.zip
allocate precisely for output buffer
-rw-r--r--src/template.c98
-rw-r--r--tests/test.h2
-rw-r--r--tests/test_template.c34
3 files changed, 83 insertions, 51 deletions
diff --git a/src/template.c b/src/template.c
index d3765a1..0529c62 100644
--- a/src/template.c
+++ b/src/template.c
@@ -3,6 +3,35 @@
#include "vendor/mpc.h"
#include "template.h"
+enum unja_object_type {
+ OBJ_NULL,
+ OBJ_INT,
+ OBJ_STRING,
+};
+
+struct unja_object {
+ enum unja_object_type type;
+ int integer;
+ char *string;
+};
+
+struct unja_object null_object = {
+ .type = OBJ_NULL,
+};
+
+struct buffer {
+ int size;
+ int cap;
+ char *string;
+};
+
+void buffer_reserve(struct buffer *buf, int l) {
+ if (buf->size + l >= buf->cap) {
+ buf->cap *= 2;
+ buf->string = realloc(buf->string, buf->size + l);
+ }
+}
+
char *read_file(char *filename) {
char *input = malloc(BUFSIZ);
unsigned int size = 0;
@@ -40,23 +69,6 @@ char *trim_leading_whitespace(char *str) {
return str;
}
-enum unja_object_type {
- OBJ_NULL,
- OBJ_INT,
- OBJ_STRING,
-};
-
-struct unja_object {
- enum unja_object_type type;
- int integer;
- char *string;
-};
-
-struct unja_object _null_object = {
- .type = OBJ_NULL,
-};
-struct unja_object *null_object = &_null_object;
-
struct unja_object *make_string_object(char *value, char *value2) {
struct unja_object *obj = malloc(sizeof *obj);
obj->type = OBJ_STRING;
@@ -82,18 +94,20 @@ struct unja_object *make_int_object(int value) {
}
-void object_to_str(char *dest, struct unja_object *obj) {
- char buf[64];
+void eval_object(struct buffer *buf, struct unja_object *obj) {
+ char tmp[64];
switch (obj->type) {
case OBJ_NULL:
break;
case OBJ_STRING:
- strcat(dest, obj->string);
+ buffer_reserve(buf, strlen(obj->string));
+ strcat(buf->string, obj->string);
break;
case OBJ_INT:
- sprintf(buf, "%d", obj->integer);
- strcat(dest, buf);
+ sprintf(tmp, "%d", obj->integer);
+ buffer_reserve(buf, strlen(tmp));
+ strcat(buf->string, tmp);
break;
}
}
@@ -134,7 +148,7 @@ struct unja_object *eval_expression_value(mpc_ast_t* node, struct hashmap *ctx)
if (strstr(node->tag, "symbol|")) {
/* Return empty string if no context was passed. Should probably signal error here. */
if (ctx == NULL) {
- return null_object;
+ return &null_object;
}
char *key = node->contents;
@@ -142,7 +156,7 @@ struct unja_object *eval_expression_value(mpc_ast_t* node, struct hashmap *ctx)
/* TODO: Handle unexisting symbols (returns NULL currently) */
if (value == NULL) {
- return null_object;
+ return &null_object;
}
return make_string_object(value, NULL);
} else if(strstr(node->tag, "number|")) {
@@ -151,7 +165,7 @@ struct unja_object *eval_expression_value(mpc_ast_t* node, struct hashmap *ctx)
return make_string_object(node->children[1]->contents, NULL);
}
- return null_object;
+ return &null_object;
}
@@ -192,14 +206,14 @@ struct unja_object *eval_expression(mpc_ast_t* expr, struct hashmap *ctx) {
return eval_expression_value(left_node, ctx);
}
-int eval(char *dest, mpc_ast_t* t, struct hashmap *ctx) {
+int eval(struct buffer *buf, mpc_ast_t* t, struct hashmap *ctx) {
static int trim_whitespace = 0;
// eval print statement
if (strstr(t->tag, "content|print")) {
// maybe eat whitespace going backward
if (strstr(t->children[1]->contents, "-")) {
- dest = trim_trailing_whitespace(dest);
+ buf->string = trim_trailing_whitespace(buf->string);
}
/* set flag for next eval() to trim leading whitespace from text */
@@ -209,7 +223,7 @@ int eval(char *dest, mpc_ast_t* t, struct hashmap *ctx) {
mpc_ast_t *expr = t->children[2];
struct unja_object *obj = eval_expression(expr, ctx);
- object_to_str(dest, obj);
+ eval_object(buf, obj);
object_free(obj);
return 0;
}
@@ -220,7 +234,7 @@ int eval(char *dest, mpc_ast_t* t, struct hashmap *ctx) {
struct vector *list = hashmap_resolve(ctx, iterator_key);
for (int i=0; i < list->size; i++) {
hashmap_insert(ctx, tmp_key, list->values[i]);
- eval(dest, t->children[6], ctx);
+ eval(buf, t->children[6], ctx);
}
return 0;
}
@@ -230,10 +244,10 @@ int eval(char *dest, mpc_ast_t* t, struct hashmap *ctx) {
struct unja_object *result = eval_expression(expr, ctx);
if (object_is_truthy(result)) {
- eval(dest, t->children[4], ctx);
+ eval(buf, t->children[4], ctx);
} else {
if (t->children_num > 8) {
- eval(dest, t->children[8], ctx);
+ eval(buf, t->children[8], ctx);
}
}
@@ -248,12 +262,13 @@ int eval(char *dest, mpc_ast_t* t, struct hashmap *ctx) {
trim_whitespace = 0;
}
- strcat(dest, str);
+ buffer_reserve(buf, strlen(str));
+ strcat(buf->string, str);
return 0;
}
for (int i=0; i < t->children_num; i++) {
- eval(dest, t->children[i], ctx);
+ eval(buf, t->children[i], ctx);
}
return 0;
@@ -295,8 +310,8 @@ mpc_parser_t *parser_init() {
" statement_open: \"{%\" /-? */;"
" statement_close: / *-?/ \"%}\";"
" for : <statement_open> \"for \" <symbol> \"in\" <symbol> <statement_close> <body> <statement_open> \"endfor\" <statement_close> ;"
- " block : <statement_open> \"block \" <statement_close> <statement_open> \"endblock\" <statement_close>;"
- " extends : <statement_open> \"extends \" <statement_close>;"
+ " block : <statement_open> \"block \" <symbol> <statement_close> <statement_open> \"endblock\" <statement_close>;"
+ " extends : <statement_open> \"extends \" <string> <statement_close>;"
" if : <statement_open> \"if \" <expression> <statement_close> <body> (<statement_open> \"else\" <statement_close> <body>)? <statement_open> \"endif\" <statement_close> ;"
" statement : <for> | <block> | <extends> | <if> ;"
" content : <print> | <statement> | <text> | <comment>;"
@@ -341,13 +356,14 @@ char *template(char *tmpl, struct hashmap *ctx) {
printf("\n");
#endif
- // FIXME: Allocate precisely
- char *output = malloc(strlen(tmpl) * 4);
- output[0] = '\0';
+ struct buffer buf;
+ buf.size = 0;
+ buf.cap = strlen(tmpl) + 1;
+ buf.string = malloc(buf.size);
+ buf.string[0] = '\0';
+ eval(&buf, r.output, ctx);
- eval(output, r.output, ctx);
mpc_ast_delete(r.output);
-
- return output;
+ return buf.string;
}
diff --git a/tests/test.h b/tests/test.h
index dec4ba4..44fd7fb 100644
--- a/tests/test.h
+++ b/tests/test.h
@@ -7,7 +7,7 @@
#define END_TESTS }
#define TEST(name) strcpy(current_test, #name);
#define assert_null(actual) _assert(actual == NULL, __FILE__, __LINE__, "invalid value: expected NULL, got %s", actual)
-#define assert_str(actual, expected) _assert(actual != NULL && strcmp(actual, expected) == 0, __FILE__, __LINE__, "invalid string: expected %s, got %s", expected, actual)
+#define assert_str(actual, expected) _assert(actual != NULL && strcmp(actual, expected) == 0, __FILE__, __LINE__, "invalid string: expected \"%s\", got \"%s\"", expected, actual)
#define assert(assertion, format, ...) _assert(assertion, __FILE__, __LINE__, format, ##__VA_ARGS__)
#define ARRAY_SIZE(arr) sizeof arr / sizeof arr[0]
diff --git a/tests/test_template.c b/tests/test_template.c
index 0567995..c13ef4c 100644
--- a/tests/test_template.c
+++ b/tests/test_template.c
@@ -114,18 +114,18 @@ TEST(expr_whitespace) {
}
TEST(for_block) {
- char *input = "{% for n in numbers %}{{ n }}, {% endfor %}";
+ char *input = "{% for n in names %}{{ n }}, {% endfor %}";
struct hashmap *ctx = hashmap_new();
- struct vector *numbers = vector_new(3);
- vector_push(numbers, "1");
- vector_push(numbers, "2");
- vector_push(numbers, "3");
- hashmap_insert(ctx, "numbers", numbers);
+ struct vector *names = vector_new(9);
+ vector_push(names, "John");
+ vector_push(names, "Sally");
+ vector_push(names, "Eric");
+ hashmap_insert(ctx, "names", names);
char *output = template(input, ctx);
- assert_str(output, "1, 2, 3, ");
- vector_free(numbers);
+ assert_str(output, "John, Sally, Eric, ");
+ vector_free(names);
hashmap_free(ctx);
free(output);
}
@@ -201,4 +201,20 @@ TEST(if_else_block) {
hashmap_free(ctx);
}
-END_TESTS
+TEST(buffer_alloc) {
+ /* Output a string so that output buffer is longer than template buffer,
+ to test dynamic allocation */
+ char *input = "{{ n }}";
+ struct hashmap *ctx = hashmap_new();
+ char *text = "Lorem ipsum dolor sit amet.";
+ hashmap_insert(ctx, "n", text);
+
+ char *output = template(input, ctx);
+ assert_str(output, text);
+ hashmap_free(ctx);
+ free(output);
+}
+
+
+
+END_TESTS \ No newline at end of file