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