diff --git a/src/Makefile.am b/src/Makefile.am index 6d04ba2..1abb548 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,6 +3,7 @@ AM_CFLAGS=--std=c11 -Wall -Werror -Wextra -pedantic lib_LIBRARIES = libapfl.a libapfl_a_SOURCES = +libapfl_a_SOURCES += alloc.c libapfl_a_SOURCES += error.c libapfl_a_SOURCES += eval.c libapfl_a_SOURCES += expr.c @@ -18,7 +19,6 @@ libapfl_a_SOURCES += source_readers.c libapfl_a_SOURCES += value.c apfl_internal_headers = -apfl_internal_headers += common.h apfl_internal_headers += hashmap.c apfl_internal_headers += internal.h apfl_internal_headers += resizable.h diff --git a/src/alloc.c b/src/alloc.c new file mode 100644 index 0000000..173beba --- /dev/null +++ b/src/alloc.c @@ -0,0 +1,72 @@ +#include +#include +#include + +#include "apfl.h" +#include "alloc.h" + +#define ALLOC_DEBUG 1 + +static void * +libc_alloc(void *opaque, void *oldptr, size_t oldsize, size_t newsize) +{ + (void)opaque; + (void)oldsize; + + if (newsize == 0) { + free(oldptr); + return NULL; + } else { + return realloc(oldptr, newsize); + } +} + +static void * +verifying_alloc(void *opaque, void *oldptr, size_t oldsize, size_t newsize) +{ + struct apfl_allocator *wrapped = opaque; + + size_t *oldptr_actual = NULL; + if (oldptr != NULL) { + oldptr_actual = (size_t *)((char *)oldptr - sizeof(size_t)); + + // This checks, if the oldsize was correct + assert(*oldptr_actual == oldsize); + } + + if (newsize == 0) { + void *newptr = ALLOCATOR_CALL(*wrapped, oldptr_actual, oldsize + sizeof(size_t), 0); + assert(newptr == NULL); + return newptr; + } + + size_t *newptr = ALLOCATOR_CALL(*wrapped, oldptr_actual, oldsize + sizeof(size_t), newsize + sizeof(size_t)); + if (newptr == NULL) { + return NULL; + } + + *newptr = newsize; + return newptr + 1; +} + +static struct apfl_allocator libc_allocator = { + .opaque = NULL, + .alloc = libc_alloc, +}; + +struct apfl_allocator +apfl_allocator_wrap_verifying(struct apfl_allocator *wrapped) +{ + return (struct apfl_allocator) { + .opaque = wrapped, + .alloc = verifying_alloc, + }; +} + +struct apfl_allocator +apfl_allocator_default(void) +{ + return ALLOC_DEBUG + ? apfl_allocator_wrap_verifying(&libc_allocator) + : libc_allocator; +} diff --git a/src/alloc.h b/src/alloc.h new file mode 100644 index 0000000..6032b5b --- /dev/null +++ b/src/alloc.h @@ -0,0 +1,29 @@ +#ifndef APFL_ALLOC_H +#define APFL_ALLOC_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define ALLOCATOR_CALL(a, oldptr, oldsize, newsize) \ + ((a).alloc((a).opaque, (oldptr), (oldsize), (newsize))) + +#define ALLOC_BYTES(a, n) (((n) == 0) ? NULL : ALLOCATOR_CALL(a, NULL, 0, (n))) +#define REALLOC_BYTES(a, ptr, old, new) ALLOCATOR_CALL(a, ptr, old, new) +#define FREE_BYTES(a, ptr, n) ALLOCATOR_CALL(a, ptr, n, 0) + +#define REALLOC_LIST(a, ptr, old, new) REALLOC_BYTES(a, ptr, sizeof(*ptr) * (old), sizeof(*ptr) * (new)) +#define ALLOC_LIST(a, T, len) (T *)ALLOC_BYTES(a, sizeof(T) * (len)) +#define FREE_LIST(a, ptr, len) FREE_BYTES(a, ptr, sizeof(*ptr) * (len)) + +#define ALLOC_OBJ(a, T) ALLOC_LIST(a, T, 1) +#define FREE_OBJ(a, ptr) FREE_LIST(a, ptr, 1) + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/apfl.h b/src/apfl.h index 7a779f1..c4ca0a0 100644 --- a/src/apfl.h +++ b/src/apfl.h @@ -9,6 +9,33 @@ extern "C" { #include #include +// Allocator + +// apfl_allocator_cb is called to (re)allocate and free memory. +// +// If called with newsize = 0, the memory of oldsize bytes pointed to by +// oldptr should be freed. If the NULL-pointer is freed, nothing should +// happen. A freeing must return the NULL-pointer and is not allowed to +// fail. +// +// If oldptr = NULL and oldsize = 0, memory of size newsize should be +// allocated and the pointer to it returned. If the allocation fails, NULL +// should be returned. +// +// Reallocation is done by calling with the old pointer, old size and new +// size. On success the new pointer should be returned and the old pointer +// (unless they're the same) no longer used. On failure, NULL should be +// returned and the oldptr should stay valid. +typedef void * (*apfl_allocator_cb) (void *opaque, void *oldptr, size_t oldsize, size_t newsize); + +struct apfl_allocator { + void *opaque; + apfl_allocator_cb alloc; +}; + +struct apfl_allocator apfl_allocator_default(void); +struct apfl_allocator apfl_allocator_wrap_verifying(struct apfl_allocator *wrapped); + typedef double apfl_number; struct apfl_position { @@ -28,6 +55,11 @@ struct apfl_string_view { struct apfl_string { char *bytes; size_t len; + + // TODO: Not sure, if it's a good idea to expose this. We now need the actual + // size of the underlying allocation tough. Maybe we should better + // shrink the cap to len wen we're done building a string? + size_t cap; }; struct apfl_refcounted_string_data; @@ -57,7 +89,7 @@ int apfl_string_view_cmp(struct apfl_string_view, struct apfl_string_view); #define apfl_string_eq(a, b) (apfl_string_cmp((a), (b)) == 0) struct apfl_string apfl_string_blank(void); -void apfl_string_deinit(struct apfl_string *); +void apfl_string_deinit(struct apfl_allocator allocator, struct apfl_string *); struct apfl_string apfl_string_move(struct apfl_string *src); /** @@ -65,15 +97,16 @@ struct apfl_string apfl_string_move(struct apfl_string *src); * Returns true on success, false otherwise (if the necessary memory could not * be allocated). */ -bool apfl_string_copy(struct apfl_string *dst, struct apfl_string_view src); +bool apfl_string_copy(struct apfl_allocator allocator, struct apfl_string *dst, struct apfl_string_view src); struct apfl_string_builder { + struct apfl_allocator allocator; char *bytes; size_t len; size_t cap; }; -void apfl_string_builder_init(struct apfl_string_builder *); +void apfl_string_builder_init(struct apfl_allocator allocator, struct apfl_string_builder *); void apfl_string_builder_deinit(struct apfl_string_builder *); bool apfl_string_builder_append(struct apfl_string_builder *, struct apfl_string_view); bool apfl_string_builder_append_byte(struct apfl_string_builder *, char byte); @@ -81,9 +114,9 @@ struct apfl_string apfl_string_builder_move_string(struct apfl_string_builder *) #define apfl_string_builder_append_cstr(builder, cstr) (apfl_string_builder_append((builder), apfl_string_view_from_cstr((cstr)))) -apfl_refcounted_string apfl_string_copy_into_new_refcounted(struct apfl_string_view); +apfl_refcounted_string apfl_string_copy_into_new_refcounted(struct apfl_allocator allocator, struct apfl_string_view); -apfl_refcounted_string apfl_string_move_into_new_refcounted(struct apfl_string *); +apfl_refcounted_string apfl_string_move_into_new_refcounted(struct apfl_allocator allocator, struct apfl_string *); /* Increases the reference of the refcounted string. * Returns the same apfl_refcounted_string value. @@ -93,12 +126,12 @@ apfl_refcounted_string apfl_refcounted_string_incref(apfl_refcounted_string); /* Unrefs the refcounted string. It is no longer allowed to use the pointer * after calling this function with it! */ -void apfl_refcounted_string_unref(apfl_refcounted_string ); +void apfl_refcounted_string_unref(struct apfl_allocator allocator, apfl_refcounted_string ); /* Like apfl_refcounted_string_unref, but accepts a pointer to a refcounted_string. * The pointed to value will be set to NULL. */ -void apfl_refcounted_string_unref_ptr(apfl_refcounted_string *); +void apfl_refcounted_string_unref_ptr(struct apfl_allocator allocator, apfl_refcounted_string *); // Tokens @@ -136,7 +169,7 @@ struct apfl_token { }; }; -void apfl_token_deinit(struct apfl_token *); +void apfl_token_deinit(struct apfl_allocator allocator, struct apfl_token *); const char *apfl_token_type_name(enum apfl_token_type); @@ -221,6 +254,7 @@ struct apfl_expr_dict_pair { struct apfl_expr_dict { struct apfl_expr_dict_pair *items; size_t len; + size_t cap; }; struct apfl_expr_call { @@ -231,6 +265,7 @@ struct apfl_expr_call { struct apfl_expr_body { struct apfl_expr *items; size_t len; + size_t cap; unsigned refcount; // TODO: Probably better to not expose refcount }; @@ -268,6 +303,7 @@ enum apfl_expr_param_type { struct apfl_expr_params { struct apfl_expr_params_item *params; size_t len; + size_t cap; }; struct apfl_expr_param { @@ -294,6 +330,7 @@ struct apfl_expr_subfunc { struct apfl_expr_complex_func { struct apfl_expr_subfunc *subfuncs; size_t len; + size_t cap; }; enum apfl_expr_assignable_type { @@ -397,30 +434,30 @@ bool apfl_expr_eq(struct apfl_expr, struct apfl_expr); // Begin deinit functions -void apfl_expr_deinit(struct apfl_expr *); -void apfl_expr_list_deinit(struct apfl_expr_list *); -void apfl_expr_list_item_deinit(struct apfl_expr_list_item *); -void apfl_expr_dict_pair_deinit(struct apfl_expr_dict_pair *); -void apfl_expr_dict_deinit(struct apfl_expr_dict *); -void apfl_expr_call_deinit(struct apfl_expr_call *); -void apfl_expr_body_unref(struct apfl_expr_body *); -void apfl_expr_const_deinit(struct apfl_expr_const *); -void apfl_expr_param_predicate_deinit(struct apfl_expr_param_predicate *); -void apfl_expr_params_deinit(struct apfl_expr_params *); -void apfl_expr_params_item_deinit(struct apfl_expr_params_item *); -void apfl_expr_param_deinit(struct apfl_expr_param *); -void apfl_expr_subfunc_deinit(struct apfl_expr_subfunc *); -void apfl_expr_complex_func_deinit(struct apfl_expr_complex_func *); -void apfl_expr_assignable_predicate_deinit(struct apfl_expr_assignable_predicate *); -void apfl_expr_assignable_list_item_deinit(struct apfl_expr_assignable_list_item *); -void apfl_expr_assignable_list_deinit(struct apfl_expr_assignable_list *); -void apfl_expr_assignable_var_or_member_dot_deinit(struct apfl_expr_assignable_var_or_member_dot *); -void apfl_expr_assignable_var_or_member_at_deinit(struct apfl_expr_assignable_var_or_member_at *); -void apfl_expr_assignable_var_or_member_deinit(struct apfl_expr_assignable_var_or_member *); -void apfl_expr_assignable_deinit(struct apfl_expr_assignable *); -void apfl_expr_assignment_deinit(struct apfl_expr_assignment *); -void apfl_expr_dot_deinit(struct apfl_expr_dot *); -void apfl_expr_at_deinit(struct apfl_expr_at *); +void apfl_expr_deinit(struct apfl_allocator allocator, struct apfl_expr *); +void apfl_expr_list_deinit(struct apfl_allocator allocator, struct apfl_expr_list *); +void apfl_expr_list_item_deinit(struct apfl_allocator allocator, struct apfl_expr_list_item *); +void apfl_expr_dict_pair_deinit(struct apfl_allocator allocator, struct apfl_expr_dict_pair *); +void apfl_expr_dict_deinit(struct apfl_allocator allocator, struct apfl_expr_dict *); +void apfl_expr_call_deinit(struct apfl_allocator allocator, struct apfl_expr_call *); +void apfl_expr_body_unref(struct apfl_allocator allocator, struct apfl_expr_body *); +void apfl_expr_const_deinit(struct apfl_allocator allocator, struct apfl_expr_const *); +void apfl_expr_param_predicate_deinit(struct apfl_allocator allocator, struct apfl_expr_param_predicate *); +void apfl_expr_params_deinit(struct apfl_allocator allocator, struct apfl_expr_params *); +void apfl_expr_params_item_deinit(struct apfl_allocator allocator, struct apfl_expr_params_item *); +void apfl_expr_param_deinit(struct apfl_allocator allocator, struct apfl_expr_param *); +void apfl_expr_subfunc_deinit(struct apfl_allocator allocator, struct apfl_expr_subfunc *); +void apfl_expr_complex_func_deinit(struct apfl_allocator allocator, struct apfl_expr_complex_func *); +void apfl_expr_assignable_predicate_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_predicate *); +void apfl_expr_assignable_list_item_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_list_item *); +void apfl_expr_assignable_list_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_list *); +void apfl_expr_assignable_var_or_member_dot_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member_dot *); +void apfl_expr_assignable_var_or_member_at_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member_at *); +void apfl_expr_assignable_var_or_member_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member *); +void apfl_expr_assignable_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable *); +void apfl_expr_assignment_deinit(struct apfl_allocator allocator, struct apfl_expr_assignment *); +void apfl_expr_dot_deinit(struct apfl_allocator allocator, struct apfl_expr_dot *); +void apfl_expr_at_deinit(struct apfl_allocator allocator, struct apfl_expr_at *); // End deinit functions @@ -463,7 +500,7 @@ typedef struct apfl_tokenizer *apfl_tokenizer_ptr; typedef bool (*apfl_source_reader_cb)(void *context, char *buf, size_t *len, bool need); -apfl_tokenizer_ptr apfl_tokenizer_new(apfl_source_reader_cb, void *context); +apfl_tokenizer_ptr apfl_tokenizer_new(struct apfl_allocator allocator, apfl_source_reader_cb, void *context); void apfl_tokenizer_destroy(apfl_tokenizer_ptr); enum apfl_parse_result apfl_tokenizer_next(apfl_tokenizer_ptr, bool need); @@ -486,7 +523,7 @@ struct apfl_error apfl_tokenizer_get_error(apfl_tokenizer_ptr); */ bool apfl_string_source_reader(void *, char *, size_t *len, bool); -void *apfl_string_source_reader_new(struct apfl_string_view); +void *apfl_string_source_reader_new(struct apfl_allocator allocator, struct apfl_string_view); void apfl_string_source_reader_destroy(void *); @@ -503,7 +540,7 @@ struct apfl_parser; typedef struct apfl_parser *apfl_parser_ptr; -apfl_parser_ptr apfl_parser_new(struct apfl_parser_token_source); +apfl_parser_ptr apfl_parser_new(struct apfl_allocator allocator, struct apfl_parser_token_source); /* Destroys the parser. * Note that if the token source needs it's own destruction, you'll have to do @@ -538,7 +575,7 @@ enum apfl_result { APFL_RESULT_ERR_FATAL, }; -apfl_ctx apfl_ctx_new(void); +apfl_ctx apfl_ctx_new(struct apfl_allocator); void apfl_ctx_destroy(apfl_ctx); diff --git a/src/common.h b/src/common.h deleted file mode 100644 index 6fae71e..0000000 --- a/src/common.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef APFL_COMMON_H -#define APFL_COMMON_H - -#ifdef __cplusplus -extern "C" { -#endif - -// APFL_DESTROY destroys a dynamically allocated value. -// It will first deinit the value using deiniter, -// free the memory and then set the variable to NULL. -// It is always allowed to destroy an already destroyed -// or deinited value. -#define APFL_DESTROY(var, deiniter) \ - do { \ - if ((var) == NULL) { \ - break; \ - } \ - deiniter(var); \ - free(var); \ - (var) = NULL; \ - } while(0) - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/eval.c b/src/eval.c index 4743a10..baa8c22 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1,6 +1,8 @@ #include #include "apfl.h" + +#include "alloc.h" #include "hashmap.h" #include "internal.h" #include "resizable.h" @@ -15,6 +17,8 @@ } while (0) struct apfl_ctx_data { + struct apfl_allocator allocator; + struct apfl_hashmap scope; struct apfl_value *stack; @@ -49,6 +53,7 @@ struct match_pattern_value { struct apfl_value value; struct apfl_value *member_keys; size_t member_keys_len; + size_t member_keys_cap; }; struct match_pattern_list { @@ -76,15 +81,16 @@ struct match_pattern { }; struct match_pattern_constraint *constraints; size_t constraints_len; + size_t constraints_cap; }; static enum apfl_result evaluate(apfl_ctx, struct apfl_expr *); -static variable variable_new(void); +static variable variable_new(apfl_ctx); static variable variable_incref(variable var); -static void variable_unref(variable var); +static void variable_unref(apfl_ctx, variable var); static enum match_result match_pattern_from_assignable(apfl_ctx ctx, struct apfl_expr_assignable *, struct match_pattern *); -static void match_pattern_deinit(struct match_pattern *); -static enum match_result match_pattern_match(struct match_pattern *, struct apfl_value); +static void match_pattern_deinit(apfl_ctx, struct match_pattern *); +static enum match_result match_pattern_match(struct apfl_allocator, struct match_pattern *, struct apfl_value value); static bool scope_keys_eq(void *opaque, const void *_a, const void *_b) @@ -108,18 +114,18 @@ scope_calc_hash(void *opaque, const void *_key) static void scope_destroy_key(void *opaque, void *_key) { - (void)opaque; + apfl_ctx ctx = opaque; apfl_refcounted_string *key = _key; - apfl_refcounted_string_unref(*key); + apfl_refcounted_string_unref(ctx->allocator, *key); } static void scope_destroy_value(void *opaque, void *_value) { - (void)opaque; + apfl_ctx ctx = opaque; variable *value = _value; - variable_unref(*value); + variable_unref(ctx, *value); } static void @@ -140,31 +146,30 @@ scope_copy_value(void *opaque, void *_dest, void *_src) *dest = variable_incref(*src); } -static const struct apfl_hashmap_callbacks scope_hashmap_callbacks = { - .opaque = NULL, - .keys_eq = scope_keys_eq, - .calc_hash = scope_calc_hash, - .destroy_key = scope_destroy_key, - .destroy_value = scope_destroy_value, - .copy_key = scope_copy_key, - .copy_value = scope_copy_value, -}; - bool -scope_init(struct apfl_hashmap *map) +scope_init(apfl_ctx ctx, struct apfl_hashmap *map) { return apfl_hashmap_init( map, - scope_hashmap_callbacks, + ctx->allocator, + (struct apfl_hashmap_callbacks) { + .opaque = ctx, + .keys_eq = scope_keys_eq, + .calc_hash = scope_calc_hash, + .destroy_key = scope_destroy_key, + .destroy_value = scope_destroy_value, + .copy_key = scope_copy_key, + .copy_value = scope_copy_value, + }, sizeof(apfl_refcounted_string), sizeof(variable) ); } static variable -variable_new(void) +variable_new(apfl_ctx ctx) { - variable var = ALLOC(struct variable_data); + variable var = ALLOC_OBJ(ctx->allocator, struct variable_data); if (var == NULL) { return NULL; } @@ -185,22 +190,22 @@ variable_incref(variable var) } static void -variable_unref(variable var) +variable_unref(apfl_ctx ctx, variable var) { if (var != NULL && apfl_refcount_dec(&var->refcount)) { - apfl_value_deinit(&var->value); - free(var); + apfl_value_deinit(ctx->allocator, &var->value); + FREE_OBJ(ctx->allocator, var); } } static void -variable_set(variable var, struct apfl_value value) +variable_set(apfl_ctx ctx, variable var, struct apfl_value value) { if (var == NULL) { return; } - apfl_value_deinit(&var->value); + apfl_value_deinit(ctx->allocator, &var->value); var->value = apfl_value_move(&value); } @@ -208,6 +213,7 @@ static bool stack_push(apfl_ctx ctx, struct apfl_value value) { bool ok = apfl_resizable_append( + ctx->allocator, sizeof(struct apfl_value), (void **)&ctx->stack, &ctx->stack_len, @@ -217,7 +223,7 @@ stack_push(apfl_ctx ctx, struct apfl_value value) ); if (!ok) { - apfl_value_deinit(&value); + apfl_value_deinit(ctx->allocator, &value); } return ok; @@ -250,6 +256,7 @@ stack_pop(apfl_ctx ctx, struct apfl_value *value, int index) *value = ctx->stack[index]; assert(apfl_resizable_splice( + ctx->allocator, sizeof(struct apfl_value), (void **)ctx->stack, &ctx->stack_len, @@ -298,7 +305,7 @@ stack_drop(apfl_ctx ctx, int index) if (!stack_pop(ctx, &value, index)) { return false; } - apfl_value_deinit(&value); + apfl_value_deinit(ctx->allocator, &value); return true; } @@ -309,22 +316,25 @@ stack_must_drop(apfl_ctx ctx, int index) } apfl_ctx -apfl_ctx_new(void) +apfl_ctx_new(struct apfl_allocator allocator) { - apfl_ctx ctx = ALLOC(struct apfl_ctx_data); + apfl_ctx ctx = ALLOC_OBJ(allocator, struct apfl_ctx_data); + if (ctx == NULL) { return NULL; } - if (!scope_init(&ctx->scope)) { - free(ctx); - return NULL; - } - + ctx->allocator = allocator; ctx->stack = NULL; ctx->stack_len = 0; ctx->stack_cap = 0; + if (!scope_init(ctx, &ctx->scope)) { + FREE_OBJ(allocator, ctx); + return NULL; + } + + return ctx; } @@ -339,9 +349,8 @@ apfl_ctx_destroy(apfl_ctx ctx) while (ctx->stack_len > 0) { stack_must_drop(ctx, -1); } - free(ctx->stack); - - free(ctx); + FREE_OBJ(ctx->allocator, ctx->stack); + FREE_OBJ(ctx->allocator, ctx); } static variable @@ -352,12 +361,12 @@ ctx_get_var_for_assignment_inner(apfl_ctx ctx, /*borrowed*/ apfl_refcounted_stri return var; } - if ((var = variable_new()) == NULL) { + if ((var = variable_new(ctx)) == NULL) { return NULL; } if (!apfl_hashmap_set(&ctx->scope, &name, &var)) { - variable_unref(var); + variable_unref(ctx, var); return NULL; } @@ -368,7 +377,7 @@ static variable ctx_get_var_for_assignment(apfl_ctx ctx, apfl_refcounted_string name) { variable var = ctx_get_var_for_assignment_inner(ctx, name); - apfl_refcounted_string_unref(name); + apfl_refcounted_string_unref(ctx->allocator, name); return var; } @@ -377,7 +386,7 @@ ctx_get_var(apfl_ctx ctx, apfl_refcounted_string name) { variable var; bool ok = apfl_hashmap_get(&ctx->scope, &name, &var); - apfl_refcounted_string_unref(name); + apfl_refcounted_string_unref(ctx->allocator, name); return ok ? var : NULL; } @@ -410,8 +419,8 @@ constant_to_value(struct apfl_expr_const *constant) static bool match_pattern_add_constraint( + struct apfl_allocator allocator, struct match_pattern *pattern, - size_t *constraints_cap, enum match_pattern_constraint_type type, struct apfl_value value ) { @@ -421,14 +430,15 @@ match_pattern_add_constraint( }; if (!apfl_resizable_append( + allocator, sizeof(struct match_pattern_constraint), (void **)&pattern->constraints, &pattern->constraints_len, - constraints_cap, + &pattern->constraints_cap, &constraint, 1 )) { - apfl_value_deinit(&value); + apfl_value_deinit(allocator, &value); return false; } return true; @@ -454,6 +464,7 @@ match_pattern_from_assignable_list( } if ((pattern_list->subpatterns = ALLOC_LIST( + ctx->allocator, struct match_pattern, assignable_list->len )) == NULL) { @@ -479,10 +490,13 @@ match_pattern_from_assignable_list( ); if (result != MATCH_OK) { - DEINIT_LIST( + DEINIT_CAP_LIST_WITH_ARGS( + ctx->allocator, pattern_list->subpatterns, pattern_list->subpatterns_len, - match_pattern_deinit + assignable_list->len, + match_pattern_deinit, + ctx ); return result; } @@ -506,8 +520,8 @@ match_pattern_from_var_or_member( .value = {.type = APFL_VALUE_NIL}, .member_keys = NULL, .member_keys_len = 0, + .member_keys_cap = 0, }; - size_t member_keys_cap = 0; enum match_result result; enum apfl_result eval_result; @@ -518,6 +532,7 @@ next: switch (var_or_member->type) { case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR: if ((pattern->value.varname = apfl_string_copy_into_new_refcounted( + ctx->allocator, apfl_string_view_from(var_or_member->var) )) == NULL) { result = MATCH_FATAL_ERROR; @@ -527,6 +542,7 @@ next: case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT: str_value.type = APFL_VALUE_STRING; if ((str_value.string = apfl_string_copy_into_new_refcounted( + ctx->allocator, apfl_string_view_from(var_or_member->dot.rhs) )) == NULL) { result = MATCH_FATAL_ERROR; @@ -534,14 +550,15 @@ next: } if (!apfl_resizable_append( + ctx->allocator, sizeof(struct apfl_value), (void **)&pattern->value.member_keys, &pattern->value.member_keys_len, - &member_keys_cap, + &pattern->value.member_keys_cap, &str_value, 1 )) { - apfl_value_deinit(&str_value); + apfl_value_deinit(ctx->allocator, &str_value); result = MATCH_FATAL_ERROR; goto fail; } @@ -564,14 +581,15 @@ next: rhs_value = stack_must_pop(ctx, -1); if (!apfl_resizable_append( + ctx->allocator, sizeof(struct apfl_value), (void **)&pattern->value.member_keys, &pattern->value.member_keys_len, - &member_keys_cap, + &pattern->value.member_keys_cap, &rhs_value, 1 )) { - apfl_value_deinit(&rhs_value); + apfl_value_deinit(ctx->allocator, &rhs_value); result = MATCH_FATAL_ERROR; goto fail; } @@ -581,7 +599,12 @@ next: } fail: - DEINIT_LIST(pattern->value.member_keys, pattern->value.member_keys_len, apfl_value_deinit); + DEINIT_LIST( + ctx->allocator, + pattern->value.member_keys, + pattern->value.member_keys_cap, + apfl_value_deinit + ); return result; } @@ -597,8 +620,7 @@ match_pattern_from_assignable_inner( pattern->type = MPATTERN_BLANK; pattern->constraints = NULL; pattern->constraints_len = 0; - - size_t constraints_cap = 0; + pattern->constraints_cap = 0; next: switch (assignable->type) { @@ -606,8 +628,8 @@ next: return match_pattern_from_var_or_member(ctx, &assignable->var_or_member, pattern); case APFL_EXPR_ASSIGNABLE_CONSTANT: if (!match_pattern_add_constraint( + ctx->allocator, pattern, - &constraints_cap, MPATTERN_CONSTRAINT_EQUALS, constant_to_value(&assignable->constant) )) { @@ -630,8 +652,8 @@ next: } if (!match_pattern_add_constraint( + ctx->allocator, pattern, - &constraints_cap, MPATTERN_CONSTRAINT_PREDICATE, apfl_value_move(&value) )) { @@ -659,7 +681,7 @@ match_pattern_from_assignable( ) { enum match_result result = match_pattern_from_assignable_inner(ctx, assignable, pattern); if (result != MATCH_OK) { - match_pattern_deinit(pattern); + match_pattern_deinit(ctx, pattern); } return result; } @@ -697,17 +719,17 @@ match_pattern_create_vars(apfl_ctx ctx, struct match_pattern *pattern) } static void -match_pattern_constraint_deinit(struct match_pattern_constraint *constraint) +match_pattern_constraint_deinit(struct apfl_allocator allocator, struct match_pattern_constraint *constraint) { if (constraint == NULL) { return; } - apfl_value_deinit(&constraint->value); + apfl_value_deinit(allocator, &constraint->value); } static void -match_pattern_deinit(struct match_pattern *pattern) +match_pattern_deinit(apfl_ctx ctx, struct match_pattern *pattern) { if (pattern == NULL) { return; @@ -717,27 +739,31 @@ match_pattern_deinit(struct match_pattern *pattern) case MPATTERN_BLANK: break; case MPATTERN_VALUE: - apfl_refcounted_string_unref(pattern->value.varname); - variable_unref(pattern->value.var); - apfl_value_deinit(&pattern->value.value); + apfl_refcounted_string_unref(ctx->allocator, pattern->value.varname); + variable_unref(ctx, pattern->value.var); + apfl_value_deinit(ctx->allocator, &pattern->value.value); DEINIT_LIST( + ctx->allocator, pattern->value.member_keys, - pattern->value.member_keys_len, + pattern->value.member_keys_cap, apfl_value_deinit ); break; case MPATTERN_LIST: - DEINIT_LIST( + DEINIT_LIST_WITH_ARGS( + ctx->allocator, pattern->list.subpatterns, pattern->list.subpatterns_len, - match_pattern_deinit + match_pattern_deinit, + ctx ); break; } DEINIT_LIST( + ctx->allocator, pattern->constraints, - pattern->constraints_len, + pattern->constraints_cap, match_pattern_constraint_deinit ); } @@ -763,6 +789,7 @@ match_pattern_check_constraint( static enum match_result match_pattern_match_list_inner( + struct apfl_allocator allocator, struct match_pattern_list *pattern_list, /*borrowed*/ apfl_list list ) { @@ -783,12 +810,14 @@ match_pattern_match_list_inner( struct apfl_value list_item; // Will not fail, as i is a valid index for the list assert(apfl_list_get_item( + allocator, apfl_list_incref(list), i, &list_item )); enum match_result result = match_pattern_match( + allocator, &pattern_list->subpatterns[i], apfl_value_move(&list_item) ); @@ -810,12 +839,14 @@ match_pattern_match_list_inner( struct apfl_value list_item; // Will not fail, as list_index is a valid index for the list assert(apfl_list_get_item( + allocator, apfl_list_incref(list), list_index, &list_item )); enum match_result result = match_pattern_match( + allocator, &pattern_list->subpatterns[subpattern_index], apfl_value_move(&list_item) ); @@ -824,7 +855,7 @@ match_pattern_match_list_inner( } } - apfl_editable_list mid_builder = apfl_editable_list_new(); + apfl_editable_list mid_builder = apfl_editable_list_new(allocator); if (mid_builder == NULL) { return MATCH_ERROR; } @@ -833,6 +864,7 @@ match_pattern_match_list_inner( struct apfl_value list_item; // Will not fail, as i is a valid index for the list assert(apfl_list_get_item( + allocator, apfl_list_incref(list), i, &list_item @@ -852,6 +884,7 @@ match_pattern_match_list_inner( } return match_pattern_match( + allocator, &pattern_list->subpatterns[pattern_list->expand_index], (struct apfl_value) { .type = APFL_VALUE_LIST, @@ -862,23 +895,24 @@ match_pattern_match_list_inner( static enum match_result match_pattern_match_list( + struct apfl_allocator allocator, struct match_pattern_list *pattern_list, struct apfl_value value ) { if (value.type != APFL_VALUE_LIST) { - apfl_value_deinit(&value); + apfl_value_deinit(allocator, &value); return MATCH_DOESNT_MATCH; } apfl_list list = apfl_list_incref(value.list); - apfl_value_deinit(&value); + apfl_value_deinit(allocator, &value); - enum match_result result = match_pattern_match_list_inner(pattern_list, list); - apfl_list_unref(list); + enum match_result result = match_pattern_match_list_inner(allocator, pattern_list, list); + apfl_list_unref(allocator, list); return result; } static enum match_result -match_pattern_match(struct match_pattern *pattern, struct apfl_value value) +match_pattern_match(struct apfl_allocator allocator, struct match_pattern *pattern, struct apfl_value value) { // We put the contraints into the list from the outside in, so we need // to iterate in reverse order. @@ -888,20 +922,20 @@ match_pattern_match(struct match_pattern *pattern, struct apfl_value value) &value ); if (result != MATCH_OK) { - apfl_value_deinit(&value); + apfl_value_deinit(allocator, &value); return result; } } switch (pattern->type) { case MPATTERN_BLANK: - apfl_value_deinit(&value); + apfl_value_deinit(allocator, &value); return MATCH_OK; case MPATTERN_VALUE: pattern->value.value = apfl_value_move(&value); return MATCH_OK; case MPATTERN_LIST: - return match_pattern_match_list(&pattern->list, apfl_value_move(&value)); + return match_pattern_match_list(allocator, &pattern->list, apfl_value_move(&value)); } assert(false); @@ -910,6 +944,7 @@ match_pattern_match(struct match_pattern *pattern, struct apfl_value value) static enum match_result match_pattern_apply_value_keys( + struct apfl_allocator allocator, struct match_pattern_value *pattern_value, apfl_editable_dict ed, size_t i @@ -936,14 +971,14 @@ match_pattern_apply_value_keys( &value )) { if (value.type != APFL_VALUE_DICT) { - apfl_value_deinit(&value); + apfl_value_deinit(allocator, &value); return MATCH_ERROR; } - next_ed = apfl_editable_dict_new_from_dict(value.dict); + next_ed = apfl_editable_dict_new_from_dict(allocator, value.dict); } else { // Be nice and create the missing dictionary - next_ed = apfl_editable_dict_new(); + next_ed = apfl_editable_dict_new(allocator); } if (next_ed == NULL) { @@ -951,6 +986,7 @@ match_pattern_apply_value_keys( } enum match_result result = match_pattern_apply_value_keys( + allocator, pattern_value, next_ed, i - 1 @@ -980,14 +1016,14 @@ match_pattern_apply_value_keys( } static enum match_result -match_pattern_apply_value(struct match_pattern_value *pattern_value) +match_pattern_apply_value(apfl_ctx ctx, struct match_pattern_value *pattern_value) { if (pattern_value->var == NULL) { return false; } if (pattern_value->member_keys_len == 0) { - variable_set(pattern_value->var, apfl_value_move(&pattern_value->value)); + variable_set(ctx, pattern_value->var, apfl_value_move(&pattern_value->value)); return MATCH_OK; } @@ -996,6 +1032,7 @@ match_pattern_apply_value(struct match_pattern_value *pattern_value) } apfl_editable_dict ed = apfl_editable_dict_new_from_dict( + ctx->allocator, apfl_dict_incref(pattern_value->var->value.dict) ); if (ed == NULL) { @@ -1003,6 +1040,7 @@ match_pattern_apply_value(struct match_pattern_value *pattern_value) } enum match_result result = match_pattern_apply_value_keys( + ctx->allocator, pattern_value, ed, pattern_value->member_keys_len - 1 @@ -1017,7 +1055,7 @@ match_pattern_apply_value(struct match_pattern_value *pattern_value) return MATCH_FATAL_ERROR; } - variable_set(pattern_value->var, (struct apfl_value) { + variable_set(ctx, pattern_value->var, (struct apfl_value) { .type = APFL_VALUE_DICT, .dict = d, }); @@ -1026,16 +1064,17 @@ match_pattern_apply_value(struct match_pattern_value *pattern_value) } static enum match_result -match_pattern_apply(struct match_pattern *pattern) +match_pattern_apply(apfl_ctx ctx, struct match_pattern *pattern) { switch (pattern->type) { case MPATTERN_BLANK: return MATCH_OK; case MPATTERN_VALUE: - return match_pattern_apply_value(&pattern->value); + return match_pattern_apply_value(ctx, &pattern->value); case MPATTERN_LIST: for (size_t i = 0; i < pattern->list.subpatterns_len; i++) { enum match_result result = match_pattern_apply( + ctx, &pattern->list.subpatterns[i] ); if (result != MATCH_OK) { @@ -1071,12 +1110,12 @@ evaluate_list_expr_to_list(apfl_ctx ctx, struct apfl_expr *expr, apfl_editable_l } if (value.type != APFL_VALUE_LIST) { - apfl_value_deinit(&value); + apfl_value_deinit(ctx->allocator, &value); return APFL_RESULT_ERR; } apfl_list list = apfl_list_incref(value.list); - apfl_value_deinit(&value); + apfl_value_deinit(ctx->allocator, &value); if (!apfl_editable_list_append_list(elist, list)) { return APFL_RESULT_ERR_FATAL; @@ -1107,7 +1146,7 @@ evaluate_expr_list_into_list(apfl_ctx ctx, struct apfl_expr_list *list, apfl_edi static enum apfl_result evaluate_list(apfl_ctx ctx, struct apfl_expr_list *list) { - apfl_editable_list elist = apfl_editable_list_new(); + apfl_editable_list elist = apfl_editable_list_new(ctx->allocator); if (elist == NULL) { return APFL_RESULT_ERR_FATAL; } @@ -1127,7 +1166,7 @@ evaluate_list(apfl_ctx ctx, struct apfl_expr_list *list) .type = APFL_VALUE_LIST, .list = out, })) { - apfl_list_unref(out); + apfl_list_unref(ctx->allocator, out); return APFL_RESULT_ERR_FATAL; } @@ -1162,7 +1201,7 @@ evaluate_dict_into_editable( static enum apfl_result evaluate_dict(apfl_ctx ctx, struct apfl_expr_dict *dict) { - apfl_editable_dict ed = apfl_editable_dict_new(); + apfl_editable_dict ed = apfl_editable_dict_new(ctx->allocator); if (ed == NULL) { return APFL_RESULT_ERR_FATAL; @@ -1182,7 +1221,7 @@ evaluate_dict(apfl_ctx ctx, struct apfl_expr_dict *dict) .type = APFL_VALUE_DICT, .dict = out, })) { - apfl_dict_unref(out); + apfl_dict_unref(ctx->allocator, out); return APFL_RESULT_ERR_FATAL; } @@ -1201,7 +1240,7 @@ evaluate_dot(apfl_ctx ctx, struct apfl_expr_dot *dot) }; struct apfl_value out; - if (apfl_value_get_item(apfl_value_move(&lhs), apfl_value_move(&key), &out) != APFL_VALUE_GET_ITEM_OK) { + if (apfl_value_get_item(ctx->allocator, apfl_value_move(&lhs), apfl_value_move(&key), &out) != APFL_VALUE_GET_ITEM_OK) { return APFL_RESULT_ERR; // TODO: Describe error } @@ -1221,7 +1260,7 @@ evaluate_at(apfl_ctx ctx, struct apfl_expr_at *at) struct apfl_value lhs = stack_must_pop(ctx, -1); struct apfl_value out; - if (apfl_value_get_item(apfl_value_move(&lhs), apfl_value_move(&rhs), &out) != APFL_VALUE_GET_ITEM_OK) { + if (apfl_value_get_item(ctx->allocator, apfl_value_move(&lhs), apfl_value_move(&rhs), &out) != APFL_VALUE_GET_ITEM_OK) { return APFL_RESULT_ERR; // TODO: Describe error } @@ -1256,7 +1295,7 @@ evaluate_assignment_setup( } if (!match_pattern_create_vars(ctx, pattern)) { - match_pattern_deinit(pattern); + match_pattern_deinit(ctx, pattern); return MATCH_FATAL_ERROR; } @@ -1265,10 +1304,12 @@ evaluate_assignment_setup( static enum match_result evaluate_assignment_finish( + apfl_ctx ctx, struct match_pattern *pattern, struct apfl_value value ) { enum match_result match_result = match_pattern_match( + ctx->allocator, pattern, apfl_value_move(&value) ); @@ -1277,7 +1318,7 @@ evaluate_assignment_finish( return match_result; } - return match_pattern_apply(pattern); + return match_pattern_apply(ctx, pattern); } @@ -1297,17 +1338,18 @@ evaluate_assignment(apfl_ctx ctx, struct apfl_expr_assignment *assignment) enum apfl_result result = evaluate(ctx, assignment->rhs); if (result != APFL_RESULT_OK) { - match_pattern_deinit(&pattern); + match_pattern_deinit(ctx, &pattern); return result; } match_result = evaluate_assignment_finish( + ctx, &pattern, stack_must_get(ctx, -1) // stack_must_get instead of stack_must_pop, // so the value is stull on the stack on return. ); - match_pattern_deinit(&pattern); + match_pattern_deinit(ctx, &pattern); if (match_result != MATCH_OK) { stack_must_drop(ctx, -1); return failing_match_result_to_apfl_result(match_result); @@ -1325,7 +1367,7 @@ evaluate_var(apfl_ctx ctx, apfl_refcounted_string varname) } bool ok = stack_push(ctx, apfl_value_incref(var->value)); - variable_unref(var); + variable_unref(ctx, var); return ok ? APFL_RESULT_OK : APFL_RESULT_ERR_FATAL; @@ -1368,7 +1410,12 @@ enum apfl_result apfl_eval(apfl_ctx ctx, struct apfl_expr expr) { enum apfl_result result = evaluate(ctx, &expr); - apfl_expr_deinit(&expr); + + // TODO: expr might have been allocated with another allocator. The apfl_ctx + // should probably also handle parsing and no longer accept + // expressions directly. + apfl_expr_deinit(ctx->allocator, &expr); + return result; } @@ -1382,5 +1429,5 @@ apfl_debug_print_val(apfl_ctx ctx, int index, FILE *f) } apfl_value_print(value, f); - apfl_value_deinit(&value); + apfl_value_deinit(ctx->allocator, &value); } diff --git a/src/expr.c b/src/expr.c index a2dc78f..3844a25 100644 --- a/src/expr.c +++ b/src/expr.c @@ -4,43 +4,43 @@ #include "apfl.h" -#include "common.h" +#include "alloc.h" #include "internal.h" void -apfl_expr_deinit(struct apfl_expr *expr) +apfl_expr_deinit(struct apfl_allocator allocator, struct apfl_expr *expr) { switch (expr->type) { case APFL_EXPR_LIST: - apfl_expr_list_deinit(&expr->list); + apfl_expr_list_deinit(allocator, &expr->list); break; case APFL_EXPR_DICT: - apfl_expr_dict_deinit(&expr->dict); + apfl_expr_dict_deinit(allocator, &expr->dict); break; case APFL_EXPR_CALL: - apfl_expr_call_deinit(&expr->call); + apfl_expr_call_deinit(allocator, &expr->call); break; case APFL_EXPR_SIMPLE_FUNC: - apfl_expr_body_unref(expr->simple_func); + apfl_expr_body_unref(allocator, expr->simple_func); expr->simple_func = NULL; break; case APFL_EXPR_COMPLEX_FUNC: - apfl_expr_complex_func_deinit(&expr->complex_func); + apfl_expr_complex_func_deinit(allocator, &expr->complex_func); break; case APFL_EXPR_ASSIGNMENT: - apfl_expr_assignment_deinit(&expr->assignment); + apfl_expr_assignment_deinit(allocator, &expr->assignment); break; case APFL_EXPR_DOT: - apfl_expr_dot_deinit(&expr->dot); + apfl_expr_dot_deinit(allocator, &expr->dot); break; case APFL_EXPR_AT: - apfl_expr_at_deinit(&expr->at); + apfl_expr_at_deinit(allocator, &expr->at); break; case APFL_EXPR_CONSTANT: - apfl_expr_const_deinit(&expr->constant); + apfl_expr_const_deinit(allocator, &expr->constant); break; case APFL_EXPR_VAR: - apfl_refcounted_string_unref(expr->var); + apfl_refcounted_string_unref(allocator, expr->var); expr->var = NULL; break; case APFL_EXPR_BLANK: @@ -50,48 +50,48 @@ apfl_expr_deinit(struct apfl_expr *expr) } void -apfl_expr_list_deinit(struct apfl_expr_list *list) +apfl_expr_list_deinit(struct apfl_allocator allocator, struct apfl_expr_list *list) { - DEINIT_LIST(list->items, list->len, apfl_expr_list_item_deinit); + DEINIT_LIST(allocator, list->items, list->len, apfl_expr_list_item_deinit); } void -apfl_expr_list_item_deinit(struct apfl_expr_list_item *item) +apfl_expr_list_item_deinit(struct apfl_allocator allocator, struct apfl_expr_list_item *item) { - DESTROY(item->expr, apfl_expr_deinit); + DESTROY(allocator, item->expr, apfl_expr_deinit); } void -apfl_expr_dict_pair_deinit(struct apfl_expr_dict_pair *pair) +apfl_expr_dict_pair_deinit(struct apfl_allocator allocator, struct apfl_expr_dict_pair *pair) { - DESTROY(pair->k, apfl_expr_deinit); - DESTROY(pair->v, apfl_expr_deinit); + DESTROY(allocator, pair->k, apfl_expr_deinit); + DESTROY(allocator, pair->v, apfl_expr_deinit); } void -apfl_expr_dict_deinit(struct apfl_expr_dict *dict) +apfl_expr_dict_deinit(struct apfl_allocator allocator, struct apfl_expr_dict *dict) { - DEINIT_LIST(dict->items, dict->len, apfl_expr_dict_pair_deinit); + DEINIT_CAP_LIST(allocator, dict->items, dict->len, dict->cap, apfl_expr_dict_pair_deinit); } void -apfl_expr_call_deinit(struct apfl_expr_call *call) +apfl_expr_call_deinit(struct apfl_allocator allocator, struct apfl_expr_call *call) { - DESTROY(call->callee, apfl_expr_deinit); - apfl_expr_list_deinit(&call->arguments); + DESTROY(allocator, call->callee, apfl_expr_deinit); + apfl_expr_list_deinit(allocator, &call->arguments); } void -apfl_expr_body_unref(struct apfl_expr_body *body) +apfl_expr_body_unref(struct apfl_allocator allocator, struct apfl_expr_body *body) { if (body != NULL && apfl_refcount_dec(&body->refcount)) { - DEINIT_LIST(body->items, body->len, apfl_expr_deinit); - free(body); + DEINIT_CAP_LIST(allocator, body->items, body->len, body->cap, apfl_expr_deinit); + FREE_OBJ(allocator, body); } } void -apfl_expr_const_deinit(struct apfl_expr_const *constant) +apfl_expr_const_deinit(struct apfl_allocator allocator, struct apfl_expr_const *constant) { switch (constant->type) { case APFL_EXPR_CONST_NIL: @@ -100,52 +100,52 @@ apfl_expr_const_deinit(struct apfl_expr_const *constant) // nop break; case APFL_EXPR_CONST_STRING: - apfl_refcounted_string_unref(constant->string); + apfl_refcounted_string_unref(allocator, constant->string); constant->string = NULL; break; } } -#define DEINIT_GENERIC_LHS_RHS_EXPR(x, lhs_deiniter) \ - do { \ - DESTROY(x->lhs, lhs_deiniter); \ - DESTROY(x->rhs, apfl_expr_deinit); \ +#define DEINIT_GENERIC_LHS_RHS_EXPR(allocator, x, lhs_deiniter) \ + do { \ + DESTROY(allocator, x->lhs, lhs_deiniter); \ + DESTROY(allocator, x->rhs, apfl_expr_deinit); \ } while (0) void -apfl_expr_param_predicate_deinit(struct apfl_expr_param_predicate *pred) +apfl_expr_param_predicate_deinit(struct apfl_allocator allocator, struct apfl_expr_param_predicate *pred) { - DEINIT_GENERIC_LHS_RHS_EXPR(pred, apfl_expr_param_deinit); + DEINIT_GENERIC_LHS_RHS_EXPR(allocator, pred, apfl_expr_param_deinit); } void -apfl_expr_params_deinit(struct apfl_expr_params *params) +apfl_expr_params_deinit(struct apfl_allocator allocator, struct apfl_expr_params *params) { - DEINIT_LIST(params->params, params->len, apfl_expr_params_item_deinit); + DEINIT_CAP_LIST(allocator, params->params, params->len, params->cap, apfl_expr_params_item_deinit); } void -apfl_expr_params_item_deinit(struct apfl_expr_params_item *item) +apfl_expr_params_item_deinit(struct apfl_allocator allocator, struct apfl_expr_params_item *item) { - apfl_expr_param_deinit(&item->param); + apfl_expr_param_deinit(allocator, &item->param); } void -apfl_expr_param_deinit(struct apfl_expr_param *param) +apfl_expr_param_deinit(struct apfl_allocator allocator, struct apfl_expr_param *param) { switch (param->type) { case APFL_EXPR_PARAM_VAR: - apfl_refcounted_string_unref(param->var); + apfl_refcounted_string_unref(allocator, param->var); param->var = NULL; break; case APFL_EXPR_PARAM_CONSTANT: - apfl_expr_const_deinit(¶m->constant); + apfl_expr_const_deinit(allocator, ¶m->constant); break; case APFL_EXPR_PARAM_PREDICATE: - apfl_expr_param_predicate_deinit(¶m->predicate); + apfl_expr_param_predicate_deinit(allocator, ¶m->predicate); break; case APFL_EXPR_PARAM_LIST: - apfl_expr_params_deinit(¶m->list); + apfl_expr_params_deinit(allocator, ¶m->list); break; case APFL_EXPR_PARAM_BLANK: // nop @@ -154,85 +154,84 @@ apfl_expr_param_deinit(struct apfl_expr_param *param) } void -apfl_expr_subfunc_deinit(struct apfl_expr_subfunc *subfunc) +apfl_expr_subfunc_deinit(struct apfl_allocator allocator, struct apfl_expr_subfunc *subfunc) { - apfl_expr_params_deinit(&subfunc->params); - apfl_expr_body_unref(subfunc->body); + apfl_expr_params_deinit(allocator, &subfunc->params); + apfl_expr_body_unref(allocator, subfunc->body); subfunc->body = NULL; } void -apfl_expr_complex_func_deinit(struct apfl_expr_complex_func *cf) +apfl_expr_complex_func_deinit(struct apfl_allocator allocator, struct apfl_expr_complex_func *cf) { - DEINIT_LIST(cf->subfuncs, cf->len, apfl_expr_subfunc_deinit); + DEINIT_CAP_LIST(allocator, cf->subfuncs, cf->len, cf->cap, apfl_expr_subfunc_deinit); } void -apfl_expr_assignable_predicate_deinit(struct apfl_expr_assignable_predicate *pred) +apfl_expr_assignable_predicate_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_predicate *pred) { - DEINIT_GENERIC_LHS_RHS_EXPR(pred, apfl_expr_assignable_deinit); + DEINIT_GENERIC_LHS_RHS_EXPR(allocator, pred, apfl_expr_assignable_deinit); } void -apfl_expr_assignable_list_item_deinit(struct apfl_expr_assignable_list_item *item) +apfl_expr_assignable_list_item_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_list_item *item) { - apfl_expr_assignable_deinit(&item->assignable); + apfl_expr_assignable_deinit(allocator, &item->assignable); } void -apfl_expr_assignable_list_deinit(struct apfl_expr_assignable_list *list) +apfl_expr_assignable_list_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_list *list) { - DEINIT_LIST(list->items, list->len, apfl_expr_assignable_list_item_deinit); + DEINIT_LIST(allocator, list->items, list->len, apfl_expr_assignable_list_item_deinit); } void -apfl_expr_assignable_var_or_member_dot_deinit(struct apfl_expr_assignable_var_or_member_dot *dot) +apfl_expr_assignable_var_or_member_dot_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member_dot *dot) { - DESTROY(dot->lhs, apfl_expr_assignable_var_or_member_deinit); - apfl_refcounted_string_unref(dot->rhs); + DESTROY(allocator, dot->lhs, apfl_expr_assignable_var_or_member_deinit); + apfl_refcounted_string_unref(allocator, dot->rhs); dot->rhs = NULL; } void -apfl_expr_assignable_var_or_member_at_deinit(struct apfl_expr_assignable_var_or_member_at *at) +apfl_expr_assignable_var_or_member_at_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member_at *at) { - DESTROY(at->lhs, apfl_expr_assignable_var_or_member_deinit); - DESTROY(at->rhs, apfl_expr_deinit); + DESTROY(allocator, at->lhs, apfl_expr_assignable_var_or_member_deinit); + DESTROY(allocator, at->rhs, apfl_expr_deinit); } - void -apfl_expr_assignable_var_or_member_deinit(struct apfl_expr_assignable_var_or_member *var_or_member) +apfl_expr_assignable_var_or_member_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member *var_or_member) { switch (var_or_member->type) { case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR: - apfl_refcounted_string_unref(var_or_member->var); + apfl_refcounted_string_unref(allocator, var_or_member->var); var_or_member->var = NULL; break; case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT: - apfl_expr_assignable_var_or_member_dot_deinit(&var_or_member->dot); + apfl_expr_assignable_var_or_member_dot_deinit(allocator, &var_or_member->dot); break; case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT: - apfl_expr_assignable_var_or_member_at_deinit(&var_or_member->at); + apfl_expr_assignable_var_or_member_at_deinit(allocator, &var_or_member->at); break; } } void -apfl_expr_assignable_deinit(struct apfl_expr_assignable *a) +apfl_expr_assignable_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable *a) { switch (a->type) { case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER: - apfl_expr_assignable_var_or_member_deinit(&a->var_or_member); + apfl_expr_assignable_var_or_member_deinit(allocator, &a->var_or_member); break; case APFL_EXPR_ASSIGNABLE_CONSTANT: - apfl_expr_const_deinit(&a->constant); + apfl_expr_const_deinit(allocator, &a->constant); break; case APFL_EXPR_ASSIGNABLE_PREDICATE: - apfl_expr_assignable_predicate_deinit(&a->predicate); + apfl_expr_assignable_predicate_deinit(allocator, &a->predicate); break; case APFL_EXPR_ASSIGNABLE_LIST: - apfl_expr_assignable_list_deinit(&a->list); + apfl_expr_assignable_list_deinit(allocator, &a->list); break; case APFL_EXPR_ASSIGNABLE_BLANK: // nop @@ -241,24 +240,24 @@ apfl_expr_assignable_deinit(struct apfl_expr_assignable *a) } void -apfl_expr_assignment_deinit(struct apfl_expr_assignment *a) +apfl_expr_assignment_deinit(struct apfl_allocator allocator, struct apfl_expr_assignment *a) { - apfl_expr_assignable_deinit(&a->lhs); - DESTROY(a->rhs, apfl_expr_deinit); + apfl_expr_assignable_deinit(allocator, &a->lhs); + DESTROY(allocator, a->rhs, apfl_expr_deinit); } void -apfl_expr_dot_deinit(struct apfl_expr_dot *dot) +apfl_expr_dot_deinit(struct apfl_allocator allocator, struct apfl_expr_dot *dot) { - DESTROY(dot->lhs, apfl_expr_deinit); - apfl_refcounted_string_unref(dot->rhs); + DESTROY(allocator, dot->lhs, apfl_expr_deinit); + apfl_refcounted_string_unref(allocator, dot->rhs); dot->rhs = NULL; } void -apfl_expr_at_deinit(struct apfl_expr_at *at) +apfl_expr_at_deinit(struct apfl_allocator allocator, struct apfl_expr_at *at) { - DEINIT_GENERIC_LHS_RHS_EXPR(at, apfl_expr_deinit); + DEINIT_GENERIC_LHS_RHS_EXPR(allocator, at, apfl_expr_deinit); } // Move functions @@ -312,6 +311,15 @@ apfl_expr_move(struct apfl_expr *in) in->len = 0; \ } while (0) +#define MOVE_CAP_LIST(out, in, items, len, cap) \ + do { \ + MOVEPTR(out.items, in->items); \ + out.len = in->len; \ + out.cap = in->cap; \ + in->len = 0; \ + in->cap = 0; \ + } while (0) + struct apfl_expr_list apfl_expr_list_move(struct apfl_expr_list *in) { @@ -341,7 +349,7 @@ struct apfl_expr_dict apfl_expr_dict_move(struct apfl_expr_dict *in) { struct apfl_expr_dict out; - MOVE_LIST(out, in, items, len); + MOVE_CAP_LIST(out, in, items, len, cap); return out; } @@ -400,7 +408,7 @@ struct apfl_expr_params apfl_expr_params_move(struct apfl_expr_params *in) { struct apfl_expr_params out; - MOVE_LIST(out, in, params, len); + MOVE_CAP_LIST(out, in, params, len, cap); return out; } @@ -441,7 +449,7 @@ struct apfl_expr_complex_func apfl_expr_complex_func_move(struct apfl_expr_complex_func *in) { struct apfl_expr_complex_func out; - MOVE_LIST(out, in, subfuncs, len); + MOVE_CAP_LIST(out, in, subfuncs, len, cap); return out; } diff --git a/src/hashmap.c b/src/hashmap.c index 2b1f0d7..9bf1847 100644 --- a/src/hashmap.c +++ b/src/hashmap.c @@ -2,14 +2,17 @@ #include #include +#include "alloc.h" #include "hashmap.h" -#include "resizable.h" struct apfl_hashmap_bucket { void *keys; void *values; size_t len; - size_t cap; + + // These will be the same, unless a memory reallocation failed + size_t keys_cap; + size_t values_cap; }; #define FNV_PRIME 1099511628211U @@ -32,6 +35,7 @@ apfl_hash_fnv1a(const void *data, size_t len) } #define HAS_CALLBACK(map, cb) ((map).callbacks.cb != NULL) +#define INVOKE_CALLBACK_NOARGS(map, cb) (map).callbacks.cb((map).callbacks.opaque) #define INVOKE_CALLBACK(map, cb, ...) (map).callbacks.cb((map).callbacks.opaque, __VA_ARGS__) static bool @@ -90,6 +94,14 @@ copy_value(const struct apfl_hashmap map, void *dest, void *src) } } +static void +on_deinit(const struct apfl_hashmap map) +{ + if (HAS_CALLBACK(map, on_deinit)) { + INVOKE_CALLBACK_NOARGS(map, on_deinit); + } +} + #define CAP_GROW 5 static_assert(CAP_GROW >= 1, "CAP_GROW must be at least 1"); @@ -131,24 +143,40 @@ set_in_bucket(struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, void return true; } - if (bucket->len <= bucket->cap) { - size_t new_cap = calc_new_cap(bucket->cap); + if (bucket->keys_cap != bucket->values_cap) { + assert(false); // A set operation was called on the hashmap, + // after a previous set operation failed. + return false; + } + + if (bucket->len <= bucket->keys_cap) { + size_t new_cap = calc_new_cap(bucket->keys_cap); void *newmem; - newmem = realloc(bucket->keys, new_cap * keysize); + newmem = REALLOC_BYTES( + map.allocator, + bucket->keys, + bucket->keys_cap * keysize, + new_cap * keysize + ); if (newmem == NULL) { return false; } bucket->keys = newmem; + bucket->keys_cap = new_cap; - newmem = realloc(bucket->values, new_cap * valsize); + newmem = REALLOC_BYTES( + map.allocator, + bucket->values, + bucket->values_cap * valsize, + new_cap * valsize + ); if (newmem == NULL) { return false; } bucket->values = newmem; - - bucket->cap = new_cap; + bucket->values_cap = new_cap; } copy_key(map, KVADDR(bucket->keys, keysize, bucket->len), key); @@ -160,16 +188,32 @@ set_in_bucket(struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, void } static bool -get_in_bucket(const struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, const void *key, void *value) +peek_in_bucket(const struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, const void *key, void **value_ptr) { size_t i; if (!find_key_in_bucket(map, bucket, key, &i)) { return false; } - if (value != NULL) { + if (value_ptr != NULL) { size_t valsize = map.valsize; - copy_value(map, value, KVADDR(bucket->values, valsize, i)); + *value_ptr = KVADDR(bucket->values, valsize, i); + } + + return true; +} + +static bool +get_in_bucket(const struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, const void *key, void *value) +{ + void *value_ptr; + + if (!peek_in_bucket(map, bucket, key, &value_ptr)) { + return false; + } + + if (value != NULL) { + copy_value(map, value, value_ptr); } return true; @@ -217,25 +261,28 @@ delete_in_bucket(struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket, co static bool hashmap_init( struct apfl_hashmap *map, + struct apfl_allocator allocator, struct apfl_hashmap_callbacks callbacks, size_t nbuckets, size_t keysize, size_t valsize ) { map->callbacks = callbacks; + map->allocator = allocator; map->keysize = keysize; map->valsize = valsize; map->nbuckets = nbuckets; - if ((map->buckets = malloc(sizeof(struct apfl_hashmap_bucket) * nbuckets)) == NULL) { + if ((map->buckets = ALLOC_LIST(allocator, struct apfl_hashmap_bucket, nbuckets)) == NULL) { return false; } for (size_t i = 0; i < nbuckets; i++) { map->buckets[i] = (struct apfl_hashmap_bucket) { - .keys = NULL, - .values = NULL, - .len = 0, - .cap = 0, + .keys = NULL, + .values = NULL, + .len = 0, + .keys_cap = 0, + .values_cap = 0, }; } @@ -245,11 +292,12 @@ hashmap_init( bool apfl_hashmap_init( struct apfl_hashmap *map, + struct apfl_allocator allocator, struct apfl_hashmap_callbacks callbacks, size_t keysize, size_t valsize ) { - return hashmap_init(map, callbacks, INITIAL_NBUCKETS, keysize, valsize); + return hashmap_init(map, allocator, callbacks, INITIAL_NBUCKETS, keysize, valsize); } void @@ -258,6 +306,12 @@ apfl_hashmap_delete(struct apfl_hashmap *map, const void *key) delete_in_bucket(*map, bucket_by_key(*map, key), key); } +bool +apfl_hashmap_peek(const struct apfl_hashmap *map, const void *key, void **value_ptr) +{ + return get_in_bucket(*map, bucket_by_key(*map, key), key, value_ptr); +} + bool apfl_hashmap_get(const struct apfl_hashmap *map, const void *key, void *value) { @@ -277,10 +331,11 @@ destroy_bucket(const struct apfl_hashmap map, struct apfl_hashmap_bucket *bucket destroy_key(map, KVADDR(bucket->keys, map.keysize, i)); destroy_value(map, KVADDR(bucket->values, map.valsize, i)); } - free(bucket->keys); - free(bucket->values); + FREE_BYTES(map.allocator, bucket->keys, bucket->keys_cap * map.keysize); + FREE_BYTES(map.allocator, bucket->values, bucket->values_cap * map.valsize); bucket->len = 0; - bucket->cap = 0; + bucket->keys_cap = 0; + bucket->values_cap = 0; } size_t @@ -304,8 +359,11 @@ apfl_hashmap_deinit(struct apfl_hashmap *map) for (size_t i = 0; i < map->nbuckets; i++) { destroy_bucket(*map, &map->buckets[i]); } - free(map->buckets); + FREE_LIST(map->allocator, map->buckets, map->nbuckets); + map->buckets = NULL; } + + on_deinit(*map); } struct apfl_hashmap @@ -323,7 +381,7 @@ apfl_hashmap_copy(struct apfl_hashmap *dst, struct apfl_hashmap src) size_t keysize = src.keysize; size_t valsize = src.valsize; - if (!hashmap_init(dst, src.callbacks, src.nbuckets, keysize, valsize)) { + if (!hashmap_init(dst, src.allocator, src.callbacks, src.nbuckets, keysize, valsize)) { return false; } @@ -336,18 +394,19 @@ apfl_hashmap_copy(struct apfl_hashmap *dst, struct apfl_hashmap src) continue; } - dstbucket->keys = malloc(keysize * len); - dstbucket->values = malloc(valsize * len); + dstbucket->keys = ALLOC_BYTES(src.allocator, keysize * len); + dstbucket->values = ALLOC_BYTES(src.allocator, valsize * len); if (dstbucket->keys == NULL || dstbucket->values == NULL) { - free(dstbucket->keys); + FREE_BYTES(src.allocator, dstbucket->keys, keysize * len); dstbucket->keys = NULL; - free(dstbucket->values); + FREE_BYTES(src.allocator, dstbucket->values, valsize * len); dstbucket->values = NULL; goto fail; } - dstbucket->cap = len; + dstbucket->keys_cap = len; + dstbucket->values_cap = len; for (size_t j = 0; j < len; j++) { copy_key( @@ -428,7 +487,7 @@ apfl_hashmap_cursor_next(struct apfl_hashmap_cursor *cursor) cursor_skip_empty_buckets(cursor); } -#define CURSOR_GET(cursor, out, bucketmemb, sizememb, copy) \ +#define CURSOR_PEEK(cursor, out, bucketmemb, sizememb) \ struct apfl_hashmap_bucket *bucket = cursor_get_bucket(cursor); \ \ if (bucket == NULL) { \ @@ -441,20 +500,32 @@ apfl_hashmap_cursor_next(struct apfl_hashmap_cursor *cursor) \ size_t size = cursor.map->sizememb; \ \ - copy( \ - *cursor.map, \ - out, \ - KVADDR(bucket->bucketmemb, size, cursor.i) \ - ); \ + out = KVADDR(bucket->bucketmemb, size, cursor.i); + +void +apfl_hashmap_cursor_peek_key(struct apfl_hashmap_cursor cursor, void **key_ptr) +{ + CURSOR_PEEK(cursor, *key_ptr, keys, keysize) +} + +void +apfl_hashmap_cursor_peek_value(struct apfl_hashmap_cursor cursor, void **value_ptr) +{ + CURSOR_PEEK(cursor, *value_ptr, values, valsize) +} void apfl_hashmap_cursor_get_key(struct apfl_hashmap_cursor cursor, void *key) { - CURSOR_GET(cursor, key, keys, keysize, copy_key) + void *key_ptr; + apfl_hashmap_cursor_peek_key(cursor, &key_ptr); + copy_key(*cursor.map, key, key_ptr); } void apfl_hashmap_cursor_get_value(struct apfl_hashmap_cursor cursor, void *value) { - CURSOR_GET(cursor, value, values, valsize, copy_value) + void *value_ptr; + apfl_hashmap_cursor_peek_value(cursor, &value_ptr); + copy_value(*cursor.map, value, value_ptr); } diff --git a/src/hashmap.h b/src/hashmap.h index 7cbcc89..68ca765 100644 --- a/src/hashmap.h +++ b/src/hashmap.h @@ -9,6 +9,8 @@ extern "C" { #include #include +#include "apfl.h" + #define APFL_HASH_FNV1A_INIT 14695981039346656037U // offset_basis for 64bit FNV-1(a) typedef uint64_t apfl_hash; @@ -42,10 +44,14 @@ struct apfl_hashmap_callbacks { // Copies a value. Returns true on success, false on failure. // If not provided, the bytes will be copied with memcpy. void (*copy_value) (void *opaque, void *dest, void *src); + + // Called on deinitialization of the hashmap, if provided. + void (*on_deinit) (void *opaque); }; struct apfl_hashmap { struct apfl_hashmap_callbacks callbacks; + struct apfl_allocator allocator; size_t keysize; size_t valsize; size_t nbuckets; @@ -55,8 +61,15 @@ struct apfl_hashmap { apfl_hash apfl_hash_fnv1a_add(const void *, size_t len, apfl_hash); apfl_hash apfl_hash_fnv1a(const void *, size_t len); -bool apfl_hashmap_init(struct apfl_hashmap *, struct apfl_hashmap_callbacks, size_t keysize, size_t valsize); +bool apfl_hashmap_init( + struct apfl_hashmap *, + struct apfl_allocator, + struct apfl_hashmap_callbacks, + size_t keysize, + size_t valsize +); void apfl_hashmap_delete(struct apfl_hashmap *, const void *key); +bool apfl_hashmap_peek(const struct apfl_hashmap *, const void *key, void **value_ptr); bool apfl_hashmap_get(const struct apfl_hashmap *, const void *key, void *value); bool apfl_hashmap_set(struct apfl_hashmap *, void *key, void *value); size_t apfl_hashmap_count(const struct apfl_hashmap); @@ -70,6 +83,8 @@ bool apfl_hashmap_copy(struct apfl_hashmap *dst, struct apfl_hashmap src); struct apfl_hashmap_cursor apfl_hashmap_get_cursor(struct apfl_hashmap *); bool apfl_hashmap_cursor_is_end(struct apfl_hashmap_cursor); void apfl_hashmap_cursor_next(struct apfl_hashmap_cursor *); +void apfl_hashmap_cursor_peek_key(struct apfl_hashmap_cursor, void **key_ptr); +void apfl_hashmap_cursor_peek_value(struct apfl_hashmap_cursor, void **value_ptr); void apfl_hashmap_cursor_get_key(struct apfl_hashmap_cursor, void *key); void apfl_hashmap_cursor_get_value(struct apfl_hashmap_cursor, void *value); diff --git a/src/hashmap_test.c b/src/hashmap_test.c index 7c89424..b080b48 100644 --- a/src/hashmap_test.c +++ b/src/hashmap_test.c @@ -9,6 +9,7 @@ init_int2int(testctx t) struct apfl_hashmap map; if (!apfl_hashmap_init( &map, + test_allocator(t), (struct apfl_hashmap_callbacks) { .opaque = NULL }, sizeof(int), sizeof(int) @@ -169,6 +170,7 @@ init_str2str(testctx t) struct apfl_hashmap map; if (!apfl_hashmap_init( &map, + test_allocator(t), (struct apfl_hashmap_callbacks) { .opaque = t, .keys_eq = str2str_keys_eq, diff --git a/src/internal.h b/src/internal.h index 1ecce08..c374124 100644 --- a/src/internal.h +++ b/src/internal.h @@ -8,19 +8,58 @@ extern "C" { #include #include -#include "common.h" +#define DEINIT_CAP_LIST(allocator, items, len, cap, item_deinit) \ +do { \ + if ((items) == NULL) { \ + break; \ + } \ + for (size_t i = 0; i < (len); i++) { \ + item_deinit(allocator, &((items)[i])); \ + } \ + FREE_LIST(allocator, items, cap); \ + len = 0; \ + cap = 0; \ + (items) = NULL; \ +} while(0) -#define DEINIT_LIST(items, len, item_deinit) \ - do { \ - if ((items) == NULL) { \ - break; \ - } \ - for (size_t i = 0; i < (len); i++) { \ - item_deinit(&((items)[i])); \ - } \ - len = 0; \ - free(items); \ - (items) = NULL; \ +#define DEINIT_LIST(allocator, items, len, item_deinit) \ + do { \ + if ((items) == NULL) { \ + break; \ + } \ + for (size_t i = 0; i < (len); i++) { \ + item_deinit(allocator, &((items)[i])); \ + } \ + FREE_LIST(allocator, items, len); \ + len = 0; \ + (items) = NULL; \ + } while(0) + +#define DEINIT_LIST_WITH_ARGS(allocator, items, len, item_deinit, ...) \ + do { \ + if ((items) == NULL) { \ + break; \ + } \ + for (size_t i = 0; i < (len); i++) { \ + item_deinit(__VA_ARGS__, &((items)[i])); \ + } \ + FREE_LIST(allocator, items, len); \ + len = 0; \ + (items) = NULL; \ + } while(0) + +#define DEINIT_CAP_LIST_WITH_ARGS(allocator, items, len, cap, item_deinit, ...) \ + do { \ + if ((items) == NULL) { \ + break; \ + } \ + for (size_t i = 0; i < (len); i++) { \ + item_deinit(__VA_ARGS__, &((items)[i])); \ + } \ + FREE_LIST(allocator, items, cap); \ + len = 0; \ + cap = 0; \ + (items) = NULL; \ } while(0) #define MOVEPTR(out, in) \ @@ -29,18 +68,20 @@ extern "C" { in = NULL; \ } while(0) -// ALLOC_LIST allocates memory for a list of n values of type T. -// n == 0 will always result in NULL (not guaranteed by calloc()) and the -// result will be cast into a pointer to T (this way the compiler can warn us, -// if we try to allocate memory for a wrong type). Also if we always use -// calloc(), the allocated memory is in a defined state. -#define ALLOC_LIST(T, n) (T *)((n) == 0 ? NULL : calloc((n), sizeof(T))) - -#define ALLOC(T) ALLOC_LIST(T, 1) - -// Aliases to commonly used functions / macros - -#define DESTROY APFL_DESTROY +// DESTROY destroys a dynamically allocated value. +// It will first deinit the value using deiniter, +// free the memory and then set the variable to NULL. +// It is always allowed to destroy an already destroyed +// or deinited value. +#define DESTROY(allocator, var, deiniter) \ + do { \ + if ((var) == NULL) { \ + break; \ + } \ + deiniter(allocator, var); \ + FREE_OBJ(allocator, var); \ + (var) = NULL; \ + } while(0) // Internal use only functions diff --git a/src/main.c b/src/main.c index 6507a81..c3048fc 100644 --- a/src/main.c +++ b/src/main.c @@ -35,7 +35,7 @@ repl_source_reader(void *context, char *buf, size_t *len, bool need) } static int -repl_tokenizer(apfl_tokenizer_ptr tokenizer) +repl_tokenizer(struct apfl_allocator allocator, apfl_tokenizer_ptr tokenizer) { while (true) { struct apfl_error err; @@ -45,7 +45,7 @@ repl_tokenizer(apfl_tokenizer_ptr tokenizer) case APFL_PARSE_OK: token = apfl_tokenizer_get_token(tokenizer); apfl_token_print(token, stdout); - apfl_token_deinit(&token); + apfl_token_deinit(allocator, &token); break; case APFL_PARSE_EOF: @@ -89,13 +89,13 @@ repl_parser_generic(apfl_parser_ptr parser, struct apfl_expr *expr, int *rv) } static int -repl_parser(apfl_parser_ptr parser) +repl_parser(struct apfl_allocator allocator, apfl_parser_ptr parser) { struct apfl_expr expr; int rv; while (repl_parser_generic(parser, &expr, &rv)) { apfl_expr_print(expr, stdout); - apfl_expr_deinit(&expr); + apfl_expr_deinit(allocator, &expr); } return rv; } @@ -141,18 +141,19 @@ main(int argc, const char **argv) } } + struct apfl_allocator allocator = apfl_allocator_default(); apfl_tokenizer_ptr tokenizer = NULL; apfl_parser_ptr parser = NULL; apfl_ctx ctx = NULL; - if ((tokenizer = apfl_tokenizer_new(repl_source_reader, NULL)) == NULL) { + if ((tokenizer = apfl_tokenizer_new(allocator, repl_source_reader, NULL)) == NULL) { fprintf(stderr, "Failed initializing tokenizer\n"); rv = 1; goto exit; } if (mode >= REPL_PARSER) { - if ((parser = apfl_parser_new(apfl_tokenizer_as_token_source(tokenizer))) == NULL) { + if ((parser = apfl_parser_new(allocator, apfl_tokenizer_as_token_source(tokenizer))) == NULL) { fprintf(stderr, "Failed initializing parser\n"); rv = 1; goto exit; @@ -160,7 +161,7 @@ main(int argc, const char **argv) } if (mode >= REPL_EVAL) { - if ((ctx = apfl_ctx_new()) == NULL) { + if ((ctx = apfl_ctx_new(allocator)) == NULL) { fprintf(stderr, "Failed initializing context\n"); rv = 1; goto exit; @@ -169,10 +170,10 @@ main(int argc, const char **argv) switch (mode) { case REPL_TOKENIZER: - rv = repl_tokenizer(tokenizer); + rv = repl_tokenizer(allocator, tokenizer); break; case REPL_PARSER: - rv = repl_parser(parser); + rv = repl_parser(allocator, parser); break; case REPL_EVAL: rv = repl_eval(parser, ctx); diff --git a/src/parser.c b/src/parser.c index 8b5665e..e452eab 100644 --- a/src/parser.c +++ b/src/parser.c @@ -4,10 +4,13 @@ #include "apfl.h" +#include "alloc.h" #include "resizable.h" #include "internal.h" struct apfl_parser { + struct apfl_allocator allocator; + struct apfl_parser_token_source token_source; bool has_expr; @@ -78,9 +81,10 @@ struct fragment { static enum parse_fragment_result parse_fragment(apfl_parser_ptr, struct fragment*, bool need, enum parse_fragment_flags); static bool -grow_fragment_cap(struct fragment_list *list, size_t inc) +grow_fragment_cap(struct apfl_allocator allocator, struct fragment_list *list, size_t inc) { return apfl_resizable_ensure_cap_for_more_elements( + allocator, sizeof(struct fragment), (void **)&list->children, list->len, @@ -90,9 +94,10 @@ grow_fragment_cap(struct fragment_list *list, size_t inc) } static bool -append_fragment(struct fragment_list *list, struct fragment fragment) +append_fragment(struct apfl_allocator allocator, struct fragment_list *list, struct fragment fragment) { return apfl_resizable_append( + allocator, sizeof(struct fragment), APFL_RESIZABLE_ARGS(*list, children), &fragment, @@ -100,24 +105,24 @@ append_fragment(struct fragment_list *list, struct fragment fragment) ); } -static void fragment_deinit(struct fragment *); +static void fragment_deinit(struct apfl_allocator allocator, struct fragment *); static void -deinit_fragment_lhs_rhs(struct fragment_lhs_rhs *lr) +deinit_fragment_lhs_rhs(struct apfl_allocator allocator, struct fragment_lhs_rhs *lr) { - DESTROY(lr->lhs, fragment_deinit); - DESTROY(lr->rhs, fragment_deinit); + DESTROY(allocator, lr->lhs, fragment_deinit); + DESTROY(allocator, lr->rhs, fragment_deinit); } static void -fragment_list_deinit(struct fragment_list *list) +fragment_list_deinit(struct apfl_allocator allocator, struct fragment_list *list) { - DEINIT_LIST(list->children, list->len, fragment_deinit); + DEINIT_CAP_LIST(allocator, list->children, list->len, list->cap, fragment_deinit); apfl_resizable_init(APFL_RESIZABLE_ARGS(*list, children)); } static void -fragment_deinit(struct fragment *fragment) +fragment_deinit(struct apfl_allocator allocator, struct fragment *fragment) { if (fragment == NULL) { return; @@ -125,29 +130,29 @@ fragment_deinit(struct fragment *fragment) switch (fragment->type) { case FRAG_EXPAND: - DESTROY(fragment->expand, fragment_deinit); + DESTROY(allocator, fragment->expand, fragment_deinit); break; case FRAG_CONSTANT: - apfl_expr_const_deinit(&fragment->constant); + apfl_expr_const_deinit(allocator, &fragment->constant); break; case FRAG_NAME: - apfl_refcounted_string_unref_ptr(&fragment->name); + apfl_refcounted_string_unref_ptr(allocator, &fragment->name); break; case FRAG_DOT: - DESTROY(fragment->dot.lhs, fragment_deinit); - apfl_refcounted_string_unref_ptr(&fragment->dot.rhs); + DESTROY(allocator, fragment->dot.lhs, fragment_deinit); + apfl_refcounted_string_unref_ptr(allocator, &fragment->dot.rhs); break; case FRAG_AT: - deinit_fragment_lhs_rhs(&fragment->at); + deinit_fragment_lhs_rhs(allocator, &fragment->at); break; case FRAG_PREDICATE: - deinit_fragment_lhs_rhs(&fragment->predicate); + deinit_fragment_lhs_rhs(allocator, &fragment->predicate); break; case FRAG_EXPR: - apfl_expr_deinit(&fragment->expr); + apfl_expr_deinit(allocator, &fragment->expr); break; case FRAG_LIST: - fragment_list_deinit(&fragment->list); + fragment_list_deinit(allocator, &fragment->list); break; case FRAG_BLANK: // nop @@ -222,13 +227,14 @@ fragment_move(struct fragment *in) } apfl_parser_ptr -apfl_parser_new(struct apfl_parser_token_source token_source) +apfl_parser_new(struct apfl_allocator allocator, struct apfl_parser_token_source token_source) { - apfl_parser_ptr p = malloc(sizeof(struct apfl_parser)); + apfl_parser_ptr p = ALLOC_OBJ(allocator, struct apfl_parser); if (p == NULL) { return NULL; } + p->allocator = allocator; p->token_source = token_source; p->eof = false; p->has_token = false; @@ -271,7 +277,7 @@ get_non_comment_token(apfl_parser_ptr p, struct apfl_token *token, bool need) } if (token->type == APFL_TOK_COMMENT) { - apfl_token_deinit(token); + apfl_token_deinit(p->allocator, token); } else { return APFL_PARSE_OK; } @@ -296,7 +302,7 @@ get_preprocessed_token(apfl_parser_ptr p, struct apfl_token *token, bool need) struct apfl_position continue_line_pos = token->position; - apfl_token_deinit(token); + apfl_token_deinit(p->allocator, token); result = get_non_comment_token(p, token, true); if (result != APFL_PARSE_OK) { @@ -304,7 +310,7 @@ get_preprocessed_token(apfl_parser_ptr p, struct apfl_token *token, bool need) } if (token->type != APFL_TOK_LINEBREAK) { - apfl_token_deinit(token); + apfl_token_deinit(p->allocator, token); p->error = (struct apfl_error) { .type = APFL_ERR_NO_LINEBREAK_AFTER_CONTINUE_LINE, @@ -314,7 +320,7 @@ get_preprocessed_token(apfl_parser_ptr p, struct apfl_token *token, bool need) return APFL_PARSE_ERROR; } - apfl_token_deinit(token); + apfl_token_deinit(p->allocator, token); } } @@ -332,7 +338,7 @@ read_token(apfl_parser_ptr p, bool need) if (p->has_token) { - apfl_token_deinit(&p->token); + apfl_token_deinit(p->allocator, &p->token); } enum apfl_parse_result result = get_preprocessed_token(p, &p->token, need); @@ -374,7 +380,7 @@ err_unexpected_token(enum apfl_token_type token_type, struct apfl_position pos) static enum parse_fragment_result parse_fragment_into_list(apfl_parser_ptr p, struct fragment_list *list, bool need, enum parse_fragment_flags flags) { - if (!grow_fragment_cap(list, 1)) { + if (!grow_fragment_cap(p->allocator, list, 1)) { p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); return PF_ERROR; } @@ -481,7 +487,7 @@ parse_parens(apfl_parser_ptr p, struct fragment *out, struct apfl_position posit return true; error: - fragment_list_deinit(&children); + fragment_list_deinit(p->allocator, &children); return false; } @@ -540,6 +546,7 @@ parse_empty_dict(apfl_parser_ptr p, struct fragment *out, struct apfl_position p .type = APFL_EXPR_DICT, .dict.items = NULL, .dict.len = 0, + .dict.cap = 0, .position = position, }; out->position = position; @@ -577,6 +584,37 @@ static bool fragment_to_list_item( static struct apfl_expr *fragment_to_expr_allocated(apfl_parser_ptr, struct fragment); +static bool +fragment_list_to_expr_list(apfl_parser_ptr p, struct fragment_list *frag_list, size_t off, struct apfl_expr_list *out) +{ + out->len = 0; + out->items = NULL; + + size_t total = frag_list->len - off; + + if (total == 0) { + out->items = NULL; + return true; + } + + if ((out->items = ALLOC_LIST( + p->allocator, + struct apfl_expr_list_item, + total + )) == NULL) { + p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); + return false; + } + for (size_t i = off; i < frag_list->len; i++) { + if (!fragment_to_list_item(p, fragment_move(&frag_list->children[i]), &out->items[i - off])) { + DEINIT_CAP_LIST(p->allocator, out->items, out->len, total, apfl_expr_list_item_deinit); + return false; + } + out->len++; + } + return true; +} + static bool fragment_to_expr_inner(apfl_parser_ptr p, struct fragment *fragment, struct apfl_expr *out) { @@ -621,22 +659,7 @@ fragment_to_expr_inner(apfl_parser_ptr p, struct fragment *fragment, struct apfl case FRAG_LIST: out->type = APFL_EXPR_LIST; out->position = fragment->position; - out->list.len = 0; - if (fragment->list.len == 0) { - out->list.items = NULL; - return true; - } - if ((out->list.items = ALLOC_LIST(struct apfl_expr_list_item, fragment->list.len)) == NULL) { - p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); - return false; - } - for (size_t i = 0; i < fragment->list.len; i++) { - if (!fragment_to_list_item(p, fragment_move(&fragment->list.children[i]), &out->list.items[i])) { - return false; - } - out->list.len++; - } - return true; + return fragment_list_to_expr_list(p, &fragment->list, 0, &out->list); case FRAG_BLANK: out->type = APFL_EXPR_BLANK; out->position = fragment->position; @@ -650,21 +673,21 @@ static bool fragment_to_expr(apfl_parser_ptr p, struct fragment fragment, struct apfl_expr *out) { bool result = fragment_to_expr_inner(p, &fragment, out); - fragment_deinit(&fragment); + fragment_deinit(p->allocator, &fragment); return result; } static struct apfl_expr * fragment_to_expr_allocated(apfl_parser_ptr p, struct fragment fragment) { - struct apfl_expr *out = malloc(sizeof(struct apfl_expr)); + struct apfl_expr *out = ALLOC_OBJ(p->allocator, struct apfl_expr); if (out == NULL) { p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); return NULL; } if (!fragment_to_expr(p, fragment, out)) { - free(out); + FREE_OBJ(p->allocator, out); return NULL; } return out; @@ -686,8 +709,8 @@ parse_dict( struct apfl_expr_dict dict = { .items = NULL, .len = 0, + .cap = 0, }; - size_t dict_cap = 0; goto after_mapsto; @@ -759,20 +782,21 @@ after_mapsto: goto error; } - fragment_deinit(&key); + fragment_deinit(p->allocator, &key); cleanup_key = false; - fragment_deinit(&value); + fragment_deinit(p->allocator, &value); cleanup_value = false; if (!apfl_resizable_append( + p->allocator, sizeof(struct apfl_expr_dict_pair), (void **)&dict.items, &dict.len, - &dict_cap, + &dict.cap, &pair, 1 )) { - apfl_expr_dict_pair_deinit(&pair); + apfl_expr_dict_pair_deinit(p->allocator, &pair); p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); goto error; } @@ -798,12 +822,12 @@ maybe_end: error: if (cleanup_key) { - fragment_deinit(&key); + fragment_deinit(p->allocator, &key); } if (cleanup_value) { - fragment_deinit(&value); + fragment_deinit(p->allocator, &value); } - apfl_expr_dict_deinit(&dict); + apfl_expr_dict_deinit(p->allocator, &dict); return false; } @@ -817,10 +841,10 @@ parse_list( struct fragment_list list; apfl_resizable_init(APFL_RESIZABLE_ARGS(list, children)); - if (!append_fragment(&list, first)) { - fragment_deinit(&first); + if (!append_fragment(p->allocator, &list, first)) { + fragment_deinit(p->allocator, &first); p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); - fragment_list_deinit(&list); + fragment_list_deinit(p->allocator, &list); return false; } @@ -856,7 +880,7 @@ maybe_end: return true; error: - fragment_list_deinit(&list); + fragment_list_deinit(p->allocator, &list); return false; } @@ -903,14 +927,14 @@ parse_brackets(apfl_parser_ptr p, struct fragment *out, struct apfl_position sta } error: - fragment_deinit(&first); + fragment_deinit(p->allocator, &first); return false; } static bool parse_expand(apfl_parser_ptr p, struct fragment *fragment, struct apfl_position position) { - struct fragment *inner = malloc(sizeof(struct fragment)); + struct fragment *inner = ALLOC_OBJ(p->allocator, struct fragment); if (inner == NULL) { p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); return false; @@ -924,7 +948,7 @@ parse_expand(apfl_parser_ptr p, struct fragment *fragment, struct apfl_position return true; } - free(inner); + FREE_OBJ(p->allocator, inner); switch (result) { case PF_OK: @@ -984,13 +1008,13 @@ parse_stringify(apfl_parser_ptr p, struct fragment *fragment, struct apfl_positi } static struct fragment -fragment_unwrap_expand(struct fragment fragment) +fragment_unwrap_expand(struct apfl_allocator allocator, struct fragment fragment) { assert(fragment.type == FRAG_EXPAND); struct fragment tmp = fragment_move(fragment.expand); - free(fragment.expand); + FREE_OBJ(allocator, fragment.expand); fragment.expand = NULL; - fragment_deinit(&fragment); + fragment_deinit(allocator, &fragment); return tmp; } @@ -1008,12 +1032,12 @@ predicate_fragment_to_param( out->predicate.lhs = NULL; out->predicate.rhs = NULL; - if ((out->predicate.lhs = malloc(sizeof(struct apfl_expr_param))) == NULL) { + if ((out->predicate.lhs = ALLOC_OBJ(p->allocator, struct apfl_expr_param)) == NULL) { goto error; } if (!fragment_to_param(p, fragment_move(lhs_rhs->lhs), out->predicate.lhs)) { - free(out->predicate.lhs); + FREE_OBJ(p->allocator, out->predicate.lhs); out->predicate.lhs = NULL; goto error; } @@ -1026,7 +1050,7 @@ predicate_fragment_to_param( return true; error: - apfl_expr_param_deinit(out); + apfl_expr_param_deinit(p->allocator, out); return false; } @@ -1080,7 +1104,7 @@ fragment_to_param( struct apfl_expr_param *out ) { bool ok = fragment_to_param_inner(p, &fragment, out); - fragment_deinit(&fragment); + fragment_deinit(p->allocator, &fragment); return ok; } @@ -1090,17 +1114,19 @@ fragments_to_params_inner( /*borrowed*/ struct fragment_list fragments, struct apfl_expr_params *out ) { + out->len = 0; + out->cap = 0; + out->params = NULL; + if (fragments.len == 0) { - out->len = 0; - out->params = NULL; return true; } - out->params = ALLOC_LIST(struct apfl_expr_params_item, fragments.len); - out->len = 0; + out->params = ALLOC_LIST(p->allocator, struct apfl_expr_params_item, fragments.len); if (out->params == NULL) { goto error; } + out->cap = fragments.len; bool seen_expand = false; for (size_t i = 0; i < fragments.len; i++) { @@ -1113,14 +1139,14 @@ fragments_to_params_inner( .type = APFL_ERR_ONLY_ONE_EXPAND_ALLOWED, .position = item_fragment.position, }; - fragment_deinit(&item_fragment); + fragment_deinit(p->allocator, &item_fragment); goto error; } out_item->expand = true; seen_expand = true; - item_fragment = fragment_unwrap_expand(item_fragment); + item_fragment = fragment_unwrap_expand(p->allocator, item_fragment); } else { out_item->expand = false; } @@ -1139,7 +1165,7 @@ fragments_to_params_inner( return true; error: - apfl_expr_params_deinit(out); + apfl_expr_params_deinit(p->allocator, out); return false; } @@ -1150,7 +1176,7 @@ fragments_to_params( struct apfl_expr_params *out ) { bool ok = fragments_to_params_inner(p, fragments, out); - fragment_list_deinit(&fragments); + fragment_list_deinit(p->allocator, &fragments); return ok; } @@ -1177,13 +1203,13 @@ static bool fragment_to_assignable_var_or_member( out->var = apfl_refcounted_string_incref(fragment->name); return true; case FRAG_DOT: - lhs = ALLOC(struct apfl_expr_assignable_var_or_member); + lhs = ALLOC_OBJ(p->allocator, struct apfl_expr_assignable_var_or_member); if (lhs == NULL) { p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); return false; } if (!fragment_to_assignable_var_or_member(p, fragment->dot.lhs, lhs)) { - free(lhs); + FREE_OBJ(p->allocator, lhs); return false; } out->type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT; @@ -1193,20 +1219,20 @@ static bool fragment_to_assignable_var_or_member( }; return true; case FRAG_AT: - lhs = ALLOC(struct apfl_expr_assignable_var_or_member); - rhs = ALLOC(struct apfl_expr); + lhs = ALLOC_OBJ(p->allocator, struct apfl_expr_assignable_var_or_member); + rhs = ALLOC_OBJ(p->allocator, struct apfl_expr); if (lhs == NULL || rhs == NULL) { p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); return false; } if (!fragment_to_assignable_var_or_member(p, fragment->at.lhs, lhs)) { - free(lhs); - free(rhs); + FREE_OBJ(p->allocator, lhs); + FREE_OBJ(p->allocator, rhs); return false; } if (!fragment_to_expr(p, fragment_move(fragment->at.rhs), rhs)) { - DESTROY(lhs, apfl_expr_assignable_var_or_member_deinit); - free(rhs); + DESTROY(p->allocator, lhs, apfl_expr_assignable_var_or_member_deinit); + FREE_OBJ(p->allocator, rhs); return false; } out->type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT; @@ -1247,6 +1273,89 @@ fragment_to_assignable( struct apfl_expr_assignable *out ); +static bool +fragment_list_to_assignable_list_inner( + apfl_parser_ptr p, + /*borrowed*/ struct fragment_list fragment_list, + struct apfl_expr_assignable_list *out +) { + *out = (struct apfl_expr_assignable_list) { + .items = NULL, + .len = 0, + }; + + if (fragment_list.len == 0) { + return true; + } + + if ((out->items = ALLOC_LIST( + p->allocator, + struct apfl_expr_assignable_list_item, + fragment_list.len + )) == NULL) { + p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); + return false; + } + + bool expand_ok = true; + for (size_t i = 0; i < fragment_list.len; i++) { + struct apfl_expr_assignable_list_item *out_item = &out->items[i]; + struct fragment item_fragment = fragment_move(&fragment_list.children[i]); + + if (item_fragment.type == FRAG_EXPAND) { + if (!expand_ok) { + p->error = (struct apfl_error) { + .type = APFL_ERR_ONLY_ONE_EXPAND_ALLOWED, + .position = item_fragment.position, + }; + fragment_deinit(p->allocator, &item_fragment); + goto error; + } + + out_item->expand = true; + expand_ok = false; + + item_fragment = fragment_unwrap_expand(p->allocator, item_fragment); + } else { + out_item->expand = false; + } + + if (!fragment_to_assignable( + p, + fragment_move(&item_fragment), + &out_item->assignable + )) { + goto error; + } + + out->len++; + } + + return true; + +error: + DEINIT_CAP_LIST( + p->allocator, + out->items, + out->len, + fragment_list.len, + apfl_expr_assignable_list_item_deinit + ); + + return false; +} + +static bool +fragment_list_to_assignable_list( + apfl_parser_ptr p, + struct fragment_list fragment_list, + struct apfl_expr_assignable_list *out +) { + bool ok = fragment_list_to_assignable_list_inner(p, fragment_list, out); + fragment_list_deinit(p->allocator, &fragment_list); + return ok; +} + static bool fragment_to_assignable_inner( apfl_parser_ptr p, @@ -1275,8 +1384,8 @@ fragment_to_assignable_inner( case FRAG_PREDICATE: out->type = APFL_EXPR_ASSIGNABLE_PREDICATE; - out->predicate.lhs = malloc(sizeof(struct apfl_expr_assignable)); - out->predicate.rhs = malloc(sizeof(struct apfl_expr)); + out->predicate.lhs = ALLOC_OBJ(p->allocator, struct apfl_expr_assignable); + out->predicate.rhs = ALLOC_OBJ(p->allocator, struct apfl_expr); if (out->predicate.lhs == NULL || out->predicate.rhs == NULL) { p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); @@ -1288,7 +1397,7 @@ fragment_to_assignable_inner( fragment_move(fragment->at.lhs), out->predicate.lhs )) { - free(out->predicate.rhs); + FREE_OBJ(p->allocator, out->predicate.rhs); out->predicate.rhs = NULL; goto error; } @@ -1310,54 +1419,14 @@ fragment_to_assignable_inner( goto error; case FRAG_LIST: out->type = APFL_EXPR_ASSIGNABLE_LIST; - out->list.len = 0; - - if (fragment->list.len == 0) { - return true; - } - - if ((out->list.items = ALLOC_LIST( - struct apfl_expr_assignable_list_item, - fragment->list.len - )) == NULL) { - p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); + if (!fragment_list_to_assignable_list( + p, + fragment_list_move(&fragment->list), + &out->list + )) { goto error; } - bool expand_ok = true; - for (size_t i = 0; i < fragment->list.len; i++) { - struct apfl_expr_assignable_list_item *out_item = &out->list.items[i]; - struct fragment item_fragment = fragment_move(&fragment->list.children[i]); - - if (item_fragment.type == FRAG_EXPAND) { - if (!expand_ok) { - p->error = (struct apfl_error) { - .type = APFL_ERR_ONLY_ONE_EXPAND_ALLOWED, - .position = item_fragment.position, - }; - fragment_deinit(&item_fragment); - goto error; - } - - out_item->expand = true; - expand_ok = false; - - item_fragment = fragment_unwrap_expand(item_fragment); - } else { - out_item->expand = false; - } - - if (!fragment_to_assignable( - p, - fragment_move(&item_fragment), - &out_item->assignable - )) { - goto error; - } - - out->list.len++; - } - return true; case FRAG_BLANK: out->type = APFL_EXPR_ASSIGNABLE_BLANK; @@ -1384,11 +1453,11 @@ fragment_to_assignable( }; bool result = fragment_to_assignable_inner(p, &fragment, &out); - fragment_deinit(&fragment); + fragment_deinit(p->allocator, &fragment); if (result) { *outptr = out; } else { - apfl_expr_assignable_deinit(&out); + apfl_expr_assignable_deinit(p->allocator, &out); } return result; } @@ -1400,15 +1469,27 @@ struct partial_assignment { }; static void -partial_assignment_deinit(struct partial_assignment *pa) +partial_assignment_deinit(struct apfl_allocator allocator, struct partial_assignment *pa) { - apfl_expr_assignable_deinit(&pa->lhs); + apfl_expr_assignable_deinit(allocator, &pa->lhs); } struct partial_assignment_list { APFL_RESIZABLE_TRAIT(struct partial_assignment, items) }; +static void +partial_assignment_list_deinit(struct apfl_allocator allocator, struct partial_assignment_list *assignments) +{ + DEINIT_CAP_LIST( + allocator, + assignments->items, + assignments->len, + assignments->cap, + partial_assignment_deinit + ); +} + enum parse_body_or_toplevel_finalize_result { BODY_FINALIZE_ERROR, BODY_FINALIZE_OK, @@ -1424,7 +1505,7 @@ fragment_to_list_item( if (fragment.type == FRAG_EXPAND) { out->expand = true; out->expr = fragment_to_expr_allocated(p, fragment_move(fragment.expand)); - fragment_deinit(&fragment); + fragment_deinit(p->allocator, &fragment); return out->expr != NULL; } else { out->expand = false; @@ -1456,32 +1537,25 @@ fragments_to_call( } if (fragments.len == 1) { - fragment_list_deinit(&fragments); + fragment_list_deinit(p->allocator, &fragments); return true; } - if ((out->call.arguments.items = ALLOC_LIST( - struct apfl_expr_list_item, - (fragments.len - 1) - )) == NULL) { - p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); + if (!fragment_list_to_expr_list( + p, + &fragments, + 1, + &out->call.arguments + )) { goto error; } - for (size_t i = 1; i < fragments.len; i++) { - if (!fragment_to_list_item(p, fragment_move(&fragments.children[i]), &out->call.arguments.items[i-1])) { - goto error; - } - - out->call.arguments.len++; - } - - fragment_list_deinit(&fragments); + fragment_list_deinit(p->allocator, &fragments); return true; error: - fragment_list_deinit(&fragments); - apfl_expr_deinit(out); + fragment_list_deinit(p->allocator, &fragments); + apfl_expr_deinit(p->allocator, out); return false; } @@ -1508,8 +1582,8 @@ parse_body_or_toplevel_finalize( goto error; } - fragment_list_deinit(&fragments); - DEINIT_LIST(partial_assignments.items, partial_assignments.len, partial_assignment_deinit); + fragment_list_deinit(p->allocator, &fragments); + partial_assignment_list_deinit(p->allocator, &partial_assignments); return BODY_FINALIZE_EMPTY; } @@ -1520,14 +1594,14 @@ parse_body_or_toplevel_finalize( dest->position = cur->position; dest->assignment.local = cur->local; dest->assignment.lhs = apfl_expr_assignable_move(&cur->lhs); - if ((dest->assignment.rhs = ALLOC(struct apfl_expr)) == NULL) { + if ((dest->assignment.rhs = ALLOC_OBJ(p->allocator, struct apfl_expr)) == NULL) { p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); goto error; } dest = dest->assignment.rhs; } - DEINIT_LIST(partial_assignments.items, partial_assignments.len, partial_assignment_deinit); + partial_assignment_list_deinit(p->allocator, &partial_assignments); if (fragments.len == 1) { if (!fragment_to_expr(p, fragment_move(&fragments.children[0]), dest)) { @@ -1540,14 +1614,14 @@ parse_body_or_toplevel_finalize( } } - fragment_list_deinit(&fragments); + fragment_list_deinit(p->allocator, &fragments); return BODY_FINALIZE_OK; error: - fragment_list_deinit(&fragments); - DEINIT_LIST(partial_assignments.items, partial_assignments.len, partial_assignment_deinit); - apfl_expr_deinit(out); + fragment_list_deinit(p->allocator, &fragments); + partial_assignment_list_deinit(p->allocator, &partial_assignments); + apfl_expr_deinit(p->allocator, out); return BODY_FINALIZE_ERROR; } @@ -1633,6 +1707,7 @@ break_inner: } if (!apfl_resizable_ensure_cap_for_more_elements( + p->allocator, sizeof(struct partial_assignment), (void **)&partial_assignments.items, partial_assignments.len, @@ -1648,7 +1723,7 @@ break_inner: cur_partial->position = position; struct fragment fragment = fragment_move(&fragments->children[0]); - fragment_list_deinit(fragments); // Reset fragment list + fragment_list_deinit(p->allocator, fragments); // Reset fragment list if (!fragment_to_assignable(p, fragment, &cur_partial->lhs)) { goto error; @@ -1698,20 +1773,21 @@ break_inner: } error: - DEINIT_LIST(partial_assignments.items, partial_assignments.len, partial_assignment_deinit); + partial_assignment_list_deinit(p->allocator, &partial_assignments); return PF_ERROR; } static bool init_body(apfl_parser_ptr p, struct apfl_expr_body **body) { - if ((*body = ALLOC(struct apfl_expr_body)) == NULL) { + if ((*body = ALLOC_OBJ(p->allocator, struct apfl_expr_body)) == NULL) { p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); return false; } **body = (struct apfl_expr_body) { .items = NULL, .len = 0, + .cap = 0, .refcount = 1, }; return true; @@ -1723,12 +1799,15 @@ parse_braces( struct fragment *out, struct apfl_position start ) { - struct apfl_expr_subfunc *subfuncs = NULL; - size_t subfuncs_len = 0; - size_t subfuncs_cap = 0; + struct apfl_expr_complex_func complex_func = { + .subfuncs = NULL, + .len = 0, + .cap = 0, + }; + bool has_params = false; - struct apfl_expr_params params = { .params = NULL, .len = 0 }; + struct apfl_expr_params params = { .params = NULL, .len = 0, .cap = 0 }; struct fragment_list fragments; apfl_resizable_init(APFL_RESIZABLE_ARGS(fragments, children)); @@ -1741,9 +1820,9 @@ parse_braces( *body = (struct apfl_expr_body) { .items = NULL, .len = 0, + .cap = 0, .refcount = 1, }; - size_t body_cap = 0; for (;;) { struct apfl_expr expr; @@ -1757,14 +1836,15 @@ parse_braces( )) { case PF_OK: if (!apfl_resizable_append( + p->allocator, sizeof(struct apfl_expr), (void **)&body->items, &body->len, - &body_cap, + &body->cap, &expr, 1 )) { - apfl_expr_deinit(&expr); + apfl_expr_deinit(p->allocator, &expr); p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); goto error; } @@ -1779,17 +1859,19 @@ parse_braces( switch (p->token.type) { case APFL_TOK_RBRACE: - fragment_list_deinit(&fragments); + fragment_list_deinit(p->allocator, &fragments); - // TODO: Rather fulgly duplication + // TODO: Rather fugly duplication if (has_params) { // Finalize previous subfunc and append if (!apfl_resizable_append( + p->allocator, sizeof(struct apfl_expr_subfunc), - (void **)&subfuncs, - &subfuncs_len, - &subfuncs_cap, + (void **)&complex_func.subfuncs, + &complex_func.len, + &complex_func.cap, &(struct apfl_expr_subfunc) { + // TODO: If apfl_resizable_append fails, we're leaking these! .params = apfl_expr_params_move(¶ms), .body = apfl_expr_body_move(&body), }, @@ -1799,30 +1881,24 @@ parse_braces( goto error; } - params = (struct apfl_expr_params) { .params = NULL, .len = 0 }; + params = (struct apfl_expr_params) { .params = NULL, .len = 0, .cap = 0 }; body = NULL; - body_cap = 0; } out->type = FRAG_EXPR; if (has_params) { out->expr = (struct apfl_expr) { .type = APFL_EXPR_COMPLEX_FUNC, - .complex_func.subfuncs = subfuncs, - .complex_func.len = subfuncs_len, + .complex_func = apfl_expr_complex_func_move(&complex_func), .position = start, }; - subfuncs = NULL; - subfuncs_len = 0; - subfuncs_cap = 0; } else { out->expr = (struct apfl_expr) { .type = APFL_EXPR_SIMPLE_FUNC, .simple_func = apfl_expr_body_move(&body), .position = start, }; - body_cap = 0; } return true; @@ -1838,10 +1914,11 @@ parse_braces( if (has_params) { // Finalize previous subfunc and append if (!apfl_resizable_append( + p->allocator, sizeof(struct apfl_expr_subfunc), - (void **)&subfuncs, - &subfuncs_len, - &subfuncs_cap, + (void **)&complex_func.subfuncs, + &complex_func.len, + &complex_func.cap, &(struct apfl_expr_subfunc) { .params = apfl_expr_params_move(¶ms), .body = apfl_expr_body_move(&body), @@ -1852,11 +1929,10 @@ parse_braces( goto error; } - params = (struct apfl_expr_params) { .params = NULL, .len = 0 }; + params = (struct apfl_expr_params) { .params = NULL, .len = 0, .cap = 0 }; if (!init_body(p, &body)) { goto error; } - body_cap = 0; } if (!fragments_to_params(p, fragment_list_move(&fragments), ¶ms)) { @@ -1875,10 +1951,10 @@ parse_braces( } error: - apfl_expr_body_unref(body); - apfl_expr_params_deinit(¶ms); - fragment_list_deinit(&fragments); - DEINIT_LIST(subfuncs, subfuncs_len, apfl_expr_subfunc_deinit); + apfl_expr_body_unref(p->allocator, body); + apfl_expr_params_deinit(p->allocator, ¶ms); + fragment_list_deinit(p->allocator, &fragments); + apfl_expr_complex_func_deinit(p->allocator, &complex_func); return false; } @@ -1987,7 +2063,7 @@ parse_fragment(apfl_parser_ptr p, struct fragment *fragment, bool need, enum par case APFL_PARSE_EOF: return PF_OK; case APFL_PARSE_ERROR: - fragment_deinit(fragment); + fragment_deinit(p->allocator, fragment); return PF_ERROR; } @@ -2000,7 +2076,7 @@ parse_fragment(apfl_parser_ptr p, struct fragment *fragment, bool need, enum par return PF_ERROR; } - if ((lhs = ALLOC(struct fragment)) == NULL) { + if ((lhs = ALLOC_OBJ(p->allocator, struct fragment)) == NULL) { p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); return PF_ERROR; } @@ -2014,8 +2090,8 @@ parse_fragment(apfl_parser_ptr p, struct fragment *fragment, bool need, enum par case APFL_TOK_AT: case APFL_TOK_QUESTION_MARK: if ( - ((lhs = ALLOC(struct fragment)) == NULL) - || ((rhs = ALLOC(struct fragment)) == NULL) + ((lhs = ALLOC_OBJ(p->allocator, struct fragment)) == NULL) + || ((rhs = ALLOC_OBJ(p->allocator, struct fragment)) == NULL) ) { p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); return PF_ERROR; @@ -2061,9 +2137,9 @@ parse_fragment(apfl_parser_ptr p, struct fragment *fragment, bool need, enum par return PF_OK; error: - DESTROY(lhs, fragment_deinit); - DESTROY(rhs, fragment_deinit); - fragment_deinit(fragment); + DESTROY(p->allocator, lhs, fragment_deinit); + DESTROY(p->allocator, rhs, fragment_deinit); + fragment_deinit(p->allocator, fragment); return PF_ERROR; } @@ -2071,7 +2147,7 @@ enum apfl_parse_result apfl_parser_next(apfl_parser_ptr p) { if (p->has_expr) { - apfl_expr_deinit(&p->expr); + apfl_expr_deinit(p->allocator, &p->expr); } struct fragment_list fragments; @@ -2086,12 +2162,12 @@ apfl_parser_next(apfl_parser_ptr p) )) { case PF_OK: p->has_expr = true; - fragment_list_deinit(&fragments); + fragment_list_deinit(p->allocator, &fragments); return APFL_PARSE_OK; case PF_ERROR: goto error; case PF_EOF: - fragment_list_deinit(&fragments); + fragment_list_deinit(p->allocator, &fragments); return APFL_PARSE_EOF; case PF_CANT_HANDLE: read_token_after_cant_handle(p); @@ -2102,7 +2178,7 @@ apfl_parser_next(apfl_parser_ptr p) assert(false); error: - fragment_list_deinit(&fragments); + fragment_list_deinit(p->allocator, &fragments); return APFL_PARSE_ERROR; } @@ -2126,10 +2202,10 @@ apfl_parser_destroy(apfl_parser_ptr p) return; } if (p->has_expr) { - apfl_expr_deinit(&p->expr); + apfl_expr_deinit(p->allocator, &p->expr); } if (p->has_token) { - apfl_token_deinit(&p->token); + apfl_token_deinit(p->allocator, &p->token); } - free(p); + FREE_OBJ(p->allocator, p); } diff --git a/src/parser_test.c b/src/parser_test.c index 86deaa1..fda342e 100644 --- a/src/parser_test.c +++ b/src/parser_test.c @@ -3,10 +3,12 @@ #include "apfl.h" #include "resizable.h" +#include "alloc.h" #include "test.h" struct parser_test { testctx t; + struct apfl_allocator allocator; void *source_reader_ctx; apfl_tokenizer_ptr tokenizer; apfl_parser_ptr parser; @@ -15,17 +17,21 @@ struct parser_test { static struct parser_test * new_parser_test(testctx t, const char *source) { + struct apfl_allocator allocator = test_allocator(t); + struct parser_test *pt = must_alloc(t, sizeof(struct parser_test)); pt->t = t; - if ((pt->source_reader_ctx = apfl_string_source_reader_new(apfl_string_view_from(source))) == NULL) { + pt->allocator = allocator; + + if ((pt->source_reader_ctx = apfl_string_source_reader_new(allocator, apfl_string_view_from(source))) == NULL) { test_fatalf(t, "Failed initializing source reader"); } - if ((pt->tokenizer = apfl_tokenizer_new(apfl_string_source_reader, pt->source_reader_ctx)) == NULL) { + if ((pt->tokenizer = apfl_tokenizer_new(allocator, apfl_string_source_reader, pt->source_reader_ctx)) == NULL) { test_fatalf(t, "Failed initializing the tokenizer"); } - if ((pt->parser = apfl_parser_new(apfl_tokenizer_as_token_source(pt->tokenizer))) == NULL) { + if ((pt->parser = apfl_parser_new(allocator, apfl_tokenizer_as_token_source(pt->tokenizer))) == NULL) { test_fatalf(t, "Failed initializing the parser"); } @@ -73,8 +79,8 @@ expect_expr(struct parser_test *pt, struct apfl_expr expected) test_failf(pt->t, "Have:"); apfl_expr_print(expr, stderr); } - apfl_expr_deinit(&expr); - apfl_expr_deinit(&expected); + apfl_expr_deinit(pt->allocator, &expr); + apfl_expr_deinit(pt->allocator, &expected); break; case APFL_PARSE_EOF: test_fatalf(pt->t, "Extected an expression but got EOF"); @@ -111,7 +117,7 @@ expect_error_of_type(struct parser_test *pt, enum apfl_error_type want) static apfl_refcounted_string new_string(struct parser_test *pt, const char *in) { - apfl_refcounted_string out = apfl_string_copy_into_new_refcounted(apfl_string_view_from(in)); + apfl_refcounted_string out = apfl_string_copy_into_new_refcounted(pt->allocator, apfl_string_view_from(in)); if (out == NULL) { test_fatalf(pt->t, "Failed copying string in new_string"); } @@ -121,7 +127,7 @@ new_string(struct parser_test *pt, const char *in) static void * new_helper(struct parser_test *pt, size_t size, void *data) { - void *out = must_alloc(pt->t, size); + void *out = ALLOC_BYTES(pt->allocator, size); memcpy(out, data, size); return out; } @@ -161,9 +167,10 @@ struct test_pre_body { static struct apfl_expr_body * create_body(struct parser_test *pt, struct test_pre_body pre_body) { - struct apfl_expr_body *body = must_alloc(pt->t, sizeof(struct apfl_expr_body)); + struct apfl_expr_body *body = ALLOC_OBJ(pt->allocator, struct apfl_expr_body); body->items = pre_body.items; body->len = pre_body.len; + body->cap = pre_body.len; body->refcount = 1; return body; } @@ -179,48 +186,69 @@ enum listbuilder_cmd { #define LISTBUILDER_NAME(n) listbuilder_##n #define LISTBUILDER_ITEM_NAME(n) listbuilder_item_##n -#define MKLISTBUILDER(name, listtype, itemtype, items_memb, len_memb) \ - struct LISTBUILDER_ITEM_NAME(name) { \ - bool has_item; \ - itemtype item; \ - }; \ - listtype \ - LISTBUILDER_NAME(name)( \ - struct parser_test *pt, \ - struct LISTBUILDER_ITEM_NAME(name) items[] \ - ) { \ - listtype body; \ - size_t cap = 0; \ +#define LISTBUILDER_FILL_LIST(pt, itemtype, items, list_items, list_len, cap) \ + apfl_resizable_init((void **)&list_items, &list_len, &cap); \ \ - apfl_resizable_init((void **)&body.items_memb, &body.len_memb, &cap); \ - \ - for (items++; items->has_item; items++) { \ - if (!apfl_resizable_append( \ - sizeof(itemtype), \ - (void **)&body.items_memb, \ - &body.len, \ - &cap, \ - &items->item, \ - 1 \ - )) { \ - test_fatalf(pt->t, "Failed appending"); \ - assert(false); \ - } \ + for (items++; items->has_item; items++) { \ + if (!apfl_resizable_append( \ + pt->allocator, \ + sizeof(itemtype), \ + (void **)&list_items, \ + &list_len, \ + &cap, \ + &items->item, \ + 1 \ + )) { \ + test_fatalf(pt->t, "Failed appending"); \ + assert(false); \ } \ - \ - return body; \ } \ +#define MKLISTBUILDER(name, listtype, itemtype, items_memb) \ + struct LISTBUILDER_ITEM_NAME(name) { \ + bool has_item; \ + itemtype item; \ + }; \ + listtype \ + LISTBUILDER_NAME(name)( \ + struct parser_test *pt, \ + struct LISTBUILDER_ITEM_NAME(name) items[] \ + ) { \ + listtype list; \ + size_t cap = 0; \ + \ + LISTBUILDER_FILL_LIST(pt, itemtype, items, list.items_memb, list.len, cap) \ + \ + return list; \ + } \ + +#define MKCAPLISTBUILDER(name, listtype, itemtype, items_memb) \ + struct LISTBUILDER_ITEM_NAME(name) { \ + bool has_item; \ + itemtype item; \ + }; \ + listtype \ + LISTBUILDER_NAME(name)( \ + struct parser_test *pt, \ + struct LISTBUILDER_ITEM_NAME(name) items[] \ + ) { \ + listtype list; \ + \ + LISTBUILDER_FILL_LIST(pt, itemtype, items, list.items_memb, list.len, list.cap) \ + \ + return list; \ + } \ + #define LIST_BEGIN(pt, builder) (LISTBUILDER_NAME(builder)(pt, (struct LISTBUILDER_ITEM_NAME(builder)[]) {{.has_item=false #define LIST_END }, {.has_item = false}})) #define LIST_ADD }, {.has_item = true, .item = -MKLISTBUILDER(list, struct apfl_expr_list, struct apfl_expr_list_item, items, len) -MKLISTBUILDER(body, struct test_pre_body, struct apfl_expr, items, len) -MKLISTBUILDER(dict, struct apfl_expr_dict, struct apfl_expr_dict_pair, items, len) -MKLISTBUILDER(complex_func, struct apfl_expr_complex_func, struct apfl_expr_subfunc, subfuncs, len) -MKLISTBUILDER(params, struct apfl_expr_params, struct apfl_expr_params_item, params, len) -MKLISTBUILDER(assignable_list, struct apfl_expr_assignable_list, struct apfl_expr_assignable_list_item, items, len) +MKLISTBUILDER(list, struct apfl_expr_list, struct apfl_expr_list_item, items) +MKLISTBUILDER(body, struct test_pre_body, struct apfl_expr, items) +MKCAPLISTBUILDER(dict, struct apfl_expr_dict, struct apfl_expr_dict_pair, items) +MKCAPLISTBUILDER(complex_func, struct apfl_expr_complex_func, struct apfl_expr_subfunc, subfuncs) +MKCAPLISTBUILDER(params, struct apfl_expr_params, struct apfl_expr_params_item, params) +MKLISTBUILDER(assignable_list, struct apfl_expr_assignable_list, struct apfl_expr_assignable_list_item, items) #define POS(l, c) (struct apfl_position) { .line = l, .col = c } diff --git a/src/resizable.c b/src/resizable.c index d688978..0dd0340 100644 --- a/src/resizable.c +++ b/src/resizable.c @@ -4,6 +4,7 @@ #include #include +#include "alloc.h" #include "resizable.h" void @@ -15,8 +16,14 @@ apfl_resizable_init(void **mem, size_t *len, size_t *cap) } bool -apfl_resizable_resize(size_t elem_size, void **mem, size_t *len, size_t *cap, size_t newlen) -{ +apfl_resizable_resize( + struct apfl_allocator allocator, + size_t elem_size, + void **mem, + size_t *len, + size_t *cap, + size_t newlen +) { // TODO: We're wasteful here by never actually shrinking the memory. if (newlen <= *len || newlen < *cap) { *len = newlen; @@ -25,7 +32,7 @@ apfl_resizable_resize(size_t elem_size, void **mem, size_t *len, size_t *cap, si assert(newlen >= *cap); - if (!apfl_resizable_ensure_cap(elem_size, mem, cap, newlen)) { + if (!apfl_resizable_ensure_cap(allocator, elem_size, mem, cap, newlen)) { return false; } @@ -34,8 +41,13 @@ apfl_resizable_resize(size_t elem_size, void **mem, size_t *len, size_t *cap, si } bool -apfl_resizable_ensure_cap(size_t elem_size, void **mem, size_t *cap, size_t want_cap) -{ +apfl_resizable_ensure_cap( + struct apfl_allocator allocator, + size_t elem_size, + void **mem, + size_t *cap, + size_t want_cap +) { if (want_cap <= *cap) { return true; } @@ -43,7 +55,7 @@ apfl_resizable_ensure_cap(size_t elem_size, void **mem, size_t *cap, size_t want // TODO: We currently simply grow the memory to have space for exactly // want_cap elements. It would probably be smarter to grow the memory // a bit larger to reduce calls to realloc. - void *newmem = realloc(*mem, want_cap * elem_size); + void *newmem = REALLOC_BYTES(allocator, *mem, *cap * elem_size, want_cap * elem_size); if (newmem == NULL) { return false; } @@ -54,12 +66,19 @@ apfl_resizable_ensure_cap(size_t elem_size, void **mem, size_t *cap, size_t want } bool -apfl_resizable_ensure_cap_for_more_elements(size_t elem_size, void **mem, size_t len, size_t *cap, size_t more_elements) -{ - return apfl_resizable_ensure_cap(elem_size, mem, cap, len + more_elements); // TODO: What if len + more_elements overflows? +apfl_resizable_ensure_cap_for_more_elements( + struct apfl_allocator allocator, + size_t elem_size, + void **mem, + size_t len, + size_t *cap, + size_t more_elements +) { + return apfl_resizable_ensure_cap(allocator, elem_size, mem, cap, len + more_elements); // TODO: What if len + more_elements overflows? } bool apfl_resizable_splice( + struct apfl_allocator allocator, size_t elem_size, void **mem, size_t *len, @@ -75,6 +94,7 @@ bool apfl_resizable_splice( if (other_len > cut_len) { if (!apfl_resizable_ensure_cap_for_more_elements( + allocator, elem_size, mem, *len, @@ -108,9 +128,10 @@ bool apfl_resizable_splice( } bool -apfl_resizable_append(size_t elem_size, void **mem, size_t *len, size_t *cap, const void *other_mem, size_t other_len) +apfl_resizable_append(struct apfl_allocator allocator, size_t elem_size, void **mem, size_t *len, size_t *cap, const void *other_mem, size_t other_len) { return apfl_resizable_splice( + allocator, elem_size, mem, len, diff --git a/src/resizable.h b/src/resizable.h index a7c8338..ed3a6fa 100644 --- a/src/resizable.h +++ b/src/resizable.h @@ -4,6 +4,8 @@ #include #include +#include "apfl.h" + #define APFL_RESIZABLE_TRAIT(T, N) \ T* N; \ size_t len; \ @@ -13,12 +15,33 @@ void apfl_resizable_init(void **mem, size_t *len, size_t *cap); -bool apfl_resizable_resize(size_t elem_size, void **mem, size_t *len, size_t *cap, size_t newlen); +bool apfl_resizable_resize( + struct apfl_allocator, + size_t elem_size, + void **mem, + size_t *len, + size_t *cap, + size_t newlen +); -bool apfl_resizable_ensure_cap(size_t elem_size, void **mem, size_t *cap, size_t want_cap); -bool apfl_resizable_ensure_cap_for_more_elements(size_t elem_size, void **mem, size_t len, size_t *cap, size_t more_elements); +bool apfl_resizable_ensure_cap( + struct apfl_allocator, + size_t elem_size, + void **mem, + size_t *cap, + size_t want_cap +); +bool apfl_resizable_ensure_cap_for_more_elements( + struct apfl_allocator, + size_t elem_size, + void **mem, + size_t len, + size_t *cap, + size_t more_elements +); bool apfl_resizable_splice( + struct apfl_allocator, size_t elem_size, void **mem, size_t *len, @@ -29,7 +52,15 @@ bool apfl_resizable_splice( size_t other_len ); -bool apfl_resizable_append(size_t elem_size, void **mem, size_t *len, size_t *cap, const void *other_mem, size_t other_len); +bool apfl_resizable_append( + struct apfl_allocator, + size_t elem_size, + void **mem, + size_t *len, + size_t *cap, + const void *other_mem, + size_t other_len +); #endif diff --git a/src/resizable_test.c b/src/resizable_test.c index f8d6115..5553244 100644 --- a/src/resizable_test.c +++ b/src/resizable_test.c @@ -2,6 +2,7 @@ #include "test.h" +#include "alloc.h" #include "resizable.h" struct splice_test_data { @@ -25,14 +26,17 @@ getlen(const int *data) static void run_splice_test(testctx t, struct splice_test_data st) { + struct apfl_allocator allocator = test_allocator(t); + size_t len = getlen(st.old); - int *mem = must_alloc(t, sizeof(int) * len); + int *mem = ALLOC_LIST(allocator, int, len); memcpy(mem, st.old, sizeof(int) * len); size_t cap = len; size_t other_len = getlen(st.other); if (!apfl_resizable_splice( + allocator, sizeof(int), (void **)&mem, &len, @@ -63,7 +67,7 @@ run_splice_test(testctx t, struct splice_test_data st) } } - free(mem); + FREE_LIST(allocator, mem, cap); } TEST(splice_empty, t) { diff --git a/src/source_readers.c b/src/source_readers.c index 0af337b..16195c4 100644 --- a/src/source_readers.c +++ b/src/source_readers.c @@ -2,9 +2,11 @@ #include #include "apfl.h" -#include "internal.h" + +#include "alloc.h" struct string_source_reader { + struct apfl_allocator allocator; struct apfl_string_view sv; size_t off; }; @@ -27,13 +29,14 @@ apfl_string_source_reader(void *opaque, char *buf, size_t *len, bool need) } void * -apfl_string_source_reader_new(struct apfl_string_view sv) +apfl_string_source_reader_new(struct apfl_allocator allocator, struct apfl_string_view sv) { - struct string_source_reader *ctx = ALLOC(struct string_source_reader); + struct string_source_reader *ctx = ALLOC_OBJ(allocator, struct string_source_reader); if (ctx == NULL) { return NULL; } + ctx->allocator = allocator; ctx->sv = sv; ctx->off = 0; @@ -43,5 +46,10 @@ apfl_string_source_reader_new(struct apfl_string_view sv) void apfl_string_source_reader_destroy(void *opaque) { - free(opaque); + struct string_source_reader *ctx = opaque; + if (ctx == NULL) { + return; + } + + FREE_OBJ(ctx->allocator, ctx); } diff --git a/src/strings.c b/src/strings.c index 9bbb288..2ff2a39 100644 --- a/src/strings.c +++ b/src/strings.c @@ -5,6 +5,7 @@ #include "apfl.h" +#include "alloc.h" #include "internal.h" #include "resizable.h" @@ -66,26 +67,28 @@ apfl_string_blank(void) return (struct apfl_string) { .bytes = NULL, .len = 0, + .cap = 0 }; } void -apfl_string_deinit(struct apfl_string *string) +apfl_string_deinit(struct apfl_allocator allocator, struct apfl_string *string) { - free(string->bytes); + FREE_BYTES(allocator, string->bytes, string->cap); *string = apfl_string_blank(); } bool -apfl_string_copy(struct apfl_string *dst, struct apfl_string_view src) +apfl_string_copy(struct apfl_allocator allocator, struct apfl_string *dst, struct apfl_string_view src) { - apfl_string_deinit(dst); - if ((dst->bytes = malloc(src.len)) == NULL) { + apfl_string_deinit(allocator, dst); + if ((dst->bytes = ALLOC_BYTES(allocator, src.len)) == NULL) { return false; } memcpy(dst->bytes, src.bytes, src.len); dst->len = src.len; + dst->cap = src.len; return true; } @@ -98,23 +101,31 @@ apfl_string_move(struct apfl_string *src) return out; } -void -apfl_string_builder_init(struct apfl_string_builder *builder) +static void +builder_reset(struct apfl_string_builder *builder) { apfl_resizable_init((void **)&(builder->bytes), &(builder->len), &(builder->cap)); } +void +apfl_string_builder_init(struct apfl_allocator allocator, struct apfl_string_builder *builder) +{ + builder->allocator = allocator; + builder_reset(builder); +} + void apfl_string_builder_deinit(struct apfl_string_builder *builder) { - free(builder->bytes); - apfl_string_builder_init(builder); + FREE_BYTES(builder->allocator, builder->bytes, builder->cap); + builder_reset(builder); } static bool append_bytes(struct apfl_string_builder *builder, const char *bytes, size_t len) { return apfl_resizable_append( + builder->allocator, sizeof(char), APFL_RESIZABLE_ARGS(*builder, bytes), bytes, @@ -141,8 +152,9 @@ apfl_string_builder_move_string(struct apfl_string_builder *builder) str.bytes = builder->bytes; str.len = builder->len; + str.cap = builder->cap; - apfl_string_builder_init(builder); + builder_reset(builder); return str; } @@ -158,16 +170,16 @@ apfl_string_view_from_refcounted_string(apfl_refcounted_string rcstring) } apfl_refcounted_string -apfl_string_copy_into_new_refcounted(struct apfl_string_view sv) +apfl_string_copy_into_new_refcounted(struct apfl_allocator allocator, struct apfl_string_view sv) { struct apfl_string str = apfl_string_blank(); - if (!apfl_string_copy(&str, sv)) { + if (!apfl_string_copy(allocator, &str, sv)) { return NULL; } - apfl_refcounted_string rcstring = apfl_string_move_into_new_refcounted(&str); + apfl_refcounted_string rcstring = apfl_string_move_into_new_refcounted(allocator, &str); if (rcstring == NULL) { - apfl_string_deinit(&str); + apfl_string_deinit(allocator, &str); return NULL; } @@ -175,9 +187,12 @@ apfl_string_copy_into_new_refcounted(struct apfl_string_view sv) } apfl_refcounted_string -apfl_string_move_into_new_refcounted(struct apfl_string *src) +apfl_string_move_into_new_refcounted(struct apfl_allocator allocator, struct apfl_string *src) { - apfl_refcounted_string dst = ALLOC(struct apfl_refcounted_string_data); + apfl_refcounted_string dst = ALLOC_OBJ( + allocator, + struct apfl_refcounted_string_data + ); if (dst == NULL) { // TODO: Or should we free src here? return NULL; @@ -201,17 +216,17 @@ apfl_refcounted_string_incref(apfl_refcounted_string rcstring) } void -apfl_refcounted_string_unref(apfl_refcounted_string rcstring) +apfl_refcounted_string_unref(struct apfl_allocator allocator, apfl_refcounted_string rcstring) { if (rcstring != NULL && apfl_refcount_dec(&rcstring->refcount)) { - apfl_string_deinit(&rcstring->string); - free(rcstring); + apfl_string_deinit(allocator, &rcstring->string); + FREE_OBJ(allocator, rcstring); } } void -apfl_refcounted_string_unref_ptr(apfl_refcounted_string *rcstring_ptr) +apfl_refcounted_string_unref_ptr(struct apfl_allocator allocator, apfl_refcounted_string *rcstring_ptr) { - apfl_refcounted_string_unref(*rcstring_ptr); + apfl_refcounted_string_unref(allocator, *rcstring_ptr); *rcstring_ptr = NULL; } diff --git a/src/test.h b/src/test.h index 76038f7..c290915 100644 --- a/src/test.h +++ b/src/test.h @@ -13,10 +13,13 @@ extern "C" { #include #include +#include "apfl.h" + struct testctx_struct { jmp_buf *fataljmp; const char *name; const char *prefix; + struct apfl_allocator allocator_unverified; bool ok; }; @@ -71,6 +74,30 @@ test_fatalf(testctx t, const char* fmt, ...) { test_fatal(t); } +static void * +test_must_allocator_cb(void *opaque, void *oldptr, size_t oldsize, size_t newsize) +{ + (void)oldsize; + testctx t = opaque; + + if (newsize == 0) { + free(oldptr); + return NULL; + } else { + void *mem = realloc(oldptr, newsize); + if (mem == NULL) { + test_fatalf(t, "Failed to allocate memory"); + } + return mem; + } +} + +struct apfl_allocator +test_allocator(testctx t) +{ + return apfl_allocator_wrap_verifying(&t->allocator_unverified); +} + #define TESTPREFIXSIZE 1024 bool @@ -87,6 +114,11 @@ test_run_test(struct testdef test) return false; } + t->allocator_unverified = (struct apfl_allocator) { + .opaque = t, + .alloc = test_must_allocator_cb, + }; + snprintf(prefix, TESTPREFIXSIZE, "%s: ", test.name); if(setjmp(*here) == 0) { diff --git a/src/token.c b/src/token.c index e95aeb8..b3aee14 100644 --- a/src/token.c +++ b/src/token.c @@ -29,10 +29,10 @@ has_numeric_data(enum apfl_token_type type) } void -apfl_token_deinit(struct apfl_token *token) +apfl_token_deinit(struct apfl_allocator allocator, struct apfl_token *token) { if (has_text_data(token->type)) { - apfl_refcounted_string_unref_ptr(&token->text); + apfl_refcounted_string_unref_ptr(allocator, &token->text); } } diff --git a/src/tokenizer.c b/src/tokenizer.c index 5639596..1d32b29 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -8,11 +8,14 @@ #include "apfl.h" +#include "alloc.h" + #define BUFSIZE 4096 typedef int buf_offset; static_assert(INT_MAX >= BUFSIZE, "BUFSIZE is too large for type buf_offset"); struct apfl_tokenizer { + struct apfl_allocator allocator; apfl_source_reader_cb source_reader; void *source_reader_context; char *buf; @@ -39,18 +42,19 @@ struct apfl_tokenizer { }; apfl_tokenizer_ptr -apfl_tokenizer_new(apfl_source_reader_cb source_reader, void *context) +apfl_tokenizer_new(struct apfl_allocator allocator, apfl_source_reader_cb source_reader, void *context) { - apfl_tokenizer_ptr tokenizer = malloc(sizeof(struct apfl_tokenizer)); + apfl_tokenizer_ptr tokenizer = ALLOC_OBJ(allocator, struct apfl_tokenizer); if (tokenizer == NULL) { return NULL; } + tokenizer->allocator = allocator; tokenizer->source_reader = source_reader; tokenizer->source_reader_context = context; - if ((tokenizer->buf = malloc(BUFSIZE)) == NULL) { - free(tokenizer); + if ((tokenizer->buf = ALLOC_BYTES(allocator, BUFSIZE)) == NULL) { + FREE_OBJ(allocator, tokenizer); return NULL; } @@ -75,8 +79,8 @@ apfl_tokenizer_destroy(apfl_tokenizer_ptr tokenizer) return; } - free(tokenizer->buf); - free(tokenizer); + FREE_BYTES(tokenizer->allocator, tokenizer->buf, BUFSIZE); + FREE_OBJ(tokenizer->allocator, tokenizer); } struct apfl_token @@ -260,9 +264,12 @@ static apfl_refcounted_string rcstring_from_string_builder(apfl_tokenizer_ptr tokenizer, struct apfl_string_builder *builder) { struct apfl_string string = apfl_string_builder_move_string(builder); - apfl_refcounted_string rcstring = apfl_string_move_into_new_refcounted(&string); + apfl_refcounted_string rcstring = apfl_string_move_into_new_refcounted( + tokenizer->allocator, + &string + ); if (rcstring == NULL) { - apfl_string_deinit(&string); + apfl_string_deinit(tokenizer->allocator, &string); tokenizer->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); } return rcstring; @@ -277,7 +284,7 @@ comment(apfl_tokenizer_ptr tokenizer) struct apfl_position last_pos; struct apfl_string_builder text; - apfl_string_builder_init(&text); + apfl_string_builder_init(tokenizer->allocator, &text); for (;;) { apfl_refcounted_string rcstring; @@ -621,7 +628,7 @@ static enum apfl_parse_result string(apfl_tokenizer_ptr tokenizer) { struct apfl_string_builder text; - apfl_string_builder_init(&text); + apfl_string_builder_init(tokenizer->allocator, &text); enum apfl_parse_result out = inner_string(tokenizer, &text); @@ -772,7 +779,7 @@ static enum apfl_parse_result maybe_name(apfl_tokenizer_ptr tokenizer, bool need, char first_byte) { struct apfl_string_builder text; - apfl_string_builder_init(&text); + apfl_string_builder_init(tokenizer->allocator, &text); enum apfl_parse_result out = maybe_name_inner(tokenizer, need, first_byte, &text); diff --git a/src/tokenizer_test.c b/src/tokenizer_test.c index 7fb9786..be40829 100644 --- a/src/tokenizer_test.c +++ b/src/tokenizer_test.c @@ -6,6 +6,7 @@ struct tokenizer_test { testctx t; + struct apfl_allocator allocator; apfl_tokenizer_ptr tokenizer; void *ctx; }; @@ -13,12 +14,14 @@ struct tokenizer_test { static struct tokenizer_test * new_tokenizer_test_sv(testctx t, struct apfl_string_view text) { - void *ctx = apfl_string_source_reader_new(text); + struct apfl_allocator allocator = apfl_allocator_default(); + + void *ctx = apfl_string_source_reader_new(allocator, text); if (ctx == NULL) { test_fatalf(t, "Failed to initialize the source reader"); } - apfl_tokenizer_ptr tokenizer = apfl_tokenizer_new(apfl_string_source_reader, ctx); + apfl_tokenizer_ptr tokenizer = apfl_tokenizer_new(allocator, apfl_string_source_reader, ctx); if (tokenizer == NULL) { test_fatalf(t, "Failed to initialize the tokenizer"); } @@ -27,6 +30,7 @@ new_tokenizer_test_sv(testctx t, struct apfl_string_view text) *tt = (struct tokenizer_test) { .t = t, + .allocator = allocator, .tokenizer = tokenizer, .ctx = ctx, }; @@ -90,7 +94,7 @@ expect_token(struct tokenizer_test *tt, int line, int col, enum apfl_token_type apfl_token_type_name(tok->type), apfl_token_type_name(type) ); - apfl_token_deinit(tok); + apfl_token_deinit(tt->allocator, tok); return false; } @@ -107,7 +111,7 @@ expect_simple_token(struct tokenizer_test *tt, int line, int col, enum apfl_toke { struct apfl_token tok; if (expect_token(tt, line, col, type, &tok)) { - apfl_token_deinit(&tok); + apfl_token_deinit(tt->allocator, &tok); } } @@ -120,7 +124,7 @@ expect_text_token(struct tokenizer_test *tt, int line, int col, enum apfl_token_ struct apfl_string_view sv = apfl_string_view_from(tok.text); test_failf(tt->t, "Token has wrong content. have=\"" APFL_STR_FMT "\", want=\"%s\"", APFL_STR_FMT_ARGS(sv), text); } - apfl_token_deinit(&tok); + apfl_token_deinit(tt->allocator, &tok); } } @@ -133,7 +137,7 @@ expect_text_token_sv(struct tokenizer_test *tt, int line, int col, enum apfl_tok struct apfl_string_view sv = apfl_string_view_from(tok.text); test_failf(tt->t, "Token has wrong content. have=\"" APFL_STR_FMT "\", want=\"%s\"", APFL_STR_FMT_ARGS(sv), text); } - apfl_token_deinit(&tok); + apfl_token_deinit(tt->allocator, &tok); } } @@ -145,7 +149,7 @@ expect_number_token(struct tokenizer_test *tt, int line, int col, enum apfl_toke if (tok.number != num) { test_failf(tt->t, "Token has wrong content. have=%f, want=%f", tok.number, num); } - apfl_token_deinit(&tok); + apfl_token_deinit(tt->allocator, &tok); } } @@ -158,7 +162,7 @@ expect_error(struct tokenizer_test *tt, enum apfl_error_type want) case APFL_PARSE_OK: tok = apfl_tokenizer_get_token(tt->tokenizer); test_failf(tt->t, "Expected error, got token of type %s instead", apfl_token_type_name(tok.type)); - apfl_token_deinit(&tok); + apfl_token_deinit(tt->allocator, &tok); return; case APFL_PARSE_EOF: test_fatalf(tt->t, "Got an EOF instead of a token"); diff --git a/src/value.c b/src/value.c index 4262676..81f16cc 100644 --- a/src/value.c +++ b/src/value.c @@ -1,6 +1,8 @@ #include #include "apfl.h" + +#include "alloc.h" #include "internal.h" #include "resizable.h" #include "hashmap.h" @@ -10,6 +12,7 @@ struct apfl_list_data { unsigned refcount; struct apfl_value *items; size_t len; + size_t cap; }; struct apfl_dict_data { @@ -36,18 +39,18 @@ apfl_list_len(apfl_list list) } void -apfl_list_unref(apfl_list list) +apfl_list_unref(struct apfl_allocator allocator, apfl_list list) { if (list == NULL || !apfl_refcount_dec(&list->refcount)) { return; } for (size_t i = 0; i < list->len; i++) { - apfl_value_deinit(&list->items[i]); + apfl_value_deinit(allocator, &list->items[i]); } - free(list->items); - free(list); + FREE_LIST(allocator, list->items, list->cap); + FREE_OBJ(allocator, list); } static bool @@ -67,8 +70,8 @@ dict_calc_hash(void *opaque, const void *key) static void dict_destroy_key_or_value(void *opaque, void *kv) { - (void)opaque; - apfl_value_deinit(kv); + struct apfl_allocator *allocator = opaque; + apfl_value_deinit(*allocator, kv); } static void @@ -82,25 +85,44 @@ dict_copy_key_or_value(void *opaque, void *_dest, void *_src) *dest = apfl_value_incref(*src); } -static const struct apfl_hashmap_callbacks dict_hashmap_callbacks = { - .opaque = NULL, - .keys_eq = dict_keys_eq, - .calc_hash = dict_calc_hash, - .destroy_key = dict_destroy_key_or_value, - .destroy_value = dict_destroy_key_or_value, - .copy_key = dict_copy_key_or_value, - .copy_value = dict_copy_key_or_value, -}; +static void +dict_on_deinit(void *opaque) +{ + struct apfl_allocator *allocator = opaque; + FREE_OBJ(*allocator, allocator); +} static bool -init_hashmap_for_dict(struct apfl_hashmap *map) +init_hashmap_for_dict(struct apfl_allocator allocator, struct apfl_hashmap *map) { - return apfl_hashmap_init( + struct apfl_allocator *allocator_ptr = ALLOC_OBJ(allocator, struct apfl_allocator); + if (allocator_ptr == NULL) { + return false; + } + *allocator_ptr = allocator; + + bool ok = apfl_hashmap_init( map, - dict_hashmap_callbacks, + allocator, + (struct apfl_hashmap_callbacks) { + .opaque = allocator_ptr, + .keys_eq = dict_keys_eq, + .calc_hash = dict_calc_hash, + .destroy_key = dict_destroy_key_or_value, + .destroy_value = dict_destroy_key_or_value, + .copy_key = dict_copy_key_or_value, + .copy_value = dict_copy_key_or_value, + .on_deinit = dict_on_deinit, + }, sizeof(struct apfl_value), sizeof(struct apfl_value) ); + + if (!ok) { + FREE_OBJ(allocator, allocator_ptr); + } + + return ok; } apfl_dict @@ -114,14 +136,14 @@ apfl_dict_incref(apfl_dict dict) } void -apfl_dict_unref(apfl_dict dict) +apfl_dict_unref(struct apfl_allocator allocator, apfl_dict dict) { if (dict == NULL || !apfl_refcount_dec(&dict->refcount)) { return; } apfl_hashmap_deinit(&dict->map); - free(dict); + FREE_OBJ(allocator, dict); } static void @@ -166,15 +188,14 @@ print(unsigned indent, FILE *out, struct apfl_value value, bool skip_first_inden apfl_print_indented(first_indent, out, "[\n"); for (; !apfl_hashmap_cursor_is_end(cur); apfl_hashmap_cursor_next(&cur)) { - struct apfl_value k, v; - apfl_hashmap_cursor_get_key(cur, &k); - apfl_hashmap_cursor_get_value(cur, &v); + struct apfl_value *k; + struct apfl_value *v; + apfl_hashmap_cursor_peek_key(cur, (void **)&k); + apfl_hashmap_cursor_peek_value(cur, (void **)&v); - print(indent+1, out, k, false); - apfl_value_deinit(&k); + print(indent+1, out, *k, false); fprintf(out, " -> "); - print(indent+1, out, v, true); - apfl_value_deinit(&v); + print(indent+1, out, *v, true); fprintf(out, "\n"); } apfl_print_indented(indent, out, "]"); @@ -253,23 +274,18 @@ dict_eq(const apfl_dict a, const apfl_dict b) struct apfl_hashmap_cursor cur = apfl_hashmap_get_cursor(&a->map); size_t total = 0; for (; !apfl_hashmap_cursor_is_end(cur); apfl_hashmap_cursor_next(&cur)) { - struct apfl_value key; - struct apfl_value val; + struct apfl_value *key; + struct apfl_value *val; - apfl_hashmap_cursor_get_key(cur, &key); - apfl_hashmap_cursor_get_value(cur, &val); + apfl_hashmap_cursor_peek_key(cur, (void **)&key); + apfl_hashmap_cursor_peek_value(cur, (void **)&val); - struct apfl_value other_val; - if (!apfl_hashmap_get(&b->map, &key, &other_val)) { - apfl_value_deinit(&key); - apfl_value_deinit(&val); + struct apfl_value *other_val; + if (!apfl_hashmap_peek(&b->map, &key, (void **)&other_val)) { return false; } - bool eq = apfl_value_eq(val, other_val); - apfl_value_deinit(&key); - apfl_value_deinit(&val); - apfl_value_deinit(&other_val); + bool eq = apfl_value_eq(*val, *other_val); if (!eq) { return false; @@ -307,19 +323,21 @@ apfl_value_eq(const struct apfl_value a, const struct apfl_value b) } struct apfl_editable_list_data { + struct apfl_allocator allocator; struct apfl_value *items; size_t len; size_t cap; }; apfl_editable_list -apfl_editable_list_new(void) +apfl_editable_list_new(struct apfl_allocator allocator) { - apfl_editable_list elist = ALLOC(struct apfl_editable_list_data); + apfl_editable_list elist = ALLOC_OBJ(allocator, struct apfl_editable_list_data); if (elist == NULL) { return NULL; } + elist->allocator = allocator; elist->items = NULL; elist->len = 0; elist->cap = 0; @@ -331,6 +349,7 @@ static bool editable_list_append_values(apfl_editable_list elist, struct apfl_value *values, size_t len) { if (!apfl_resizable_ensure_cap_for_more_elements( + elist->allocator, sizeof(struct apfl_value), (void **)&elist->items, elist->len, @@ -352,7 +371,7 @@ bool apfl_editable_list_append(apfl_editable_list elist, struct apfl_value value) { bool ok = editable_list_append_values(elist, &value, 1); - apfl_value_deinit(&value); + apfl_value_deinit(elist->allocator, &value); return ok; } @@ -360,7 +379,7 @@ bool apfl_editable_list_append_list(apfl_editable_list elist, apfl_list list) { bool ok = editable_list_append_values(elist, list->items, list->len); - apfl_list_unref(list); + apfl_list_unref(elist->allocator, list); return ok; } @@ -373,18 +392,18 @@ apfl_editable_list_destroy(apfl_editable_list elist) if (elist->items != NULL) { for (size_t i = 0; i < elist->len; i++) { - apfl_value_deinit(&elist->items[i]); + apfl_value_deinit(elist->allocator, &elist->items[i]); } - free(elist->items); + FREE_LIST(elist->allocator, elist->items, elist->cap); } - free(elist); + FREE_OBJ(elist->allocator, elist); } apfl_list apfl_editable_list_finalize(apfl_editable_list elist) { - apfl_list list = ALLOC(struct apfl_list_data); + apfl_list list = ALLOC_OBJ(elist->allocator, struct apfl_list_data); if (list == NULL) { apfl_editable_list_destroy(elist); return NULL; @@ -393,26 +412,29 @@ apfl_editable_list_finalize(apfl_editable_list elist) list->refcount = 1; list->items = elist->items; // TODO: Maybe shrink memory with realloc? list->len = elist->len; + list->cap = elist->cap; - free(elist); + FREE_OBJ(elist->allocator, elist); return list; } struct apfl_editable_dict_data { + struct apfl_allocator allocator; struct apfl_hashmap map; }; apfl_editable_dict -apfl_editable_dict_new(void) +apfl_editable_dict_new(struct apfl_allocator allocator) { - apfl_editable_dict ed = ALLOC(struct apfl_editable_dict_data); + apfl_editable_dict ed = ALLOC_OBJ(allocator, struct apfl_editable_dict_data); if (ed == NULL) { return NULL; } - if (!init_hashmap_for_dict(&ed->map)) { - free(ed); + ed->allocator = allocator; + if (!init_hashmap_for_dict(allocator, &ed->map)) { + FREE_OBJ(allocator, ed); return NULL; } @@ -420,31 +442,32 @@ apfl_editable_dict_new(void) } apfl_editable_dict -apfl_editable_dict_new_from_dict(apfl_dict dict) +apfl_editable_dict_new_from_dict(struct apfl_allocator allocator, apfl_dict dict) { if (dict->refcount == 0) { return NULL; } - apfl_editable_dict ed = ALLOC(struct apfl_editable_dict_data); + apfl_editable_dict ed = ALLOC_OBJ(allocator, struct apfl_editable_dict_data); if (ed == NULL) { - apfl_dict_unref(dict); + apfl_dict_unref(allocator, dict); return NULL; } + ed->allocator = allocator; if (dict->refcount == 1) { // We're the only one having a reference to the dict. We can directly use it's hashmap! ed->map = apfl_hashmap_move(&dict->map); } else { // There are other places referencing the dict, we need to create a shallow copy first. if (!apfl_hashmap_copy(&ed->map, dict->map)) { - free(ed); - apfl_dict_unref(dict); + FREE_OBJ(allocator, ed); + apfl_dict_unref(allocator, dict); return NULL; } } - apfl_dict_unref(dict); + apfl_dict_unref(allocator, dict); return ed; } @@ -456,7 +479,7 @@ apfl_editable_dict_get( struct apfl_value *out ) { bool ok = apfl_hashmap_get(&ed->map, &key, out); - apfl_value_deinit(&key); + apfl_value_deinit(ed->allocator, &key); return ok; } @@ -468,8 +491,8 @@ apfl_editable_dict_set(apfl_editable_dict ed, struct apfl_value key, struct apfl } bool ok = apfl_hashmap_set(&ed->map, &key, &value); - apfl_value_deinit(&key); - apfl_value_deinit(&value); + apfl_value_deinit(ed->allocator, &key); + apfl_value_deinit(ed->allocator, &value); return ok; } @@ -481,7 +504,7 @@ apfl_editable_dict_delete(apfl_editable_dict ed, struct apfl_value key) } apfl_hashmap_delete(&ed->map, &key); - apfl_value_deinit(&key); + apfl_value_deinit(ed->allocator, &key); } apfl_dict @@ -491,7 +514,7 @@ apfl_editable_dict_finalize(apfl_editable_dict ed) return NULL; } - apfl_dict dict = ALLOC(struct apfl_dict_data); + apfl_dict dict = ALLOC_OBJ(ed->allocator, struct apfl_dict_data); if (dict == NULL) { apfl_editable_dict_destroy(ed); return NULL; @@ -500,7 +523,7 @@ apfl_editable_dict_finalize(apfl_editable_dict ed) dict->refcount = 1; dict->map = apfl_hashmap_move(&ed->map); - free(ed); + FREE_OBJ(ed->allocator, ed); return dict; } @@ -513,11 +536,11 @@ apfl_editable_dict_destroy(apfl_editable_dict ed) } apfl_hashmap_deinit(&ed->map); - free(ed); + FREE_OBJ(ed->allocator, ed); } void -apfl_value_deinit(struct apfl_value *value) +apfl_value_deinit(struct apfl_allocator allocator, struct apfl_value *value) { switch (value->type) { case APFL_VALUE_NIL: @@ -525,15 +548,15 @@ apfl_value_deinit(struct apfl_value *value) case APFL_VALUE_NUMBER: goto ok; case APFL_VALUE_STRING: - apfl_refcounted_string_unref(value->string); + apfl_refcounted_string_unref(allocator, value->string); value->string = NULL; goto ok; case APFL_VALUE_LIST: - apfl_list_unref(value->list); + apfl_list_unref(allocator, value->list); value->list = NULL; goto ok; case APFL_VALUE_DICT: - apfl_dict_unref(value->dict); + apfl_dict_unref(allocator, value->dict); value->dict = NULL; goto ok; } @@ -545,29 +568,29 @@ ok: } bool -apfl_list_get_item(apfl_list list, size_t index, struct apfl_value *out) +apfl_list_get_item(struct apfl_allocator allocator, apfl_list list, size_t index, struct apfl_value *out) { if (index >= list->len) { - apfl_list_unref(list); + apfl_list_unref(allocator, list); return false; } *out = apfl_value_incref(list->items[index]); - apfl_list_unref(list); + apfl_list_unref(allocator, list); return true; } bool -apfl_dict_get_item(apfl_dict dict, struct apfl_value key, struct apfl_value *out) +apfl_dict_get_item(struct apfl_allocator allocator, apfl_dict dict, struct apfl_value key, struct apfl_value *out) { bool ok = apfl_hashmap_get(&dict->map, &key, out); - apfl_dict_unref(dict); - apfl_value_deinit(&key); + apfl_dict_unref(allocator, dict); + apfl_value_deinit(allocator, &key); return ok; } static enum apfl_value_get_item_result -get_item(/*borrowed*/ struct apfl_value container, /*borrowed*/ struct apfl_value key, struct apfl_value *out) +get_item(struct apfl_allocator allocator, /*borrowed*/ struct apfl_value container, /*borrowed*/ struct apfl_value key, struct apfl_value *out) { if (container.type == APFL_VALUE_LIST) { if (key.type != APFL_VALUE_NUMBER) { @@ -575,6 +598,7 @@ get_item(/*borrowed*/ struct apfl_value container, /*borrowed*/ struct apfl_valu } return apfl_list_get_item( + allocator, apfl_list_incref(container.list), (size_t)key.number, out @@ -583,6 +607,7 @@ get_item(/*borrowed*/ struct apfl_value container, /*borrowed*/ struct apfl_valu : APFL_VALUE_GET_ITEM_KEY_DOESNT_EXIST; } else if (container.type == APFL_VALUE_DICT) { return apfl_dict_get_item( + allocator, apfl_dict_incref(container.dict), apfl_value_incref(key), out @@ -595,11 +620,11 @@ get_item(/*borrowed*/ struct apfl_value container, /*borrowed*/ struct apfl_valu } enum apfl_value_get_item_result -apfl_value_get_item(struct apfl_value container, struct apfl_value key, struct apfl_value *out) +apfl_value_get_item(struct apfl_allocator allocator, struct apfl_value container, struct apfl_value key, struct apfl_value *out) { - enum apfl_value_get_item_result result = get_item(container, key, out); - apfl_value_deinit(&container); - apfl_value_deinit(&key); + enum apfl_value_get_item_result result = get_item(allocator, container, key, out); + apfl_value_deinit(allocator, &container); + apfl_value_deinit(allocator, &key); return result; } diff --git a/src/value.h b/src/value.h index e3c0740..c11ad5d 100644 --- a/src/value.h +++ b/src/value.h @@ -42,7 +42,7 @@ bool apfl_value_eq(const struct apfl_value, const struct apfl_value); struct apfl_value apfl_value_move(struct apfl_value *src); struct apfl_value apfl_value_incref(struct apfl_value); void apfl_value_print(struct apfl_value, FILE *); -void apfl_value_deinit(struct apfl_value *); +void apfl_value_deinit(struct apfl_allocator, struct apfl_value *); enum apfl_value_get_item_result { APFL_VALUE_GET_ITEM_OK, @@ -51,21 +51,21 @@ enum apfl_value_get_item_result { APFL_VALUE_GET_ITEM_WRONG_KEY_TYPE, }; -enum apfl_value_get_item_result apfl_value_get_item(struct apfl_value container, struct apfl_value key, struct apfl_value *out); +enum apfl_value_get_item_result apfl_value_get_item(struct apfl_allocator allocator, struct apfl_value container, struct apfl_value key, struct apfl_value *out); apfl_list apfl_list_incref(apfl_list); size_t apfl_list_len(apfl_list); -bool apfl_list_get_item(apfl_list, size_t index, struct apfl_value *out); -void apfl_list_unref(apfl_list); +bool apfl_list_get_item(struct apfl_allocator, apfl_list, size_t index, struct apfl_value *out); +void apfl_list_unref(struct apfl_allocator, apfl_list); apfl_dict apfl_dict_incref(apfl_dict); -bool apfl_dict_get_item(apfl_dict, struct apfl_value key, struct apfl_value *out); -void apfl_dict_unref(apfl_dict); +bool apfl_dict_get_item(struct apfl_allocator, apfl_dict, struct apfl_value key, struct apfl_value *out); +void apfl_dict_unref(struct apfl_allocator, apfl_dict); struct apfl_editable_list_data; typedef struct apfl_editable_list_data *apfl_editable_list; -apfl_editable_list apfl_editable_list_new(void); +apfl_editable_list apfl_editable_list_new(struct apfl_allocator); bool apfl_editable_list_append(apfl_editable_list, struct apfl_value); bool apfl_editable_list_append_list(apfl_editable_list, apfl_list); void apfl_editable_list_destroy(apfl_editable_list); @@ -79,8 +79,8 @@ apfl_list apfl_editable_list_finalize(apfl_editable_list); struct apfl_editable_dict_data; typedef struct apfl_editable_dict_data *apfl_editable_dict; -apfl_editable_dict apfl_editable_dict_new(void); -apfl_editable_dict apfl_editable_dict_new_from_dict(apfl_dict); +apfl_editable_dict apfl_editable_dict_new(struct apfl_allocator); +apfl_editable_dict apfl_editable_dict_new_from_dict(struct apfl_allocator, apfl_dict); bool apfl_editable_dict_get(apfl_editable_dict, struct apfl_value key, struct apfl_value *out); bool apfl_editable_dict_set(apfl_editable_dict, struct apfl_value key, struct apfl_value value); void apfl_editable_dict_delete(apfl_editable_dict, struct apfl_value key);