Mercurial > lbo > hg > ylisp
changeset 51:a3bbf2a1a353
Check struct sizes and make yref_t take only 16B
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Fri, 23 Aug 2019 14:14:31 +0200 |
parents | fd6eb7b5966c |
children | a28fff6d1133 |
files | gen/y.yy src/CMakeLists.txt src/expr.c src/expr.h src/expr_test.c src/func.h src/sizes_test.c src/value.c src/value.h src/value_test.c |
diffstat | 10 files changed, 172 insertions(+), 31 deletions(-) [+] |
line wrap: on
line diff
--- a/gen/y.yy Fri Aug 23 10:57:57 2019 +0200 +++ b/gen/y.yy Fri Aug 23 14:14:31 2019 +0200 @@ -8,6 +8,7 @@ %{ #include <assert.h> +#include <error.h> #include <stdio.h> #include <string.h>
--- a/src/CMakeLists.txt Fri Aug 23 10:57:57 2019 +0200 +++ b/src/CMakeLists.txt Fri Aug 23 14:14:31 2019 +0200 @@ -27,3 +27,8 @@ ADD_EXECUTABLE(expr_test expr_test.c) TARGET_LINK_LIBRARIES(expr_test core gcov) YADD_TEST(expr_test) + +# Print sizes. +ADD_EXECUTABLE(sizes_test sizes_test.c) +TARGET_LINK_LIBRARIES(sizes_test core gcov) +YADD_TEST(sizes_test)
--- a/src/expr.c Fri Aug 23 10:57:57 2019 +0200 +++ b/src/expr.c Fri Aug 23 14:14:31 2019 +0200 @@ -4,28 +4,51 @@ #include <src/base/vec.h> #include <assert.h> -#include <stdbool.h> #include <stdio.h> -yref_t yref_new_id(yvalue_id_t id) { - yref_t ref = {.typ = YREF_ID, .ref.id = id}; +static const yvalue_id_t YVALUE_ID_MARKER = 0xff00000000000000; + +bool yvalue_is_valid(yvalue_id_t id) { + return (id & YVALUE_ID_MARKER) == YVALUE_ID_MARKER; +} + +YREF_TYPE yref_type(yref_t* ref) { + if ((ref->ref.id & YVALUE_ID_MARKER) == YVALUE_ID_MARKER) { + return YREF_ID; + } + return YREF_SYM; +} + +yvalue_id_t yvalue_create(void); + +yref_t yref_new(void) { + yref_t ref = {.ref.id = yvalue_create() }; return ref; } -yref_t yref_new_str(ystr_t* sym) { - yref_t ref = {.typ = YREF_SYM, .ref.sym = *sym}; + +yref_t yref_new_id(yvalue_id_t id) { + assert(yvalue_is_valid(id)); + yref_t ref = {.ref.id = id}; return ref; } + +yref_t yref_new_str(ystr_t* sym) { + yref_t ref = {.ref.sym = *sym}; + return ref; +} + yref_t yref_new_c(const char* sym) { - yref_t ref = {.typ = YREF_SYM, .ref.sym = ystr_new(sym)}; + yref_t ref = {.ref.sym = ystr_new(sym)}; return ref; } ystr_t yref_debug(yref_t* ref) { - if (ref->typ == YREF_ID) { + YREF_TYPE typ = yref_type(ref); + if (typ == YREF_ID) { ystr_t s = ystr_new(NULL); ystr_build(&s, "id:<%llu>", ref->ref.id); return s; - } else if (ref->typ == YREF_SYM) { + } else if (typ == YREF_SYM) { ystr_t s = ystr_new(NULL); ystr_build(&s, "id:<\"%s\">", ystr_str(&ref->ref.sym)); return s; @@ -35,7 +58,7 @@ void yref_destroy(yref_t* ref) { if (ref == NULL) return; - if (ref->typ == YREF_SYM) ystr_destroy(&ref->ref.sym); + if (yref_type(ref) == YREF_SYM) ystr_destroy(&ref->ref.sym); } void yexpr_init(yexpr_t* v) {
--- a/src/expr.h Fri Aug 23 10:57:57 2019 +0200 +++ b/src/expr.h Fri Aug 23 14:14:31 2019 +0200 @@ -3,6 +3,7 @@ #include <src/atom.h> +#include <stdbool.h> #include <stdio.h> #include <string.h> @@ -15,13 +16,20 @@ * @{ */ +/// @brief Numeric reference to a stored value or function (see value.h). Must have a +/// MSB that is 0xff. typedef uint64_t yvalue_id_t; -enum YREF_TYPE { +/** + * @brief Checks if the id is a valid ID. + */ +bool yvalue_is_valid(yvalue_id_t id); + +typedef enum { YREF_UNDEF = 0, YREF_ID = 1, YREF_SYM = 2, -}; +} YREF_TYPE; /** * @brief A reference to a stored function or value. To be used as value (not @@ -32,10 +40,19 @@ ystr_t sym; yvalue_id_t id; } ref; - enum YREF_TYPE typ; } yref_t; /** + * @brief Return type of a reference. + */ +YREF_TYPE yref_type(yref_t* ref); + +/** + * Create a new unnamed reference. + */ +yref_t yref_new(void); + +/** * Create a new reference with the given ID. */ yref_t yref_new_id(yvalue_id_t id);
--- a/src/expr_test.c Fri Aug 23 10:57:57 2019 +0200 +++ b/src/expr_test.c Fri Aug 23 14:14:31 2019 +0200 @@ -2,6 +2,28 @@ #include <src/value.h> +void test_expr_ref_type(void) { + ystr_t str = ystr_new("abc"); + yref_t ref0, ref = yref_new_c("abc"); + + assert(YREF_SYM == yref_type(&ref)); + yref_destroy(&ref); + + ref = yref_new_str(&str); + assert(YREF_SYM == yref_type(&ref)); + yref_destroy(&ref); + + ystr_t str2 = ystr_new("abc"); + ref = yref_new_id(yvalue_resolve_or_create_symbol(&str2)); + assert(YREF_ID == yref_type(&ref)); + ystr_destroy(&str2); + ref0 = ref; + + ref = yref_new(); + assert(YREF_ID == yref_type(&ref)); + assert(ref.ref.id == ref0.ref.id+1); +} + void test_expr_set(void) { yexpr_t expr = yexpr_new(); @@ -36,7 +58,7 @@ yexpr_debug(&expr); fputs("\n", stderr); yvalue_resolve_or_create_ref(&expr.value.ref); - assert(YREF_ID == expr.value.ref.typ); + assert(YREF_ID == yref_type(&expr.value.ref)); yexpr_debug(&expr); fputs("\n", stderr); yexpr_destroy(&expr); @@ -84,6 +106,7 @@ } int main(int argc, char** argv) { + test_expr_ref_type(); test_expr_set(); return 0; }
--- a/src/func.h Fri Aug 23 10:57:57 2019 +0200 +++ b/src/func.h Fri Aug 23 14:14:31 2019 +0200 @@ -11,12 +11,16 @@ * @{ */ +typedef struct { + ystr_t name; +} yfunc_desc_t; + /** * @brief Compiled function code. Functions are usually inserted in the global * value table and referenced there. */ typedef struct { - ystr_t name; + yfunc_desc_t desc; yvec_t instructions; } yfunc_t;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sizes_test.c Fri Aug 23 14:14:31 2019 +0200 @@ -0,0 +1,47 @@ +#include <assert.h> +#include <stdio.h> + +#include <src/atom.h> +#include <src/base/str.h> +#include <src/base/vec.h> +#include <src/expr.h> +#include <src/value.h> + +void print_sizes(void) { +#define PRINT_SIZE(TYP) fprintf(stderr, "%s: %d bytes\n", #TYP, sizeof(TYP)) + + PRINT_SIZE(yatom_t); + PRINT_SIZE(yexpr_t); + PRINT_SIZE(yfunc_t); + PRINT_SIZE(yref_t); + PRINT_SIZE(ystr_t); + PRINT_SIZE(yvalue_id_t); + PRINT_SIZE(yvec_t); + + PRINT_SIZE(YREF_TYPE); + PRINT_SIZE(YEXPR_TYPE); +} + +/** + * Protect against accidental enlargement of types. + */ +void assert_sizes(void) { + assert(16 == sizeof(ystr_t)); + assert(16 == sizeof(yvec_t)); +} + +/** + * yref_t assumes little endian. + */ +void assert_little_endian(void) { + uint64_t v = 0xff00000000000000; + const uint8_t* access = (const uint8_t*)&v; + assert(access[0] == 0 && access[7] == 0xff); +} + +int main(int argc, char** argv) { + print_sizes(); + assert_sizes(); + assert_little_endian(); + return 0; +}
--- a/src/value.c Fri Aug 23 10:57:57 2019 +0200 +++ b/src/value.c Fri Aug 23 14:14:31 2019 +0200 @@ -7,11 +7,12 @@ const yvalue_id_t YVALUE_INVALID_ID = UINT64_MAX - 1; /// Use YVALUE_INSERT in `yvalue_set()` to insert a new value. -const yref_t YVALUE_INSERT = {.typ = YREF_ID, .ref.id = UINT64_MAX - 2}; +const yref_t YVALUE_INSERT = {.ref.id = (0xff00000000000000 | UINT64_MAX) - 2}; // TODO: Adjust when programs are bigger. static const size_t YVALUE_INITIAL_VALUES = 256, YVALUE_MAX_VALUES = 1024; -static const yvalue_id_t YVALUE_COUNTER_OFFSET = 0xb0b0b0b0b0; +// ff marks IDs as such. +static const yvalue_id_t YVALUE_COUNTER_OFFSET = 0xffb0b0b0b0b0b0b0; // ylisp keeps tables containing all named values. Unnamed values are // represented as yexpr_t, whereas named values are referenced by their name @@ -61,12 +62,22 @@ ENTRY want = {NULL, NULL}; ENTRY* result; - want.key = sym_s; // Valid, key is not modified. + want.key = (char*)sym_s; // Valid, key is not modified. hsearch_r(want, FIND, &result, &yvalue_name_table); if (result == NULL) return YVALUE_INVALID_ID; return (yvalue_id_t)result->data; } +yvalue_id_t yvalue_create(void) { + yvalue_init_tables(); + + // Create new value entry. + yvalue_t empty; + memset(&empty, 0, sizeof(yvalue_t)); + YVEC_PUSH(&yvalue_table, &empty); + return yvalue_counter++; +} + yvalue_id_t yvalue_resolve_or_create_symbol(ystr_t* sym) { yvalue_init_tables(); @@ -85,25 +96,24 @@ return new_id; } -bool yvalue_resolve_or_create_ref(yref_t* ref) { +void yvalue_resolve_or_create_ref(yref_t* ref) { yvalue_init_tables(); + YREF_TYPE typ = yref_type(ref); - if (ref->typ == YREF_ID) return true; - if (ref->typ == YREF_SYM) { + if (typ == YREF_ID) return; + if (typ == YREF_SYM) { yvalue_id_t id = yvalue_resolve_or_create_symbol(&ref->ref.sym); ystr_destroy(&ref->ref.sym); ref->ref.id = id; - ref->typ = YREF_ID; - return true; } - assert(ref->typ == YREF_ID || ref->typ == YREF_SYM); } yref_t yvalue_set(yref_t ref, yvalue_t* val) { yvalue_init_tables(); + YREF_TYPE typ = yref_type(&ref); // ref == YVALUE_INSERT - if (ref.typ == YVALUE_INSERT.typ && ref.ref.id == YVALUE_INSERT.ref.id) { + if (typ == YREF_ID && ref.ref.id == YVALUE_INSERT.ref.id) { // Unnamed value. ref.ref.id = yvalue_counter++; size_t new_index = YVEC_PUSH(&yvalue_table, val); @@ -118,22 +128,22 @@ yvalue_t* yvalue_get(yref_t ref) { yvalue_init_tables(); + YREF_TYPE typ = yref_type(&ref); - if (ref.typ == YREF_ID) { + if (typ == YREF_ID) { assert(ref.ref.id <= yvalue_counter); yvalue_t* vp = YVEC_AT(&yvalue_table, ref.ref.id - YVALUE_COUNTER_OFFSET, yvalue_t); assert(vp != NULL); return vp; - } else if (ref.typ == YREF_SYM) { + } else if (typ == YREF_SYM) { yvalue_id_t id = yvalue_resolve_symbol(&ref.ref.sym); if (id == YVALUE_INVALID_ID) { return NULL; } - ref.typ = YREF_ID; ref.ref.id = id; return yvalue_get(ref); } else { - assert(ref.typ == YREF_ID || ref.typ == YREF_SYM); + return NULL; } }
--- a/src/value.h Fri Aug 23 10:57:57 2019 +0200 +++ b/src/value.h Fri Aug 23 14:14:31 2019 +0200 @@ -49,6 +49,11 @@ } yvalue_t; /** + * @brief Create an unnamed value ID. + */ +yvalue_id_t yvalue_create(void); + +/** * @brief Free all memory used by a value. */ void yvalue_destroy(yvalue_t* val); @@ -69,7 +74,7 @@ * @brief Translate a symbol in `ref` to an ID, or create a new ID. `ref` is * updated to be an ID ref. Caller retains ownership of `ref`. */ -bool yvalue_resolve_or_create_ref(yref_t* ref); +void yvalue_resolve_or_create_ref(yref_t* ref); /** * @brief Set a value referenced by `ref`. If a new value should be created, use
--- a/src/value_test.c Fri Aug 23 10:57:57 2019 +0200 +++ b/src/value_test.c Fri Aug 23 14:14:31 2019 +0200 @@ -5,11 +5,17 @@ void test_value_create_resolve(void) { ystr_t sym; ystr_init(&sym, "vcr_symbol"); + assert(YVALUE_INVALID_ID == yvalue_resolve_symbol(&sym)); + yvalue_id_t id = yvalue_resolve_or_create_symbol(&sym); assert(YVALUE_INVALID_ID != id); assert(id == yvalue_resolve_symbol(&sym)); + yvalue_id_t id2 = yvalue_create(); + assert(id2 != id); + assert(id2 == id+1); + ystr_destroy(&sym); } @@ -21,7 +27,7 @@ yvalue_resolve_or_create_ref(&ref); assert(YVALUE_INVALID_ID != yvalue_resolve_symbol(&symbol2)); - assert(YREF_ID == ref.typ); + assert(YREF_ID == yref_type(&ref)); assert(ref.ref.id == yvalue_resolve_symbol(&symbol2)); ystr_destroy(&symbol2); @@ -38,7 +44,7 @@ // Allocate unnamed ref. yref_t ref = yvalue_set(YVALUE_INSERT, &val); - assert(YREF_ID == ref.typ); + assert(YREF_ID == yref_type(&ref)); yvalue_t* got = yvalue_get(ref); assert(YVALUE_EXPR == got->typ); assert(YEXPR_NUMBER == got->value.expr.typ);