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:
parent
b2c252fbc2
commit
b29219af25
6 changed files with 96 additions and 34 deletions
27
src/apfl.h
27
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;
|
||||
};
|
||||
};
|
||||
|
|
|
|||
18
src/eval.c
18
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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
27
src/value.c
27
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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue