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.
This commit is contained in:
Laria 2022-01-02 17:55:05 +01:00
parent b2c252fbc2
commit b29219af25
6 changed files with 96 additions and 34 deletions

View file

@ -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;
};
};

View file

@ -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);

View file

@ -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;
}

View file

@ -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

View file

@ -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);
}
}

View file

@ -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;