From e8b3499289022c8729900a58ed218d961a85fec8 Mon Sep 17 00:00:00 2001 From: eunhhu Date: Sun, 18 Oct 2026 18:52:48 +0100 Subject: Module format and executor: 027's 4b-1, a record field holding bytes (type 5): held as a text is but for UTF-8, its default a constant; a function's bytes in a signature this type too (PURE_SIGNATURE_BYTES gone); no list keyed by bytes; replaced, a text field becomes a bytes field keeping its value (pure_cell_as, kinds_carry); images' headers take its kind; the JSON edge reads it from a string; pure-run shows it as hex; an append asks a record's fields held as bytes from a mask the load makes (record_held) instead of each field's type twice, so b32 runs fewer instructions than b30 (BFS without batches -1.05%, join -0.25%, BFS with batches -0.36%, the rest within 0.04%; the bank's queries s30 against s32 -0.01% to +0.30%); unit test bytes_fields (bytes not UTF-8, a text field replaced by a bytes field, three refused); server_trees with a bytes field; 008 and 027 --- core/test-module.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) (limited to 'core/test-module.c') diff --git a/core/test-module.c b/core/test-module.c index 9d925dd..181f96d 100644 --- a/core/test-module.c +++ b/core/test-module.c @@ -56,7 +56,8 @@ typedef struct { const char *name; uint32_t type, ref; } T; /* type: the state t #define TR (1u << 24) /* in a T's type: a transient state */ /* A record field: a number and its default, text (text = 1) whose default * is the constant numbered initial, a bool (text = 2) or an i64 (text = 3) - * and its default. */ + * and its default, a list (4, initial its record) or bytes (5, its default + * a constant as a text's). */ typedef struct { const char *name; uint32_t text; double initial; } RF; typedef struct { const char *name; const RF *fields; size_t nfields; } R; /* A further view: a table (type 2, start = list state) or a column of the @@ -185,7 +186,7 @@ static size_t build(unsigned char *out, Spec spec) { const RF *field = &spec.r[i].fields[f]; p = put32(p, offset[++n]); p = put32(p, field->text); - if (field->text == 1) { p = put32(p, (uint32_t)field->initial); p = put32(p, 0); } + if (field->text == 1 || field->text == 5) { p = put32(p, (uint32_t)field->initial); p = put32(p, 0); } else if (field->text == 3 || field->text == 4) { /* an i64's default, a list's record */ int64_t initial = (int64_t)field->initial; p = put32(p, (uint32_t)(uint64_t)initial); @@ -6686,6 +6687,61 @@ static void nested_rows(void) { printf("{\"nested_rows\":{\"rounds\":3,\"refused\":%zu}}\n", LEN(cuts) + 1); } +/* 027's 4b: a record field holding bytes. A message's bytes (any, UTF-8 or not) go into a list's item and come back + * as they were; a text goes where bytes go only as bytes (BYTES_FROM), and bytes never where a text goes. Replaced, a + * text field becomes a bytes field keeping its value, and a new bytes field takes its constant. */ +static void bytes_fields(void) { + enum { BF = PURE_OP_BYTES_FROM, TC = PURE_OP_TEXT_CONST, XL_ = PURE_OP_INDEX_LAST, ITEMS = 1, BLOB = 2 }; + static const S nums[] = {{"n", 0}}; + static const char *const constants[] = {"", "zz"}; + static const RF text_fields[] = {{"t", 1, 0}}; + static const RF bytes_fields_[] = {{"t", 5, 0}, {"b", 5, 1}}; + static const R before[] = {{"Item", text_fields, 1}}, after[] = {{"Item", bytes_fields_, 2}}; + static const T others[] = {{"items", 3, 0}, {"blob", PURE_STATE_TEXT, 0}}; + static const F text_in[] = {{"t", PURE_FIELD_TEXT}}, bytes_in[] = {{"b", PURE_FIELD_BYTES}}; + static const I inputs_a[] = {{"put", NULL, PURE_KIND_MESSAGE, false, text_in, 1}}; + static const I inputs_b[] = {{"put", NULL, PURE_KIND_MESSAGE, false, bytes_in, 1}}; + /* A: items.append(Item { t: put.t }); n = items.count */ + static const O code_a[] = {{FIELD, 0, 0}, {LA, ITEMS, 0}, {LC, ITEMS, 0}, {ST, 0, 0}}; + /* B: items.append(Item { t: put.b, b: bytes("zz") + put.b }); n = items.last.b's length */ + static const O code_b[] = {{FIELD, 0, 0}, {TC, 1, 0}, {BF, 0, 0}, {FIELD, 0, 0}, {CAT, 1, 0}, {LA, ITEMS, 0}, + {XL_, ITEMS, 0}, {IG, ITEM(ITEMS, 1), 0}, {PURE_OP_LEN, 0, 0}, {ST, 0, 0}}; + H ha = {1, 0, LEN(code_a)}, hb = {1, 0, LEN(code_b)}; + Spec a = {nums, 1, inputs_a, 1, NULL, 0, &ha, 1, code_a, LEN(code_a), NULL, 0, others, LEN(others), constants, LEN(constants), + NULL, 0, before, 1, NULL, 0}; + Spec b = {nums, 1, inputs_b, 1, NULL, 0, &hb, 1, code_b, LEN(code_b), NULL, 0, others, LEN(others), constants, LEN(constants), + NULL, 0, after, 1, NULL, 0}; + if (load(a, &module_a) != PURE_OK || load(b, &module_b) != PURE_OK) { fprintf(stderr, "bytes fields: %s\n", error); abort(); } + PureApp app; + CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK); + CHECK(pure_app_message(&app, 0, NULL, -1, (PureSpan){(const unsigned char *)"héllo", 6}) == PURE_OK); + CHECK(pure_app_replace(&app, &module_b) == PURE_OK); + PureCell t = pure_list_get(app.lists[ITEMS], 0, 0), zz = pure_list_get(app.lists[ITEMS], 0, 1); + CHECK(t.size == 6 && !memcmp(t.text, "héllo", 6) && zz.size == 2 && !memcmp(zz.text, "zz", 2)); + static const unsigned char raw[] = {0xFF, 0x00, 0xC3, 0x28, 0x80}; /* no UTF-8 */ + CHECK(pure_app_message(&app, 0, NULL, -1, (PureSpan){raw, sizeof(raw)}) == PURE_OK && app.values[0].num == 2 + sizeof(raw)); + PureCell back = pure_list_get(app.lists[ITEMS], 1, 0), joined = pure_list_get(app.lists[ITEMS], 1, 1); + CHECK(back.size == sizeof(raw) && !memcmp(back.text, raw, sizeof(raw)) && joined.size == 2 + sizeof(raw) && + !memcmp(joined.text, "zz", 2) && !memcmp(joined.text + 2, raw, sizeof(raw))); + pure_app_stop(&app); + /* Refused: a text where bytes go, bytes where a text goes (the field read into a text state). */ + static const O text_in_bytes[] = {{TC, 0, 0}, {TC, 1, 0}, {LA, ITEMS, 0}, {C, 0, 0}, {ST, 0, 0}}; + static const O bytes_as_text[] = {{XL_, ITEMS, 0}, {IG, ITEM(ITEMS, 0), 0}, {PURE_OP_TEXT_SET, BLOB, 0}, {C, 0, 0}, {ST, 0, 0}}; + const O *refused[] = {text_in_bytes, bytes_as_text}; + for (size_t k = 0; k < LEN(refused); ++k) { + H one = {1, 0, 5}; + Spec bad = {nums, 1, inputs_b, 1, NULL, 0, &one, 1, refused[k], 5, NULL, 0, others, LEN(others), constants, LEN(constants), + NULL, 0, after, 1, NULL, 0}; + CHECK(load(bad, &module_a) != PURE_OK && strstr(error, "operand type")); + } + static const T keyed[] = {{"items", 3 | 1u << 8, 0}, {"blob", PURE_STATE_TEXT, 0}}; /* keyed by t, bytes */ + H one = {1, 0, LEN(code_b)}; + Spec by_bytes = {nums, 1, inputs_b, 1, NULL, 0, &one, 1, code_b, LEN(code_b), NULL, 0, keyed, LEN(keyed), constants, + LEN(constants), NULL, 0, after, 1, NULL, 0}; + CHECK(load(by_bytes, &module_a) != PURE_OK && strstr(error, "bytes as a key")); + printf("{\"bytes_fields\":{\"items\":2,\"refused\":%zu}}\n", LEN(refused) + 1); +} + /* A timer's tick sees the time the host set; floor and a floored modulo * take it apart. now is for handlers only. */ enum { NOW = PURE_OP_NOW, FLOOR = PURE_OP_FLOOR, MOD = PURE_OP_MOD }; @@ -7066,6 +7122,7 @@ int main(void) { nested_crashes(); nested_replace(); nested_rows(); + bytes_fields(); memory_budget(); work_budget(); fold_mutations(); -- cgit v1.2.3