From 13704fae2cb7076e8f34ea4b938c33fd94452b22 Mon Sep 17 00:00:00 2001 From: Max Regan Date: Tue, 29 Nov 2022 20:57:50 -0500 Subject: [PATCH] Improve memory efficiency via plain ints for length --- include/uclisp.h | 4 +++- src/arena.c | 21 ++++++++------------- src/builtins.c | 33 ++++++++++++++++++++++++++++++++- src/evaluate.c | 6 +++++- src/lisp.h | 31 +++++++++++-------------------- src/memory.c | 10 +++++++++- src/memory.h | 3 +++ src/scope.c | 10 +++++----- src/special.c | 24 +++++++++++++++++------- src/types.h | 14 ++++++++++++++ src/utility.c | 30 ++++++++++++++++++++---------- src/utility.h | 5 ++--- test/test_e2e.c | 33 +++++++++++++++++++++++++++++++++ test/test_scope.c | 8 ++++---- test/test_utility.c | 12 ++++-------- test/testing_helpers.h | 23 +++++++++++++---------- 16 files changed, 183 insertions(+), 84 deletions(-) diff --git a/include/uclisp.h b/include/uclisp.h index d0d135b..127cce6 100644 --- a/include/uclisp.h +++ b/include/uclisp.h @@ -1,7 +1,6 @@ #ifndef _UCLISP_H_ #define _UCLISP_H_ -struct ucl_scope; struct ucl_object; struct ucl; @@ -11,6 +10,9 @@ void ucl_delete(struct ucl* state); struct ucl_object *ucl_tokenize(struct ucl* state, const char *source); struct ucl_object *ucl_parse(struct ucl* state, const char *sexp); struct ucl_object *ucl_evaluate(struct ucl* state, struct ucl_object *sexp); +struct ucl_object *ucl_evaluate_progn(struct ucl* state, struct ucl_object *sexps); +void ucl_print_obj(struct ucl_object *obj); + void ucl_gc(struct ucl* ucl); diff --git a/src/arena.c b/src/arena.c index 2899aca..a6d0b7d 100644 --- a/src/arena.c +++ b/src/arena.c @@ -4,12 +4,8 @@ #include #include -struct ucl_arena { - size_t object_size; - size_t capacity; - void *objects; - int *used_map; -}; +#include "arena.h" +#include "types.h" #define DIV_ROUND_UP(numerator, denominator) \ (((numerator) + (denominator) - 1) / (denominator)) @@ -24,6 +20,8 @@ struct ucl_arena *ucl_arena_create(size_t object_size, size_t capacity) { arena->capacity = capacity; arena->objects = malloc(capacity * object_size); arena->used_map = malloc(used_map_size); + arena->stats.used = 0; + arena->stats.freed = 0; memset(arena->used_map, 0, used_map_size); @@ -49,7 +47,6 @@ void ucl_arena_map(struct ucl_arena *arena, void (*map_function)(struct ucl_aren } } -int total_arena_gets = 0; void *ucl_arena_get(struct ucl_arena *arena) { int used_map_ints = DIV_ROUND_UP(arena->capacity, INT_BITS); @@ -67,7 +64,7 @@ void *ucl_arena_get(struct ucl_arena *arena) { return NULL; } arena->used_map[i] |= 1 << bit_index; - total_arena_gets++; + arena->stats.used += 1; return (char *) arena->objects + (index * arena->object_size); } @@ -77,7 +74,6 @@ void *ucl_arena_get(struct ucl_arena *arena) { int total_arena_puts = 0; void ucl_arena_put(struct ucl_arena *arena, void *object) { - if (object == NULL) { return; } @@ -91,10 +87,9 @@ void ucl_arena_put(struct ucl_arena *arena, void *object) { assert(index < arena->capacity); assert(arena->used_map[int_index] & (1 << bit_index)); - //if (arena->used_map[int_index] & (1 << bit_index)) { - total_arena_puts++; - //} - + total_arena_puts++; arena->used_map[int_index] &= ~(1 << bit_index); + arena->stats.used -= 1; + arena->stats.freed += 1; } diff --git a/src/builtins.c b/src/builtins.c index 0f4b7df..9a4a2e4 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -75,7 +75,7 @@ LISP_FUNC_2(ucl_builtin_nth, state, scope, n, list) { UCL_COND_OR_RET_ERROR(n->type == UCL_TYPE_INT, "First argument to nth must be an integer"); UCL_COND_OR_RET_ERROR(list->type == UCL_TYPE_CELL, "Second argument to nth must be a list"); - return ucl_list_nth(state, list, n->integer); + return ucl_list_nth(list, n->integer); } LISP_FUNC_2(ucl_builtin_add, state, scope, arg0, arg1) { @@ -272,6 +272,30 @@ LISP_FUNC_2(ucl_builtin_append, state, scope, list, elem) { return list; } +LISP_FUNC_0(ucl_builtin_gc_obj_freed, state, scope) { + return ucl_int_create(state, state->obj_arena->stats.freed); +} + +LISP_FUNC_0(ucl_builtin_gc_obj_used, state, scope) { + return ucl_int_create(state, state->obj_arena->stats.used); +} + +LISP_FUNC_0(ucl_builtin_gc_obj_remaining, state, scope) { + return ucl_int_create(state, state->obj_arena->capacity - state->obj_arena->stats.used); +} + +LISP_FUNC_0(ucl_builtin_gc_scope_freed, state, scope) { + return ucl_int_create(state, state->scope_arena->stats.freed); +} + +LISP_FUNC_0(ucl_builtin_gc_scope_used, state, scope) { + return ucl_int_create(state, state->scope_arena->stats.used); +} + +LISP_FUNC_0(ucl_builtin_gc_scope_remaining, state, scope) { + return ucl_int_create(state, state->scope_arena->capacity - state->scope_arena->stats.used); +} + void ucl_add_builtin(struct ucl* ucl, const char *name, ucl_lisp fun) { ucl_scope_put(ucl, ucl->global_scope, name, ucl_builtin_create(ucl, fun)); } @@ -316,4 +340,11 @@ void ucl_add_builtins(struct ucl* state) { ucl_add_builtin(state, "reduce", ucl_builtin_reduce); ucl_add_builtin(state, "equal", ucl_builtin_equal); ucl_add_builtin(state, "append", ucl_builtin_append); + + ucl_add_builtin(state, "gc-obj-used", ucl_builtin_gc_obj_used); + ucl_add_builtin(state, "gc-obj-freed", ucl_builtin_gc_obj_freed); + ucl_add_builtin(state, "gc-obj-remaining", ucl_builtin_gc_obj_remaining); + ucl_add_builtin(state, "gc-scope-used", ucl_builtin_gc_scope_used); + ucl_add_builtin(state, "gc-scope-freed", ucl_builtin_gc_scope_freed); + ucl_add_builtin(state, "gc-scope-remaining", ucl_builtin_gc_scope_remaining); } diff --git a/src/evaluate.c b/src/evaluate.c index 938ff22..4db3f8b 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -30,7 +30,7 @@ struct ucl_object *ucl_evaluate_builtin_form(struct ucl *state, struct ucl_scope struct ucl_object *fun_forms = ucl_cdr(state, fun); int i = 0; FOREACH_LIST(fun_arg_syms, iter, sym) { - ucl_scope_put(state, fun_scope, sym->symbol, ucl_list_nth(state, evaluated_list, i + 1)); + ucl_scope_put(state, fun_scope, sym->symbol, ucl_list_nth(evaluated_list, i + 1)); i++; } result = ucl_progn(state, fun_scope, fun_forms); @@ -98,3 +98,7 @@ struct ucl_object *ucl_evaluate_in_scope(struct ucl *state, struct ucl_scope *sc struct ucl_object *ucl_evaluate(struct ucl *state, struct ucl_object *obj) { return ucl_evaluate_in_scope(state, state->global_scope, obj); } + +struct ucl_object *ucl_evaluate_progn(struct ucl *state, struct ucl_object *forms) { + return ucl_progn(state, state->global_scope, forms); +} diff --git a/src/lisp.h b/src/lisp.h index 659f433..e12aa86 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -21,11 +21,8 @@ void ucl_add_special(struct ucl *state, const char *name, ucl_lisp fun); #define LISP_FUNC_1(func_name, ucl_name, scope_name, arg0_name) \ static struct ucl_object *func_name##_impl(struct ucl* ucl, struct ucl_scope *scope, struct ucl_object *arg0_name); \ struct ucl_object *func_name(struct ucl* ucl, struct ucl_scope *scope, struct ucl_object *args) { \ - struct ucl_object *len_obj = ucl_list_length(ucl, args); \ - if (len_obj->type != UCL_TYPE_INT) { \ - return NULL; \ - } \ - if (len_obj->integer != 1) { \ + int len = ucl_list_length_int(args); \ + if (len != 1) { \ return NULL; \ } \ struct ucl_object *arg0 = ucl_car(ucl, args); \ @@ -37,15 +34,12 @@ void ucl_add_special(struct ucl *state, const char *name, ucl_lisp fun); #define LISP_FUNC_2(func_name, ucl_name, scope_name, arg0_name, arg1_name) \ static struct ucl_object *func_name##_impl(struct ucl* ucl, struct ucl_scope *scope, struct ucl_object *arg0_name, struct ucl_object *arg1_name); \ struct ucl_object *func_name(struct ucl* ucl, struct ucl_scope *scope, struct ucl_object *args) { \ - struct ucl_object *len_obj = ucl_list_length(ucl, args); \ - if (len_obj->type != UCL_TYPE_INT) { \ + int len = ucl_list_length_int(args); \ + if (len != 2) { \ return NULL; \ } \ - if (len_obj->integer != 2) { \ - return NULL; \ - } \ - struct ucl_object *arg0 = ucl_list_nth(ucl, args, 0); \ - struct ucl_object *arg1 = ucl_list_nth(ucl, args, 1); \ + struct ucl_object *arg0 = ucl_list_nth(args, 0); \ + struct ucl_object *arg1 = ucl_list_nth(args, 1); \ return func_name##_impl(ucl, scope, arg0, arg1); \ } \ static struct ucl_object *func_name##_impl(UNUSED struct ucl *ucl_name, UNUSED struct ucl_scope *scope, struct ucl_object *arg0_name, struct ucl_object *arg1_name) @@ -54,16 +48,13 @@ void ucl_add_special(struct ucl *state, const char *name, ucl_lisp fun); #define LISP_FUNC_3(func_name, ucl_name, scope_name, arg0_name, arg1_name, arg2_name) \ static struct ucl_object *func_name##_impl(struct ucl* ucl, struct ucl_scope *scope, struct ucl_object *arg0_name, struct ucl_object *arg1_name, struct ucl_object *arg2_name); \ struct ucl_object *func_name(struct ucl* ucl, struct ucl_scope *scope, struct ucl_object *args) { \ - struct ucl_object *len_obj = ucl_list_length(ucl, args); \ - if (len_obj->type != UCL_TYPE_INT) { \ + int len = ucl_list_length_int(args); \ + if (len != 3) { \ return NULL; \ } \ - if (len_obj->integer != 3) { \ - return NULL; \ - } \ - struct ucl_object *arg0 = ucl_list_nth(ucl, args, 0); \ - struct ucl_object *arg1 = ucl_list_nth(ucl, args, 1); \ - struct ucl_object *arg2 = ucl_list_nth(ucl, args, 2); \ + struct ucl_object *arg0 = ucl_list_nth(args, 0); \ + struct ucl_object *arg1 = ucl_list_nth(args, 1); \ + struct ucl_object *arg2 = ucl_list_nth(args, 2); \ return func_name##_impl(ucl, scope, arg0, arg1, arg2); \ } \ static struct ucl_object *func_name##_impl(UNUSED struct ucl* ucl_name, UNUSED struct ucl_scope *scope_name, struct ucl_object *arg0_name, struct ucl_object *arg1_name, struct ucl_object *arg2_name) diff --git a/src/memory.c b/src/memory.c index 2cc6ca9..de21228 100644 --- a/src/memory.c +++ b/src/memory.c @@ -79,6 +79,14 @@ struct ucl_object *ucl_special_create(struct ucl* ucl, ucl_lisp special) return obj; } +void ucl_object_pin(struct ucl_object *obj) { + obj->flags |= UCL_OBJ_FLAG_PINNED; +} + +void ucl_object_unpinned(struct ucl_object *obj) { + obj->flags &= ~UCL_OBJ_FLAG_PINNED; +} + static struct ucl_object* ucl_object_alloc(struct ucl* ucl) { struct ucl_object *obj = ucl_arena_get(ucl->obj_arena); @@ -175,7 +183,7 @@ void ucl_gc(struct ucl* ucl) { struct ucl *ucl_create() { struct ucl *ucl = malloc(sizeof(*ucl)); - ucl->obj_arena = ucl_arena_create(sizeof(struct ucl_object), 1 << 16); + ucl->obj_arena = ucl_arena_create(sizeof(struct ucl_object), 4096); ucl->scope_arena = ucl_arena_create(sizeof(struct ucl_scope), 64); ucl->global_scope = ucl_scope_create(ucl); diff --git a/src/memory.h b/src/memory.h index 3a4eeae..9884abd 100644 --- a/src/memory.h +++ b/src/memory.h @@ -11,6 +11,9 @@ struct ucl_object *ucl_error_create(struct ucl* ucl, const char* error); struct ucl_object *ucl_builtin_create(struct ucl* ucl, ucl_lisp builtin); struct ucl_object *ucl_special_create(struct ucl* ucl, ucl_lisp special); +void ucl_object_pin(struct ucl_object *obj); +void ucl_object_unpin(struct ucl_object *obj); + void ucl_object_delete(struct ucl* ucl, struct ucl_object *obj); #endif diff --git a/src/scope.c b/src/scope.c index 6aab983..931d547 100644 --- a/src/scope.c +++ b/src/scope.c @@ -16,10 +16,10 @@ #define SCOPE_ARENA_CAPACITY 32 -static struct ucl_object *ucl_scope_get_cell(struct ucl *state, struct ucl_scope *scope, const char *name) { +static struct ucl_object *ucl_scope_get_cell(struct ucl_scope *scope, const char *name) { FOREACH_LIST(scope->list, iter, item) { assert(item->type == UCL_TYPE_CELL); - const char *item_name = ucl_list_nth(state, item, NAME_POSITION)->string; + const char *item_name = ucl_list_nth(item, NAME_POSITION)->string; if (!strcmp(name, item_name)) { return item; } @@ -29,7 +29,7 @@ static struct ucl_object *ucl_scope_get_cell(struct ucl *state, struct ucl_scope } struct ucl_object *ucl_scope_get(struct ucl *state, struct ucl_scope *scope, const char *name) { - struct ucl_object *cell = ucl_scope_get_cell(state, scope, name); + struct ucl_object *cell = ucl_scope_get_cell(scope, name); if (cell == NULL) { if (scope->parent == NULL) { // TODO: Include the symbol name @@ -38,11 +38,11 @@ struct ucl_object *ucl_scope_get(struct ucl *state, struct ucl_scope *scope, con return ucl_scope_get(state, scope->parent, name); } } - return ucl_list_nth(state, cell, DATA_POSITION); + return ucl_list_nth(cell, DATA_POSITION); } void ucl_scope_put(struct ucl *state, struct ucl_scope *scope, const char *name, struct ucl_object *obj) { - struct ucl_object *cell = ucl_scope_get_cell(state, scope, name); + struct ucl_object *cell = ucl_scope_get_cell(scope, name); if (cell == NULL) { ucl_list_append(state, scope->list, diff --git a/src/special.c b/src/special.c index 8a45560..49d10ed 100644 --- a/src/special.c +++ b/src/special.c @@ -37,7 +37,7 @@ struct ucl_object *ucl_special_let(struct ucl *state, struct ucl_scope *scope, s struct ucl_object *ucl_special_if(struct ucl *state, struct ucl_scope *scope, struct ucl_object *args) { // TODO: Check arguments struct ucl_object *cond = ucl_car(state, args); - struct ucl_object *true_form = ucl_list_nth(state, args, 1); + struct ucl_object *true_form = ucl_list_nth(args, 1); struct ucl_object *false_forms = ucl_cdr(state, ucl_cdr(state, args)); struct ucl_object *cond_result = ucl_evaluate_in_scope(state, scope, cond); @@ -57,7 +57,7 @@ struct ucl_object *ucl_special_defun(struct ucl *state, struct ucl_scope *scope, return ucl_error_create(state, "First argument to defun must be a symbol"); } - struct ucl_object *fun_args = ucl_list_nth(state, args, 1); + struct ucl_object *fun_args = ucl_list_nth(args, 1); if (fun_args->type != UCL_TYPE_CELL) { // TODO: Check that the list contains only symbols return ucl_error_create(state, "Second argument to defun must be a list of symbols"); @@ -75,7 +75,7 @@ struct ucl_object *ucl_special_defun(struct ucl *state, struct ucl_scope *scope, struct ucl_object *ucl_special_lambda(struct ucl *state, UNUSED struct ucl_scope *scope, struct ucl_object *args) { // TODO: Check arguments - struct ucl_object *fun_args = ucl_list_nth(state, args, 0); + struct ucl_object *fun_args = ucl_list_nth(args, 0); if (fun_args->type != UCL_TYPE_CELL) { // TODO: Check that the list contains only symbols return ucl_error_create(state, "First argument to lambda must be a list of symbols"); @@ -90,19 +90,27 @@ struct ucl_object *ucl_special_dotimes(struct ucl *state, struct ucl_scope *scop struct ucl_object *body = ucl_cdr(state, args); struct ucl_object *var = ucl_car(state, assignment); - struct ucl_object *times = ucl_evaluate_in_scope(state, scope, ucl_list_nth(state, assignment, 1)); + struct ucl_object *times = ucl_evaluate_in_scope(state, scope, ucl_list_nth(assignment, 1)); UCL_COND_OR_RET_ERROR(var->type == UCL_TYPE_SYMBOL, "'var' argument to dotimes must be an symbol"); UCL_COND_OR_RET_ERROR(times->type == UCL_TYPE_INT, "'times' argument to dotimes must be an int"); struct ucl_scope *let_scope = ucl_scope_create_child(state, scope); + struct ucl_object *iter = ucl_int_create(state, 0); + ucl_scope_put(state, let_scope, var->symbol, var); + for (int i = 0; i < times->integer; i++) { - ucl_scope_put(state, let_scope, var->symbol, ucl_int_create(state, i)); + iter->integer = i; struct ucl_object *iterval = ucl_progn(state, let_scope, body); UCL_RET_IF_ERROR(iterval); + // TODO: don't make this an error, just rebind? + /* UCL_COND_OR_RET_ERROR(ucl_scope_get(state, let_scope, var->symbol)->type == UCL_TYPE_INT, */ + /* "dotimes iterator was re-bound to a non-integer type"); */ } + // TODO: cleanup scopes on error + ucl_scope_delete(state, let_scope); return ucl_nil_create(state); } @@ -112,7 +120,7 @@ struct ucl_object *ucl_special_dolist(struct ucl *state, struct ucl_scope *scope struct ucl_object *body = ucl_cdr(state, args); struct ucl_object *var = ucl_car(state, assignment); - struct ucl_object *list = ucl_evaluate_in_scope(state, scope, ucl_list_nth(state, assignment, 1)); + struct ucl_object *list = ucl_evaluate_in_scope(state, scope, ucl_list_nth(assignment, 1)); UCL_COND_OR_RET_ERROR(var->type == UCL_TYPE_SYMBOL, "'var' argument to dolist must be an symbol"); UCL_COND_OR_RET_ERROR(list->type == UCL_TYPE_CELL, "'list' argument to dolist must be a list"); @@ -128,6 +136,8 @@ struct ucl_object *ucl_special_dolist(struct ucl *state, struct ucl_scope *scope UCL_RET_IF_ERROR(iterval); } + // TODO: cleanup scopes on error + ucl_scope_delete(state, let_scope); return ucl_nil_create(state); } @@ -155,7 +165,7 @@ struct ucl_object *ucl_special_setq(struct ucl *state, struct ucl_scope *scope, return ucl_error_create(state, "First argument to setq must be a symbol"); } - struct ucl_object *value = ucl_evaluate_in_scope(state, scope, ucl_list_nth(state, args, 1)); + struct ucl_object *value = ucl_evaluate_in_scope(state, scope, ucl_list_nth(args, 1)); UCL_RET_IF_ERROR(value); ucl_scope_put(state, scope, sym->symbol, value); diff --git a/src/types.h b/src/types.h index 25317f7..a9cce81 100644 --- a/src/types.h +++ b/src/types.h @@ -1,6 +1,8 @@ #ifndef _UCLISP_TYPES_H_ #define _UCLISP_TYPES_H_ +#include + struct ucl; struct ucl_scope; struct ucl_cell; @@ -48,6 +50,18 @@ struct ucl_scope { struct ucl_scope *parent; }; +struct ucl_arena { + size_t object_size; + size_t capacity; + void *objects; + int *used_map; + + struct { + size_t used; + size_t freed; + } stats; +}; + struct ucl { struct ucl_scope *global_scope; diff --git a/src/utility.c b/src/utility.c index 8e800bd..c0a1a30 100644 --- a/src/utility.c +++ b/src/utility.c @@ -52,31 +52,41 @@ struct ucl_object *ucl_predicate(struct ucl* state, bool value) { } } -struct ucl_object *ucl_list_length(struct ucl *state, struct ucl_object *list) { - UCL_COND_OR_RET_ERROR( - list != NULL && list->type == UCL_TYPE_CELL, - "Invalid type of argument 0 to 'ucl_list_length'"); - - struct ucl_object *node = list; - if (list->cell.car == NULL) { - return ucl_int_create(state, 0); +int ucl_list_length_int(struct ucl_object *list) { + if (list == NULL || list->type != UCL_TYPE_CELL) { + return -1; } + if (list->cell.car == NULL) { + return 0; + } + + struct ucl_object *node = list; int length = 1; while (node->cell.cdr != NULL) { node = node->cell.cdr; length++; } + return length; +} + +struct ucl_object *ucl_list_length(struct ucl *state, struct ucl_object *list) { + int length = ucl_list_length_int(list); + + UCL_COND_OR_RET_ERROR( + length >= 0, + "Invalid type of argument 0 to 'ucl_list_length'"); + return ucl_int_create(state, length); } -struct ucl_object *ucl_list_nth(struct ucl *state, struct ucl_object *list, int n) { +struct ucl_object *ucl_list_nth(struct ucl_object *list, int n) { UCL_COND_OR_RET_ERROR( list != NULL && list->type == UCL_TYPE_CELL, "Invalid type of argument 0 to 'ucl_list_nth'"); - int length = ucl_list_length(state, list)->integer; + int length = ucl_list_length_int(list); UCL_COND_OR_RET_ERROR(length > n, "Position n >= list length in ucl_list_nth"); UCL_COND_OR_RET_ERROR(n >= 0, "Index to ucl_list_nth was less that zero"); diff --git a/src/utility.h b/src/utility.h index d3ee0cf..9ee38f5 100644 --- a/src/utility.h +++ b/src/utility.h @@ -14,15 +14,14 @@ struct ucl_object *ucl_equal(struct ucl* state, struct ucl_object *arg0, struct struct ucl_object* ucl_car(struct ucl* state, struct ucl_object *list); struct ucl_object* ucl_cdr(struct ucl* state, struct ucl_object *list); +int ucl_list_length_int(struct ucl_object *list); struct ucl_object* ucl_list_length(struct ucl* state, struct ucl_object *list); -struct ucl_object* ucl_list_nth(struct ucl* state, struct ucl_object *list, int n); +struct ucl_object* ucl_list_nth(struct ucl_object *list, int n); struct ucl_object* ucl_list_append(struct ucl* state, struct ucl_object *list, struct ucl_object *obj); struct ucl_object* ucl_tuple_create(struct ucl* state, struct ucl_object *obj0, struct ucl_object *obj1); struct ucl_object *ucl_progn(struct ucl* state, struct ucl_scope *scope, struct ucl_object *forms); -void ucl_print_obj(struct ucl_object *obj); - struct ucl_object* ucl_evaluate_in_scope(struct ucl *state, struct ucl_scope *scope, struct ucl_object* sexp); void ucl_add_builtins(struct ucl *state); diff --git a/test/test_e2e.c b/test/test_e2e.c index 4fe950e..62c2511 100644 --- a/test/test_e2e.c +++ b/test/test_e2e.c @@ -225,6 +225,36 @@ void test_complex(void) { TEST_ASSERT_OBJ_INT_V(response, 9); } +void test_memory_perf_low(void) { + response = eval("(defun fib (n) (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))"); + + TEST_ASSERT_OBJ_SYMBOL_V(response, "fib"); + + response = eval("(fib 4)"); + + TEST_ASSERT_OBJ_INT_V(response, 3); +} + +void test_memory_perf_medium(void) { + response = eval("(defun fib (n) (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))"); + + TEST_ASSERT_OBJ_SYMBOL_V(response, "fib"); + + response = eval("(fib 10)"); + + TEST_ASSERT_OBJ_INT_V(response, 55); +} + +void test_memory_perf_high(void) { + response = eval("(defun fib (n) (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))"); + + TEST_ASSERT_OBJ_SYMBOL_V(response, "fib"); + + response = eval("(fib 8)"); + + TEST_ASSERT_OBJ_INT_V(response, 21); +} + int main(void) { UNITY_BEGIN(); @@ -254,6 +284,9 @@ int main(void) { RUN_TEST(test_nth_oob); RUN_TEST(test_eval_defun_gc); RUN_TEST(test_complex); + RUN_TEST(test_memory_perf_low); + RUN_TEST(test_memory_perf_medium); + RUN_TEST(test_memory_perf_high); return UNITY_END(); } diff --git a/test/test_scope.c b/test/test_scope.c index fbaace3..ddeb89d 100644 --- a/test/test_scope.c +++ b/test/test_scope.c @@ -50,11 +50,11 @@ static void test_put_modify_get(void) { ucl_list_append(state, obj, ucl_string_create(state, "quux")); response = ucl_scope_get(state, scope, "foo"); - TEST_ASSERT_OBJ_STRING(ucl_list_nth(state, response, 2)); - TEST_ASSERT_EQUAL_STRING(ucl_list_nth(state, response, 2)->string, "quux"); + TEST_ASSERT_OBJ_STRING(ucl_list_nth(response, 2)); + TEST_ASSERT_EQUAL_STRING(ucl_list_nth(response, 2)->string, "quux"); - TEST_ASSERT_OBJ_STRING(ucl_list_nth(state, obj, 2)); - TEST_ASSERT_EQUAL_STRING(ucl_list_nth(state, obj, 2)->string, "quux"); + TEST_ASSERT_OBJ_STRING(ucl_list_nth(obj, 2)); + TEST_ASSERT_EQUAL_STRING(ucl_list_nth(obj, 2)->string, "quux"); } diff --git a/test/test_utility.c b/test/test_utility.c index 6776758..a6ec76e 100644 --- a/test/test_utility.c +++ b/test/test_utility.c @@ -134,20 +134,19 @@ static void test_list_append_list() { } static void test_list_nth_nil_0() { - response = ucl_list_nth(state, ucl_nil_create(state), 0); + response = ucl_list_nth(ucl_nil_create(state), 0); TEST_ASSERT_OBJ_ERROR(response); } static void test_list_nth_nil_1() { - response = ucl_list_nth(state, ucl_nil_create(state), 1); + response = ucl_list_nth(ucl_nil_create(state), 1); TEST_ASSERT_OBJ_ERROR(response); } static void test_list_nth_list_0() { response = ucl_list_nth( - state, ucl_tuple_create(state, ucl_t_create(state), ucl_string_create(state, "foo")), @@ -158,7 +157,6 @@ static void test_list_nth_list_0() { static void test_list_nth_list_1() { response = ucl_list_nth( - state, ucl_tuple_create(state, ucl_t_create(state), ucl_string_create(state, "foo")), @@ -168,20 +166,19 @@ static void test_list_nth_list_1() { } static void test_list_nth_t_0() { - response = ucl_list_nth(state, ucl_t_create(state), 1); + response = ucl_list_nth(ucl_t_create(state), 1); TEST_ASSERT_OBJ_ERROR(response); } static void test_list_nth_bounds_0() { - response = ucl_list_nth(state, ucl_nil_create(state), 0); + response = ucl_list_nth(ucl_nil_create(state), 0); TEST_ASSERT_OBJ_ERROR(response); } static void test_list_nth_bounds_1() { response = ucl_list_nth( - state, ucl_cell_create(state, ucl_nil_create(state), NULL), @@ -192,7 +189,6 @@ static void test_list_nth_bounds_1() { static void test_list_nth_bounds_2() { response = ucl_list_nth( - state, ucl_tuple_create(state, ucl_nil_create(state), ucl_nil_create(state)), diff --git a/test/testing_helpers.h b/test/testing_helpers.h index 70d2968..826a3c9 100644 --- a/test/testing_helpers.h +++ b/test/testing_helpers.h @@ -40,21 +40,24 @@ } while (0) #define TEST_ASSERT_OBJ_LIST(obj) \ - TEST_ASSERT_EQUAL_MESSAGE(UCL_TYPE_CELL, obj->type, "Expected cell type") + TEST_ASSERT_EQUAL_MESSAGE(UCL_TYPE_CELL, (obj)->type, "Expected cell type") -#define TEST_ASSERT_LIST_LEN(list, len) \ - do { \ - TEST_ASSERT_OBJ_LIST(list); \ - TEST_ASSERT_EQUAL(len, ucl_list_length(state, list)->integer); \ +#define TEST_ASSERT_LIST_LEN(list, len) \ + do { \ + TEST_ASSERT_OBJ_LIST(list); \ + TEST_ASSERT_EQUAL_MESSAGE(len, ucl_list_length(state, list)->integer, "Unexpected list length"); \ } while(0) #define TEST_ASSERT_NIL(obj) \ - TEST_ASSERT_LIST_LEN(obj, 0) + do { \ + TEST_ASSERT_OBJ_LIST(obj); \ + TEST_ASSERT_LIST_LEN(obj, 0); \ + } while(0) -#define TEST_ASSERT_T(obj) \ - do { \ - TEST_ASSERT_OBJ_SYMBOL(obj); \ - TEST_ASSERT_EQUAL_STRING(obj->symbol, "t"); \ +#define TEST_ASSERT_T(obj) \ + do { \ + TEST_ASSERT_OBJ_SYMBOL(obj); \ + TEST_ASSERT_EQUAL_STRING((obj)->symbol, "t"); \ } while(0) #endif