From b29219af250c66aa54d72b1d2851b2ec686bfa7d Mon Sep 17 00:00:00 2001 From: Laria Carolin Chabowski Date: Sun, 2 Jan 2022 17:55:05 +0100 Subject: [PATCH] Make strings in apfl_value refcounted This avoids copying the string every time we pass it around. Not too important right now, but will become important onve we're able to evaluate more complex expressions. --- src/apfl.h | 27 +++++++++++++++++++++------ src/eval.c | 18 ++++++++++-------- src/internal.c | 9 +++++++++ src/internal.h | 4 ++++ src/strings.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/value.c | 27 +++++++-------------------- 6 files changed, 96 insertions(+), 34 deletions(-) diff --git a/src/apfl.h b/src/apfl.h index b279fa8..18ff5fb 100644 --- a/src/apfl.h +++ b/src/apfl.h @@ -30,6 +30,11 @@ struct apfl_string { size_t len; }; +struct apfl_refcounted_string { + unsigned refcount; + struct apfl_string string; +}; + #define APFL_STR_FMT "%.*s" #define APFL_STR_FMT_ARGS(s) (int)(s).len,(s).bytes @@ -37,12 +42,14 @@ struct apfl_string_view apfl_string_view_from_view(struct apfl_string_view); struct apfl_string_view apfl_string_view_from_cstr(char *); struct apfl_string_view apfl_string_view_from_const_cstr(const char *); struct apfl_string_view apfl_string_view_from_string(struct apfl_string); +struct apfl_string_view apfl_string_view_from_refcounted_string(struct apfl_refcounted_string *); -#define apfl_string_view_from(s) _Generic((s), \ - struct apfl_string: apfl_string_view_from_string, \ - struct apfl_string_view: apfl_string_view_from_view, \ - char *: apfl_string_view_from_cstr, \ - const char *: apfl_string_view_from_const_cstr \ +#define apfl_string_view_from(s) _Generic((s), \ + struct apfl_string: apfl_string_view_from_string, \ + struct apfl_string_view: apfl_string_view_from_view, \ + struct apfl_refcounted_string *: apfl_string_view_from_refcounted_string, \ + char *: apfl_string_view_from_cstr, \ + const char *: apfl_string_view_from_const_cstr \ )(s) int apfl_string_view_cmp(struct apfl_string_view, struct apfl_string_view); @@ -75,6 +82,14 @@ 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)))) +struct apfl_refcounted_string *apfl_string_move_into_new_refcounted(struct apfl_string *); +struct apfl_refcounted_string *apfl_refcounted_string_copy(struct 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(struct apfl_refcounted_string *); + // Tokens enum apfl_token_type { @@ -503,7 +518,7 @@ struct apfl_value { union { bool boolean; apfl_number number; - struct apfl_string string; + struct apfl_refcounted_string *string; struct apfl_list *list; }; }; diff --git a/src/eval.c b/src/eval.c index a71cc88..cdf3318 100644 --- a/src/eval.c +++ b/src/eval.c @@ -28,11 +28,11 @@ apfl_ctx_destroy(apfl_ctx ctx) } static struct apfl_value -evaluate_constant(struct apfl_expr_const constant) +evaluate_constant(struct apfl_expr_const *constant) { - struct apfl_string str = apfl_string_blank(); + struct apfl_refcounted_string *rcstring; - switch (constant.type) { + switch (constant->type) { case APFL_EXPR_CONST_NIL: return (struct apfl_value) { .type = APFL_VALUE_NIL, @@ -40,18 +40,20 @@ evaluate_constant(struct apfl_expr_const constant) case APFL_EXPR_CONST_BOOLEAN: return (struct apfl_value) { .type = APFL_VALUE_BOOLEAN, - .boolean = constant.boolean, + .boolean = constant->boolean, }; case APFL_EXPR_CONST_STRING: - assert(apfl_string_copy(&str, apfl_string_view_from(constant.string))); // TODO: This can fail! + rcstring = apfl_string_move_into_new_refcounted(&constant->string); + assert(rcstring != NULL); // TODO: This can fail! + return (struct apfl_value) { .type = APFL_VALUE_STRING, - .string = str, + .string = rcstring, }; case APFL_EXPR_CONST_NUMBER: return (struct apfl_value) { .type = APFL_VALUE_NUMBER, - .number = constant.number, + .number = constant->number, }; } @@ -161,7 +163,7 @@ evaluate(apfl_ctx ctx, struct apfl_expr *expr) case APFL_EXPR_CONSTANT: return (struct apfl_result) { .type = APFL_RESULT_OK, - .value = evaluate_constant(expr->constant), + .value = evaluate_constant(&expr->constant), }; case APFL_EXPR_LIST: return evaluate_list(ctx, &expr->list); diff --git a/src/internal.c b/src/internal.c index 043c9ef..67d78db 100644 --- a/src/internal.c +++ b/src/internal.c @@ -15,3 +15,12 @@ apfl_print_indented(unsigned indent, FILE *f, const char* fmt, ...) vfprintf(f, fmt, ap); va_end(ap); } + +bool +apfl_refcount_dec(unsigned *rc) +{ + if (rc == 0) { + return false; + } + return --(*rc) == 0; +} diff --git a/src/internal.h b/src/internal.h index aa3cb9b..4381949 100644 --- a/src/internal.h +++ b/src/internal.h @@ -48,6 +48,10 @@ void apfl_print_indented(unsigned indent, FILE *, const char* fmt, ...); struct apfl_list *apfl_list_new(void); void apfl_list_deinit(struct apfl_list *); +/* Decrease a refererence count. Returns true, if the object should be freed. + */ +bool apfl_refcount_dec(unsigned *); + #ifdef __cplusplus } #endif diff --git a/src/strings.c b/src/strings.c index b2228c7..44e93dd 100644 --- a/src/strings.c +++ b/src/strings.c @@ -5,6 +5,7 @@ #include "apfl.h" +#include "internal.h" #include "resizable.h" struct apfl_string_view @@ -140,3 +141,47 @@ apfl_string_builder_move_string(struct apfl_string_builder *builder) return str; } + +struct +apfl_string_view apfl_string_view_from_refcounted_string(struct apfl_refcounted_string *rcstring) +{ + return apfl_string_view_from(rcstring->string); +} + +struct apfl_refcounted_string * +apfl_string_move_into_new_refcounted(struct apfl_string *src) +{ + struct apfl_refcounted_string *dst = ALLOC(struct apfl_refcounted_string); + if (dst == NULL) { + return NULL; + } + + *dst = (struct apfl_refcounted_string) { + .refcount = 1, + .string = apfl_string_move(src), + }; + + return dst; +} + +struct apfl_refcounted_string * +apfl_refcounted_string_copy(struct apfl_refcounted_string *src) +{ + struct apfl_refcounted_string *dst = ALLOC(struct apfl_refcounted_string); + if (dst == NULL) { + return NULL; + } + + *dst = *src; + dst->refcount++; + + return dst; +} + +void +apfl_refcounted_string_unref(struct apfl_refcounted_string *rcstring) +{ + if (apfl_refcount_dec(&rcstring->refcount)) { + apfl_string_deinit(&rcstring->string); + } +} diff --git a/src/value.c b/src/value.c index c136516..9db7609 100644 --- a/src/value.c +++ b/src/value.c @@ -3,21 +3,11 @@ #include "apfl.h" #include "internal.h" -/** - * Decrease a refererence count. Returns true, if the object should be freed. - */ -static bool -dec_refcount(unsigned *rc) -{ - if (rc == 0) { - return false; - } - return --(*rc) == 0; -} - static void print(unsigned indent, FILE *out, struct apfl_value value) { + struct apfl_string_view sv; + switch (value.type) { case APFL_VALUE_NIL: apfl_print_indented(indent, out, "nil\n"); @@ -29,7 +19,8 @@ print(unsigned indent, FILE *out, struct apfl_value value) apfl_print_indented(indent, out, "%f\n", value.number); return; case APFL_VALUE_STRING: - apfl_print_indented(indent, out, "\"" APFL_STR_FMT "\"\n", APFL_STR_FMT_ARGS(value.string)); + sv = apfl_string_view_from(value.string); + apfl_print_indented(indent, out, "\"" APFL_STR_FMT "\"\n", APFL_STR_FMT_ARGS(sv)); return; case APFL_VALUE_LIST: apfl_print_indented(indent, out, "[\n"); @@ -46,8 +37,6 @@ print(unsigned indent, FILE *out, struct apfl_value value) bool apfl_value_copy(struct apfl_value *dst, struct apfl_value src) { - struct apfl_string string; - *dst = src; switch (src.type) { @@ -57,11 +46,9 @@ apfl_value_copy(struct apfl_value *dst, struct apfl_value src) // Nothing to do goto ok; case APFL_VALUE_STRING: - string = apfl_string_blank(); - if (!apfl_string_copy(&string, apfl_string_view_from(src.string))) { + if ((dst->string = apfl_refcounted_string_copy(src.string)) == NULL) { return false; } - dst->string = string; goto ok; case APFL_VALUE_LIST: assert(src.list != NULL); @@ -117,10 +104,10 @@ apfl_value_deinit(struct apfl_value *value) case APFL_VALUE_NUMBER: goto ok; case APFL_VALUE_STRING: - apfl_string_deinit(&value->string); + DESTROY(value->string, apfl_refcounted_string_unref); goto ok; case APFL_VALUE_LIST: - if (dec_refcount(&value->list->refcount)) { + if (apfl_refcount_dec(&value->list->refcount)) { DESTROY(value->list, apfl_list_deinit); } goto ok;