aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-09-30 15:51:32 +0100
committereunhhu <[email protected]>2026-09-30 15:51:32 +0100
commite02bfc916743a0327d76144b32b6ec6381e5907f (patch)
treefaf17d43c373e322c5b55ae8adc680d09bd49b4c
parentee21aec10726863d8e812643e1419e4b7d0f2b51 (diff)
downloadpure-e02bfc916743a0327d76144b32b6ec6381e5907f.tar.gz
pure-e02bfc916743a0327d76144b32b6ec6381e5907f.zip
Let a value's 8 bytes hold a number or an integer
Before i64 (016 stage 2): a state's value, a record field's default, a list cell's number, the interpreter's stack slot and an instruction's constant become unions of a double and an int64_t (PureValue, anonymous unions in PureCell, Slot and PureOp). LOAD and STORE, and a list cell made from the stack, copy the bits; everything else reads .num as before. No behaviour changes: every check passes as it did.
-rw-r--r--cli/pure-run.c4
-rw-r--r--core/module-list.c8
-rw-r--r--core/module-list.h2
-rw-r--r--core/module-save.c8
-rw-r--r--core/module-save.h4
-rw-r--r--core/module.c107
-rw-r--r--core/module.h15
-rw-r--r--core/test-module.c300
-rw-r--r--serve/message-json.c4
-rw-r--r--tests/module-probe.c2
10 files changed, 231 insertions, 223 deletions
diff --git a/cli/pure-run.c b/cli/pure-run.c
index 4b2e236..b9eb222 100644
--- a/cli/pure-run.c
+++ b/cli/pure-run.c
@@ -98,8 +98,8 @@ static void show(PureApp *app, const char *failure) {
pure_draft_read(app->texts[i], 0, pure_draft_size(app->texts[i]), json_span, NULL);
putchar('"');
} else if (m->state_type[i] == PURE_STATE_LIST) show_list(m, app->lists[i], &m->record[m->state_ref[i]]);
- else if (m->state_type[i] == PURE_STATE_BOOL) printf("%s", app->values[i] ? "true" : "false");
- else printf("%.17g", app->values[i]);
+ else if (m->state_type[i] == PURE_STATE_BOOL) printf("%s", app->values[i].num ? "true" : "false");
+ else printf("%.17g", app->values[i].num);
}
printf("},\"views\":{");
bool first = true;
diff --git a/core/module-list.c b/core/module-list.c
index 16511de..20fed2b 100644
--- a/core/module-list.c
+++ b/core/module-list.c
@@ -130,9 +130,9 @@ static double *numbers(const Layout *l, const Node *leaf, unsigned f) { return (
static PureSpan *texts(const Layout *l, const Node *leaf, unsigned f) { return (PureSpan *)((char *)leaf + l->column[f]); }
static PureCell cell_at(const Layout *l, const Node *leaf, unsigned i, unsigned f) {
- if (!l->text_fields[f]) return (PureCell){numbers(l, leaf, f)[i], NULL, 0};
+ if (!l->text_fields[f]) return (PureCell){.num = numbers(l, leaf, f)[i]};
PureSpan text = texts(l, leaf, f)[i];
- return (PureCell){0, (unsigned char *)text.data, text.size};
+ return (PureCell){.text = (unsigned char *)text.data, .size = text.size};
}
static void put_cells(const Layout *l, Node *leaf, unsigned i, const PureCell *cells) {
@@ -706,7 +706,7 @@ static bool decode_cell(const Layout *l, unsigned field, const unsigned char *by
}
if (bits > size - *at || (l->text_fields[field] == 1 && !pure_utf8_valid((PureSpan){bytes + *at, (size_t)bits})))
return false;
- *cell = (PureCell){0, (unsigned char *)bytes + *at, (size_t)bits};
+ *cell = (PureCell){.text = (unsigned char *)bytes + *at, .size = (size_t)bits};
*at += (size_t)bits;
return true;
}
@@ -769,7 +769,7 @@ static bool get_compact(const Layout *l, unsigned field, const unsigned char *by
if (l->text_fields[field]) {
if (value > size - *at || (l->text_fields[field] == 1 && !pure_utf8_valid((PureSpan){bytes + *at, (size_t)value})))
return false;
- *cell = (PureCell){0, (unsigned char *)bytes + *at, (size_t)value};
+ *cell = (PureCell){.text = (unsigned char *)bytes + *at, .size = (size_t)value};
*at += (size_t)value;
return true;
}
diff --git a/core/module-list.h b/core/module-list.h
index d97aa77..603134a 100644
--- a/core/module-list.h
+++ b/core/module-list.h
@@ -19,7 +19,7 @@
* cell per field: a number, or text bytes the list owns. Every change made
* during an event is logged; a failed event undoes the log in reverse and a
* successful one frees what it replaced. */
-typedef struct { double num; unsigned char *text; size_t size; } PureCell;
+typedef struct { union { double num; int64_t i64; }; unsigned char *text; size_t size; } PureCell;
typedef struct PureList PureList;
/* An event's list changes; a removed item's cells wait in cells. */
typedef struct {
diff --git a/core/module-save.c b/core/module-save.c
index 2b96252..ae32c02 100644
--- a/core/module-save.c
+++ b/core/module-save.c
@@ -696,7 +696,7 @@ PURE_COLD static bool fits(const PureModule *m, PureDraft *const *texts, PureLis
return pure_draft_read(text, at, 1, gather, &w) == PURE_OK && (byte & 0xC0) != 0x80;
}
-static size_t encode_values(const PureModule *m, const double *values, unsigned char *out) {
+static size_t encode_values(const PureModule *m, const PureValue *values, unsigned char *out) {
size_t at = 4;
memcpy(out, values_magic, 4);
for (uint32_t i = 0; i < m->states; ++i) {
@@ -715,7 +715,7 @@ static size_t encode_values(const PureModule *m, const double *values, unsigned
enum { VALUES_BYTES = 4 + PURE_MODULE_STATES * (2 + PURE_MODULE_NAME + 8) };
-PURE_COLD PureResult pure_save_open_values(PureApp *app, const PureModule *m, double *values, const int *kept,
+PURE_COLD PureResult pure_save_open_values(PureApp *app, const PureModule *m, PureValue *values, const int *kept,
PureDraft *const *texts, PureList *const *lists) {
bool scalars = false, fresh = false;
for (uint32_t i = 0; i < m->states; ++i)
@@ -743,7 +743,7 @@ PURE_COLD PureResult pure_save_open_values(PureApp *app, const PureModule *m, do
if (state >= 0 && kept[state] < 0 && scalar(type) &&
(m->state_type[state] == type || (valued(type) && valued(m->state_type[state]))) &&
fits(m, texts, lists, (uint32_t)state, value))
- values[state] = m->state_type[state] == PURE_STATE_BOOL ? pure_as_bool(value, 0) : value;
+ values[state].num = m->state_type[state] == PURE_STATE_BOOL ? pure_as_bool(value, 0) : value;
}
free(bytes);
return ok ? PURE_OK : PURE_STORE_ERROR;
@@ -1002,7 +1002,7 @@ static unsigned char *list_changes(const PureApp *app, size_t *size) {
return payload;
}
-PureResult pure_save_commit(PureApp *app, const double *values, void *const *documents, size_t document_count,
+PureResult pure_save_commit(PureApp *app, const PureValue *values, void *const *documents, size_t document_count,
int64_t *revisions) {
if (!pure_save_enabled(app)) {
/* Without a host that saves together, a save has one document at most. */
diff --git a/core/module-save.h b/core/module-save.h
index 9c0f74e..991c498 100644
--- a/core/module-save.h
+++ b/core/module-save.h
@@ -43,7 +43,7 @@ PureList *pure_runtime_list_new(uint32_t place);
/* Open the stored numbers, cursors and indices, and put each stored one of the
* same name and kind that still fits into values, for the states of m that
* kept marks as new (-1); texts and lists are m's. */
-PureResult pure_save_open_values(PureApp *, const PureModule *m, double *values, const int *kept,
+PureResult pure_save_open_values(PureApp *, const PureModule *m, PureValue *values, const int *kept,
PureDraft *const *texts, PureList *const *lists);
/* The stored links of a starting app (module.h, PureApp), after its runtime
* lists. The sends left in the outbox of an output whose fields changed are
@@ -56,7 +56,7 @@ PureResult pure_save_open_values(PureApp *, const PureModule *m, double *values,
PureResult pure_save_open_links(PureApp *, const PureModule *m, bool *heard_moved, bool *sent_moved);
/* Publish the documents and every state changed since the last save
* (values are the states' numbers); revisions are the documents'. */
-PureResult pure_save_commit(PureApp *, const double *values, void *const *documents, size_t document_count,
+PureResult pure_save_commit(PureApp *, const PureValue *values, void *const *documents, size_t document_count,
int64_t *revisions);
/* A message's fields laid out as load lays them: in order, the fixed ones
diff --git a/core/module.c b/core/module.c
index db19222..99b3088 100644
--- a/core/module.c
+++ b/core/module.c
@@ -991,9 +991,9 @@ PURE_COLD PureResult pure_module_load(const void *bytes, size_t size, PureModule
(rows && !strchr(out->state_names[i], '.')))
REFUSE("state name", i);
if (value) {
- out->initial[i] = read_double(at + 8);
- if (!isfinite(out->initial[i])) REFUSE("non-finite initial value", i);
- if (out->state_type[i] == PURE_STATE_BOOL && !canonical_bool(out->initial[i])) REFUSE("bool initial value", i);
+ out->initial[i].num = read_double(at + 8);
+ if (!isfinite(out->initial[i].num)) REFUSE("non-finite initial value", i);
+ if (out->state_type[i] == PURE_STATE_BOOL && !canonical_bool(out->initial[i].num)) REFUSE("bool initial value", i);
} else {
if ((flags && !(cursor && flags == PURE_CURSOR_STAYS) && !(list && flags <= PURE_RECORD_FIELDS) && !rows) ||
read32(at + 8) > UINT16_MAX) REFUSE("state reference", i);
@@ -1101,9 +1101,10 @@ PURE_COLD PureResult pure_module_load(const void *bytes, size_t size, PureModule
REFUSE("record field", f);
for (uint32_t k = record->first; k < f; ++k)
if (!strcmp(out->record_field_names[k], out->record_field_names[f])) REFUSE("duplicate record field", f);
- out->record_field_default[f] = type == 1 ? read32(entry + 8) : read_double(entry + 8);
+ out->record_field_default[f].num = type == 1 ? read32(entry + 8) : read_double(entry + 8);
+ double given = out->record_field_default[f].num;
if (type == 1 ? read32(entry + 12) || read32(entry + 8) >= out->constants
- : !isfinite(out->record_field_default[f]) || (type == 2 && !canonical_bool(out->record_field_default[f])))
+ : !isfinite(given) || (type == 2 && !canonical_bool(given)))
REFUSE("record field default", f);
}
next_record_field += record->count;
@@ -1344,7 +1345,7 @@ int pure_module_column(const PureModule *m, uint32_t table, const char *name) {
static double minimum(double a, double b) { return a < b || (a == b && signbit(a)) ? a : b; }
static double maximum(double a, double b) { return a > b || (a == b && !signbit(a)) ? a : b; }
-typedef struct { double num; PureBytes bytes; } Slot;
+typedef struct { union { double num; int64_t i64; }; PureBytes bytes; } Slot;
/* A walk (EACH): the list, the item it runs for, where it ends (items the
* walk removes move it), and whether that item left. */
typedef struct { uint32_t list; size_t at, end; bool removed; } Walk;
@@ -1453,32 +1454,32 @@ static PureBytes text_of(const PureApp *app, uint32_t state) {
return (PureBytes){FROM_TEXT, (uint16_t)state, version, 0, version.bytes};
}
-static void move_cursors(const PureModule *m, double *values, uint32_t text, size_t at,
+static void move_cursors(const PureModule *m, PureValue *values, uint32_t text, size_t at,
size_t removed, size_t inserted) {
for (uint32_t c = 0; c < m->states; ++c) {
if (m->state_type[c] != PURE_STATE_CURSOR || m->state_ref[c] != text) continue;
- double position = values[c];
+ double position = values[c].num;
if (removed) {
if (position >= at + removed) position -= (double)removed;
else if (position > at) position = (double)at;
} else if (position > at || (position == at && !(m->state_flags[c] & PURE_CURSOR_STAYS)))
position += (double)inserted;
- values[c] = position;
+ values[c].num = position;
}
}
/* Indices follow their item across an insert or remove; an index whose item
* was removed names the next item (or the last, or 0 in an empty list). */
-static void move_indices(const PureModule *m, double *values, uint32_t list, size_t count,
+static void move_indices(const PureModule *m, PureValue *values, uint32_t list, size_t count,
size_t at, bool inserted) {
double last = count ? (double)(count - 1) : 0;
for (uint32_t i = 0; i < m->states; ++i) {
if (m->state_type[i] != PURE_STATE_INDEX || m->state_ref[i] != list) continue;
if (inserted) {
- if (count > 1 && values[i] >= (double)at) ++values[i];
+ if (count > 1 && values[i].num >= (double)at) ++values[i].num;
} else {
- if (values[i] > (double)at) --values[i];
- values[i] = minimum(values[i], last);
+ if (values[i].num > (double)at) --values[i].num;
+ values[i].num = minimum(values[i].num, last);
}
}
}
@@ -1550,7 +1551,7 @@ static PureUndo *undo_for(PureApp *app, uint32_t list) {
? NULL : &app->undo;
}
-static PureResult run(const PureModule *m, double *values, PureBlock block, Event *ev, Slot *result);
+static PureResult run(const PureModule *m, PureValue *values, PureBlock block, Event *ev, Slot *result);
/* ---- Folds and filters a leaf at a time ------------------------------
* A block whose instructions all have lane forms (has_lanes) runs for a run
@@ -1656,7 +1657,7 @@ static bool lanes_op(uint8_t op, const double *a, size_t sa, const double *b, si
* result holds what it leaves (lanes may point into store, a row of lanes
* per stack depth, or into the list's leaf). false when run would fault for
* some item. */
-static bool run_lanes(const PureModule *m, const double *values, PureBlock block, const Event *ev, const PureRun *items,
+static bool run_lanes(const PureModule *m, const PureValue *values, PureBlock block, const Event *ev, const PureRun *items,
Lanes *result, double (*store)[PURE_LIST_RUN], Memo *memo, const PureShape *shape) {
PureApp *app = ev->app;
Lanes stack[PURE_MODULE_STACK];
@@ -1680,7 +1681,7 @@ static bool run_lanes(const PureModule *m, const double *values, PureBlock block
}
switch (op->op) {
case PURE_OP_CONST: stack[sp++] = (Lanes){NULL, op->value, false, 0}; continue;
- case PURE_OP_LOAD: stack[sp++] = (Lanes){NULL, values[arg], false, 0}; continue;
+ case PURE_OP_LOAD: stack[sp++] = (Lanes){NULL, values[arg].num, false, 0}; continue;
case PURE_OP_FIELD: {
const PureField *field = &m->field[ev->shape->field_start + arg];
const unsigned char *given = ev->arguments ? app->arguments.data : app->payload.data;
@@ -1689,7 +1690,7 @@ static bool run_lanes(const PureModule *m, const double *values, PureBlock block
stack[sp++] = (Lanes){NULL, value, false, 0};
continue;
}
- case PURE_OP_INDEX_LOAD: stack[sp++] = (Lanes){NULL, values[arg], false, m->state_ref[arg]}; continue;
+ case PURE_OP_INDEX_LOAD: stack[sp++] = (Lanes){NULL, values[arg].num, false, m->state_ref[arg]}; continue;
case PURE_OP_ROW: stack[sp++] = (Lanes){NULL, 0, true, (uint16_t)arg}; continue;
case PURE_OP_LOCAL_LOAD: stack[sp++] = (Lanes){NULL, ev->locals[arg & 0xFF], false, (uint16_t)(arg >> 16)}; continue;
case PURE_OP_OUTER_ROW: stack[sp++] = (Lanes){NULL, (double)ev->outer, false, (uint16_t)arg}; continue;
@@ -1743,7 +1744,7 @@ static bool run_lanes(const PureModule *m, const double *values, PureBlock block
if (uniform(top)) {
double value;
if (!recalled(memo, pc - block.start, &value)) {
- size_t at = pure_list_seek(keyed, key, (PureCell){top->value, NULL, 0}, &found);
+ size_t at = pure_list_seek(keyed, key, (PureCell){.num = top->value}, &found);
if (find && !found) return false;
remember(memo, pc - block.start, value = find ? (double)at : found);
}
@@ -1752,7 +1753,7 @@ static bool run_lanes(const PureModule *m, const double *values, PureBlock block
}
const double *keys = lanes_of(top, scratch[0], items);
for (unsigned i = 0; i < n; ++i) {
- size_t at = pure_list_seek(keyed, key, (PureCell){keys[i], NULL, 0}, &found);
+ size_t at = pure_list_seek(keyed, key, (PureCell){.num = keys[i]}, &found);
if (find && !found) return false;
out[i] = find ? (double)at : found;
}
@@ -1894,7 +1895,7 @@ static bool fold_in(Fold *fold, size_t row, double value, double counts) {
}
/* The fold's block for its items from first to last, one at a time. */
-static PureResult fold_rows(const PureModule *m, double *values, PureBlock inner, Event *per, size_t first,
+static PureResult fold_rows(const PureModule *m, PureValue *values, PureBlock inner, Event *per, size_t first,
size_t last, Fold *fold) {
bool one = fold->how == PURE_FOLD_COUNT || fold->how == PURE_FOLD_FIRST;
for (size_t row = first; row < last && !fold->done; ++row) {
@@ -1912,7 +1913,7 @@ static PureResult fold_rows(const PureModule *m, double *values, PureBlock inner
/* The fold's block a leaf of its list at a time; a run where an item would
* fault goes item by item. The items join the fold in order, so a sum adds
* as it would one at a time. */
-static PureResult fold_runs(const PureModule *m, double *values, PureBlock inner, const PureList *list, Event *per,
+static PureResult fold_runs(const PureModule *m, PureValue *values, PureBlock inner, const PureList *list, Event *per,
Fold *fold, size_t from, size_t to) {
double store[PURE_MODULE_STACK][PURE_LIST_RUN], scratch[2][PURE_LIST_RUN];
bool one = fold->how == PURE_FOLD_COUNT || fold->how == PURE_FOLD_FIRST;
@@ -1957,15 +1958,15 @@ static PureResult fold_runs(const PureModule *m, double *values, PureBlock inner
/* The positions of the items whose keys are from low up to before high. */
static void slice_of(const PureList *list, unsigned key, double low, double high, size_t *from, size_t *to) {
bool found;
- *from = pure_list_seek(list, key, (PureCell){low, NULL, 0}, &found);
- *to = pure_list_seek(list, key, (PureCell){high, NULL, 0}, &found);
+ *from = pure_list_seek(list, key, (PureCell){.num = low}, &found);
+ *to = pure_list_seek(list, key, (PureCell){.num = high}, &found);
if (*to < *from) *to = *from;
}
/* The positions of the items whose keys a range allows: its bounds run
* alone, then found by key. false when a bound faults: the test then runs
* for every item, as written (and faults as it would). */
-static bool range_bounds(const PureModule *m, double *values, const PureRange *range, const PureList *list, unsigned key,
+static bool range_bounds(const PureModule *m, PureValue *values, const PureRange *range, const PureList *list, unsigned key,
Event *ev, size_t *from, size_t *to) {
PureApp *app = ev->app;
PureFault fault = app->fault;
@@ -1982,7 +1983,7 @@ static bool range_bounds(const PureModule *m, double *values, const PureRange *r
ev->arena->used = mark;
if (!ok) break;
bool found;
- size_t at = pure_list_seek(list, key, (PureCell){bound.num, NULL, 0}, &found);
+ size_t at = pure_list_seek(list, key, (PureCell){.num = bound.num}, &found);
at += (relation == PURE_OP_GT || relation == PURE_OP_LE) && found; /* past the key itself */
if (side) *to = at < *to ? at : *to;
else *from = at;
@@ -2109,7 +2110,7 @@ static Group *group_of(struct PurePass *pass, double key, unsigned how, bool add
}
/* Every group's fold in one pass over the list, a leaf at a time. */
-static bool group_pass(const PureModule *m, double *values, PureBlock inner, const PureShape *shape, unsigned how,
+static bool group_pass(const PureModule *m, PureValue *values, PureBlock inner, const PureShape *shape, unsigned how,
const PureList *list, Event *per, struct PurePass *pass) {
double store[PURE_MODULE_STACK][PURE_LIST_RUN], scratch[2][PURE_LIST_RUN];
bool one = how == PURE_FOLD_COUNT || how == PURE_FOLD_FIRST;
@@ -2175,7 +2176,7 @@ static size_t ranked(const struct PurePass *pass, uint8_t relation, double key)
}
/* A shaped fold from its list's pass: false leaves it to run as written. */
-static bool fold_shaped(const PureModule *m, double *values, const PureOp *op, const PureList *list, Event *per,
+static bool fold_shaped(const PureModule *m, PureValue *values, const PureOp *op, const PureList *list, Event *per,
Fold *fold) {
PureApp *app = per->app;
const PureShape *shape = &m->shape[op->shape - 1];
@@ -2211,7 +2212,7 @@ static bool fold_shaped(const PureModule *m, double *values, const PureOp *op, c
return true;
}
-static PureResult run(const PureModule *m, double *values, PureBlock block, Event *ev, Slot *result) {
+static PureResult run(const PureModule *m, PureValue *values, PureBlock block, Event *ev, Slot *result) {
Slot stack[PURE_MODULE_STACK];
size_t sp = 0;
PureApp *app = ev->app;
@@ -2225,11 +2226,11 @@ static PureResult run(const PureModule *m, double *values, PureBlock block, Even
Slot *top = sp ? &stack[sp - 1] : stack;
switch (op->op) {
case PURE_OP_CONST: stack[sp++].num = op->value; continue;
- case PURE_OP_LOAD: stack[sp++].num = values[arg]; continue;
+ case PURE_OP_LOAD: stack[sp++].i64 = values[arg].i64; continue; /* the bits, a number's or a bool's */
case PURE_OP_DT: stack[sp++].num = ev->dt; continue;
case PURE_OP_NOW: stack[sp++].num = app->now; continue;
case PURE_OP_FLOOR: top->num = floor(top->num); continue;
- case PURE_OP_STORE: case PURE_OP_CUR_STORE: values[arg] = stack[--sp].num; continue;
+ case PURE_OP_STORE: case PURE_OP_CUR_STORE: values[arg].i64 = stack[--sp].i64; continue;
case PURE_OP_JZ: if (stack[--sp].num == 0) pc = arg; continue;
case PURE_OP_JMP: pc = arg; continue;
case PURE_OP_NEG: top->num = -top->num; continue;
@@ -2307,7 +2308,7 @@ static PureResult run(const PureModule *m, double *values, PureBlock block, Even
}
/* Positions: a cursor or an index, with the text or list it is in. */
case PURE_OP_CUR_LOAD: case PURE_OP_INDEX_LOAD:
- stack[sp].num = values[arg];
+ stack[sp].num = values[arg].num;
stack[sp++].bytes.owner = m->state_ref[arg];
continue;
case PURE_OP_CUR_START: case PURE_OP_CUR_END: case PURE_OP_INDEX_FIRST: case PURE_OP_INDEX_LAST:
@@ -2360,7 +2361,7 @@ static PureResult run(const PureModule *m, double *values, PureBlock block, Even
if (status != PURE_OK) goto refused;
if (op->op == PURE_OP_TEXT_SET) {
for (uint32_t c = 0; c < m->states; ++c)
- if (m->state_type[c] == PURE_STATE_CURSOR && m->state_ref[c] == arg) values[c] = 0;
+ if (m->state_type[c] == PURE_STATE_CURSOR && m->state_ref[c] == arg) values[c].num = 0;
} else move_cursors(m, values, arg, at, 0, span.size);
continue;
}
@@ -2417,7 +2418,7 @@ static PureResult run(const PureModule *m, double *values, PureBlock block, Even
PureSpan span = {0};
if (text_field(m, arg, f) && (status = contiguous(app, arena, stack[base + f].bytes, &span)) != PURE_OK)
return status;
- cells[f] = (PureCell){stack[base + f].num, (unsigned char *)span.data, span.size};
+ cells[f] = (PureCell){.i64 = stack[base + f].i64, .text = (unsigned char *)span.data, .size = span.size};
}
if (m->state_key[arg]) { /* a keyed list places the item by its key */
bool found;
@@ -2460,7 +2461,8 @@ static PureResult run(const PureModule *m, double *values, PureBlock block, Even
status = text_field(m, list, field) ? contiguous(app, arena, value.bytes, &span) : PURE_OK;
if (status == PURE_OK)
status = pure_list_set(app->lists[list], at, field,
- (PureCell){value.num, (unsigned char *)span.data, span.size}, undo_for(app, list));
+ (PureCell){.i64 = value.i64, .text = (unsigned char *)span.data, .size = span.size},
+ undo_for(app, list));
arena->used = mark;
if (status != PURE_OK) goto refused;
continue;
@@ -2472,7 +2474,7 @@ static PureResult run(const PureModule *m, double *values, PureBlock block, Even
kind = PURE_FAULT_NOT_FOUND;
goto failed;
}
- values[arg] = position;
+ values[arg].num = position;
continue;
}
case PURE_OP_FOLD: {
@@ -2554,11 +2556,11 @@ static PureResult run(const PureModule *m, double *values, PureBlock block, Even
/* A keyed list's item by key, found by halving: FIND gives its index, HAS whether it is there. */
unsigned key = m->state_key[arg] - 1u;
size_t mark = arena->used;
- PureCell cell = {top->num, NULL, 0};
+ PureCell cell = {.i64 = top->i64};
if (text_field(m, arg, key)) {
PureSpan span;
if ((status = contiguous(app, arena, top->bytes, &span)) != PURE_OK) return status;
- cell = (PureCell){0, (unsigned char *)span.data, span.size};
+ cell = (PureCell){.text = (unsigned char *)span.data, .size = span.size};
}
bool found;
size_t at = pure_list_seek(app->lists[arg], key, cell, &found);
@@ -2760,7 +2762,7 @@ struct PureSnapshot {
unsigned refs;
const PureModule *module;
uint64_t changes, epoch;
- double values[PURE_MODULE_STATES];
+ PureValue values[PURE_MODULE_STATES];
PureDraft *texts[PURE_MODULE_STATES];
PureList *lists[PURE_MODULE_STATES];
PureList *runtime[PURE_LISTS_STORED - PURE_LIST_HEARD]; /* only in a checkpoint's of one */
@@ -2942,7 +2944,8 @@ size_t pure_app_state_bytes(const PureApp *app) {
* holding more than before, fails. */
static PureResult dispatch(PureApp *app, uint32_t event, Event *ev) {
const PureModule *m = app->module;
- double pending[PURE_MODULE_STATES], locals[PURE_MODULE_LOCALS] = {0};
+ PureValue pending[PURE_MODULE_STATES];
+ double locals[PURE_MODULE_LOCALS] = {0};
ev->locals = locals;
size_t held = app->memory ? pure_app_state_bytes(app) : 0;
Mark mark;
@@ -3019,7 +3022,7 @@ PureResult pure_app_point(PureApp *app, uint32_t state, size_t position) {
open_burst(app); /* what a failed save returns to */
app->fault = PURE_FAULT_NONE;
app->fault_pc = UINT32_MAX;
- app->values[state] = (double)position;
+ app->values[state].num = (double)position;
++app->events;
++app->changes;
return app->deferred ? PURE_OK : pure_app_save(app);
@@ -3255,7 +3258,7 @@ static bool argue(const PureApp *app, uint32_t view, Event *ev) {
}
static PureResult evaluate(PureApp *app, uint32_t view, size_t row, Slot *out) {
- double values[PURE_MODULE_STATES];
+ PureValue values[PURE_MODULE_STATES];
memcpy(values, app->values, sizeof(values));
app->view_arena.used = 0;
Event ev = {.app = app, .arena = &app->view_arena, .row = row};
@@ -3315,7 +3318,7 @@ static struct PureRows *filtered(PureApp *app, uint32_t table) {
if (!argue(app, table, &ev)) return NULL;
size_t from = 0, to = pure_list_count(list);
if (m->view_range[table]) /* items out of the range fail the filter: they are not read */
- range_bounds(m, (double *)app->values, &m->range[m->view_range[table] - 1], list,
+ range_bounds(m, app->values, &m->range[m->view_range[table] - 1], list,
m->state_key[m->view_list[table]] - 1u, &ev, &from, &to);
for (bool more = from < to && pure_list_run(list, from, &run); more && run.start < to; more = pure_list_next(list, &run)) {
Lanes keep;
@@ -3526,14 +3529,14 @@ PURE_COLD PureResult pure_list_migrate(PureList **out, const PureList *from, con
for (size_t at = 0; result == PURE_OK && at < pure_list_count(from); ++at) {
PureCell cells[PURE_RECORD_FIELDS];
for (unsigned f = 0; f < r->count; ++f) {
- uint32_t field = r->first + f, constant = (uint32_t)m->record_field_default[field];
+ uint32_t field = r->first + f, constant = (uint32_t)m->record_field_default[field].num;
cells[f] = source[f] >= 0 ? pure_list_get(from, at, (unsigned)source[f]) :
m->record_field_text[field] ?
- (PureCell){0, (unsigned char *)m->constant_data + m->constant_offset[constant],
- m->constant_length[constant]} :
- (PureCell){m->record_field_default[field], NULL, 0};
+ (PureCell){.text = (unsigned char *)m->constant_data + m->constant_offset[constant],
+ .size = m->constant_length[constant]} :
+ (PureCell){.i64 = m->record_field_default[field].i64};
if (m->record_field_bool[field])
- cells[f] = (PureCell){pure_as_bool(cells[f].num, m->record_field_default[field]), NULL, 0};
+ cells[f] = (PureCell){.num = pure_as_bool(cells[f].num, m->record_field_default[field].num)};
}
result = pure_list_insert(*out, at, cells, NULL);
}
@@ -3673,7 +3676,7 @@ PURE_COLD static PureResult remap_outputs(PureApp *app, const PureModule *old, c
PURE_COLD static PureResult open_module(PureApp *app, const PureModule *module) {
const PureModule *old = app->module;
bool stored = pure_save_enabled(app);
- double values[PURE_MODULE_STATES] = {0};
+ PureValue values[PURE_MODULE_STATES] = {0};
PureDraft *texts[PURE_MODULE_STATES] = {0}, *list_drafts[PURE_MODULE_STATES] = {0};
PureList *lists[PURE_MODULE_STATES] = {0};
PureVersion saved[PURE_MODULE_STATES] = {{0, 0}};
@@ -3720,7 +3723,7 @@ PURE_COLD static PureResult open_module(PureApp *app, const PureModule *module)
rewrite[i] = result == PURE_OK && listed[i].rewrite;
} else if (!(lists[i] = new_list(module, module->state_ref[i]))) result = PURE_ALLOCATION;
} else values[i] = kept[i] >= 0 ? app->values[kept[i]] : module->initial[i];
- if (module->state_type[i] == PURE_STATE_BOOL) values[i] = pure_as_bool(values[i], module->initial[i]);
+ if (module->state_type[i] == PURE_STATE_BOOL) values[i].num = pure_as_bool(values[i].num, module->initial[i].num);
}
/* A keyed list keeps its items in key order: one that was not keyed, or
* keyed by another field, is put in order and its indices follow their
@@ -3747,7 +3750,7 @@ PURE_COLD static PureResult open_module(PureApp *app, const PureModule *module)
if (module->state_type[i] != PURE_STATE_CURSOR && module->state_type[i] != PURE_STATE_INDEX) continue;
uint32_t owner = module->state_ref[i];
bool same = kept[i] >= 0 && kept[owner] >= 0 && old->state_ref[kept[i]] == (unsigned)kept[owner];
- values[i] = same ? app->values[kept[i]] : 0;
+ values[i] = same ? app->values[kept[i]] : (PureValue){0};
}
if (result == PURE_OK && stored) result = pure_save_open_values(app, module, values, kept, texts, lists);
/* Outputs keep their sends and numbers by name. */
@@ -3762,8 +3765,8 @@ PURE_COLD static PureResult open_module(PureApp *app, const PureModule *module)
sent.rewrite = sent.rewrite || (sent_moved && lists_stored);
for (uint32_t i = 0; i < module->states; ++i) {
uint32_t list = module->state_ref[i];
- if (module->state_type[i] == PURE_STATE_INDEX && moved[list] && values[i] < (double)pure_list_count(lists[list]))
- values[i] = (double)moved[list][(size_t)values[i]];
+ if (module->state_type[i] == PURE_STATE_INDEX && moved[list] && values[i].num < (double)pure_list_count(lists[list]))
+ values[i].num = (double)moved[list][(size_t)values[i].num];
}
for (uint32_t i = 0; i < module->states; ++i) free(moved[i]);
if (!old) pure_save_close_start(app);
diff --git a/core/module.h b/core/module.h
index 0634ed1..10fe6c3 100644
--- a/core/module.h
+++ b/core/module.h
@@ -115,6 +115,11 @@ typedef enum {
PURE_STATE_NUM = 0, PURE_STATE_TEXT = 1, PURE_STATE_CURSOR = 2, PURE_STATE_LIST = 3, PURE_STATE_INDEX = 4,
PURE_STATE_BOOL = 5
} PureStateType;
+/* A value's 8 bytes: a number (a bool's 0 or 1, a cursor's or an index's
+ * position too), or an integer. What a value is its state's or field's type
+ * says, and the verifier keeps to it: moves copy the bytes, and only what
+ * computes reads them as one or the other. */
+typedef union { double num; int64_t i64; } PureValue;
/* A cursor that stays before text inserted at it (a mark); other cursors go
* after it (a caret). A list of a message's rows (its state's flags hold
* PURE_LIST_ROWS beside the key byte; its name is the field's path): a
@@ -142,7 +147,7 @@ typedef enum {
* pass over its list (shape: its PureShape + 1), and a fold or a walk whose
* test bounds a keyed list's key reads only the items in bounds (range: its
* PureRange + 1). */
-typedef struct { double value; uint32_t arg; uint8_t op, plan, shape, range; } PureOp;
+typedef struct { union { double value; int64_t integer; }; uint32_t arg; uint8_t op, plan, shape, range; } PureOp;
typedef struct { uint32_t start, length; } PureBlock;
typedef struct { uint32_t event; PureBlock code; } PureHandler;
typedef struct { char name[PURE_MODULE_NAME]; uint8_t type; uint16_t rows; uint32_t offset; } PureField;
@@ -194,7 +199,7 @@ typedef struct {
uint8_t state_key[PURE_MODULE_STATES];
PureInputShape input[PURE_MODULE_INPUTS];
PureField field[PURE_MODULE_FIELDS];
- double initial[PURE_MODULE_STATES];
+ PureValue initial[PURE_MODULE_STATES];
uint8_t view_type[PURE_MODULE_VIEWS];
uint16_t view_table[PURE_MODULE_VIEWS]; /* a column's table view + 1, or 0 */
uint16_t view_list[PURE_MODULE_VIEWS]; /* the list of a table or column */
@@ -218,7 +223,7 @@ typedef struct {
char record_field_names[PURE_MODULE_RECORD_FIELDS][PURE_MODULE_NAME];
uint8_t record_field_text[PURE_MODULE_RECORD_FIELDS];
uint8_t record_field_bool[PURE_MODULE_RECORD_FIELDS]; /* a bool: a number field (0 or 1) as lists go */
- double record_field_default[PURE_MODULE_RECORD_FIELDS]; /* number (a bool's 0 or 1), or text constant index */
+ PureValue record_field_default[PURE_MODULE_RECORD_FIELDS]; /* a number (a bool's 0 or 1), or a text constant's index */
} PureModule;
/* Decode and verify into out, which must be zeroed (a static one is) or hold
@@ -313,7 +318,7 @@ enum { PURE_LIST_HEARD = PURE_MODULE_STATES, PURE_LIST_OUTBOX, PURE_LISTS_STORED
typedef struct {
const PureModule *module;
PureData data;
- double values[PURE_MODULE_STATES]; /* numbers, cursor byte positions and list indices */
+ PureValue values[PURE_MODULE_STATES]; /* numbers, bools, cursor byte positions and list indices */
PureDraft *texts[PURE_MODULE_STATES];
PureList *lists[PURE_MODULE_STATES];
PureUndo undo; /* list changes of the current event */
@@ -337,7 +342,7 @@ typedef struct {
/* Events since the last save: what a failed save returns to, and the
* documents messages opened (held until the save). */
bool deferred, open;
- double durable_values[PURE_MODULE_STATES];
+ PureValue durable_values[PURE_MODULE_STATES];
PureVersion durable_texts[PURE_MODULE_STATES];
size_t durable_lists[PURE_MODULE_STATES]; /* each stored list's journal length */
uint64_t durable_ticks, durable_events;
diff --git a/core/test-module.c b/core/test-module.c
index 0589877..131d9d9 100644
--- a/core/test-module.c
+++ b/core/test-module.c
@@ -240,14 +240,14 @@ static void behaviour(void) {
double value;
CHECK(pure_app_view(&app, 1, &value) && value == 40);
for (int i = 0; i < 25; ++i) CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK);
- CHECK(app.values[0] == 25 && pure_app_view(&app, 1, &value) && value == 200);
+ CHECK(app.values[0].num == 25 && pure_app_view(&app, 1, &value) && value == 200);
CHECK(pure_app_input(&app, 0, PURE_EDGE_UP) == PURE_OK && app.events == 26);
/* if / else with forward JZ and JMP */
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[1] == 4);
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[1] == 5);
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[1] == 6);
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[1] == 0);
- CHECK(pure_app_frame(&app) == PURE_OK && app.values[1] == 1.0 / 60.0 && app.ticks == 1);
+ CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[1].num == 4);
+ CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[1].num == 5);
+ CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[1].num == 6);
+ CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[1].num == 0);
+ CHECK(pure_app_frame(&app) == PURE_OK && app.values[1].num == 1.0 / 60.0 && app.ticks == 1);
CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_INVALID);
CHECK(pure_module_input(&module_a, "move") == 1 && pure_module_view(&module_a, "width") == 1 &&
pure_module_state(&module_a, "missing") == -1);
@@ -260,9 +260,9 @@ static void behaviour(void) {
static const O no_code[1];
CHECK(load((Spec){next_states, 2, no_inputs, 0, no_views, 0, no_handlers, 0, no_code, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0}, &module_b) == PURE_OK);
CHECK(pure_app_replace(&app, &module_b) == PURE_OK);
- CHECK(app.module == &module_b && app.values[0] == 7 && app.values[1] == 25);
+ CHECK(app.module == &module_b && app.values[0].num == 7 && app.values[1].num == 25);
CHECK(pure_app_replace(&app, &module_a) == PURE_OK);
- CHECK(app.values[0] == 25 && app.values[1] == 3);
+ CHECK(app.values[0].num == 25 && app.values[1].num == 3);
pure_app_stop(&app);
}
@@ -286,7 +286,7 @@ static void faults(void) {
const PureFault expected[] = {PURE_FAULT_DIVIDE, PURE_FAULT_NON_FINITE, PURE_FAULT_CLAMP};
for (uint32_t i = 0; i < 3; ++i) {
CHECK(pure_app_input(&app, i, PURE_EDGE_DOWN) == PURE_INVALID && app.fault == expected[i]);
- CHECK(app.values[0] == 1 && app.values[1] == 2 && app.events == 0);
+ CHECK(app.values[0].num == 1 && app.values[1].num == 2 && app.events == 0);
}
/* A later handler of the same event does not run after a fault. */
CHECK(app.fault_pc == 17);
@@ -403,7 +403,7 @@ static void mutations(void) {
if (e % 3 == 0) pure_app_frame(&app);
else pure_app_input(&app, e % 4, e & 1 ? PURE_EDGE_UP : PURE_EDGE_DOWN);
for (uint32_t v = 0; v < module_a.views; ++v) { double value; pure_app_view(&app, v, &value); }
- for (uint32_t s = 0; s < module_a.states; ++s) CHECK(isfinite(app.values[s]));
+ for (uint32_t s = 0; s < module_a.states; ++s) CHECK(isfinite(app.values[s].num));
}
pure_app_stop(&app);
}
@@ -592,7 +592,7 @@ static void messages(void) {
CHECK(pure_app_message(&app, 0, "doc", -1, (PureSpan){0}) == PURE_NOT_FOUND && app.revision == 0);
CHECK(pure_app_message(&app, 1, "doc", 0, edit_payload(buffer, 0, 0, "hello")) == PURE_OK);
- CHECK(app.revision == 1 && replied(&app, "hello") && app.values[0] == 1);
+ CHECK(app.revision == 1 && replied(&app, "hello") && app.values[0].num == 1);
CHECK(pure_app_message(&app, 0, "doc", -1, (PureSpan){0}) == PURE_OK && app.revision == 1 && replied(&app, "hello"));
/* stale base conflicts before bounds; bounds fail as limit; nothing changes */
CHECK(pure_app_message(&app, 1, "doc", 0, edit_payload(buffer, 99, 0, "x")) == PURE_CONFLICT && app.revision == 1);
@@ -602,12 +602,12 @@ static void messages(void) {
CHECK(pure_app_message(&app, 1, "doc", -1, edit_payload(buffer, 0, 0, "x")) == PURE_INVALID);
CHECK(pure_app_message(&app, 1, "bad/key", 1, edit_payload(buffer, 0, 0, "x")) == PURE_INVALID);
CHECK(pure_app_message(&app, 4, "doc", 1, (PureSpan){0}) == PURE_INVALID);
- CHECK(memory.docs[0].revision == 1 && memory.docs[0].size == 5 && app.values[0] == 1);
+ CHECK(memory.docs[0].revision == 1 && memory.docs[0].size == 5 && app.values[0].num == 1);
/* A busy commit changes neither the document nor the states. */
memory.busy = true;
CHECK(pure_app_message(&app, 1, "doc", 1, edit_payload(buffer, 5, 0, "!")) == PURE_BUSY);
memory.busy = false;
- CHECK(memory.docs[0].size == 5 && app.values[0] == 1 && app.events == 2);
+ CHECK(memory.docs[0].size == 5 && app.values[0].num == 1 && app.events == 2);
/* The size limit of the data host fails as limit. */
memory.limit = 6;
CHECK(pure_app_message(&app, 1, "doc", 1, edit_payload(buffer, 5, 0, "!!")) == PURE_LIMIT);
@@ -618,9 +618,9 @@ static void messages(void) {
CHECK(pure_app_message(&app, 0, "doc", -1, (PureSpan){0}) == PURE_OK && replied(&app, ">> hello"));
/* require: an edit that would exceed 8 bytes changes nothing */
CHECK(pure_app_message(&app, 3, "doc", 2, edit_payload(buffer, 8, 0, "!")) == PURE_LIMIT);
- CHECK(memory.docs[0].size == 8 && app.values[0] == 1);
+ CHECK(memory.docs[0].size == 8 && app.values[0].num == 1);
CHECK(pure_app_message(&app, 3, "doc", 2, edit_payload(buffer, 0, 3, "")) == PURE_OK && app.revision == 3);
- CHECK(app.values[0] == 2);
+ CHECK(app.values[0].num == 2);
/* Another key is its own document. */
CHECK(pure_app_message(&app, 1, "other", 0, edit_payload(buffer, 0, 0, "abc")) == PURE_OK && app.revision == 1);
pure_app_stop(&app);
@@ -702,7 +702,7 @@ static void message_mutations(void) {
}
} else pure_app_input(&app, input, e & 1 ? PURE_EDGE_UP : PURE_EDGE_DOWN);
pure_app_frame(&app);
- for (uint32_t s = 0; s < module_b.states; ++s) CHECK(isfinite(app.values[s]));
+ for (uint32_t s = 0; s < module_b.states; ++s) CHECK(isfinite(app.values[s].num));
}
pure_app_stop(&app);
CHECK(memory.opened == memory.closed);
@@ -799,41 +799,41 @@ static void texts(void) {
if (load(spec, &module_a) != PURE_OK) { fprintf(stderr, "%s\n", error); abort(); }
PureApp app;
CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK);
- CHECK(!strcmp(state_text(&app, 1), "\xea\xb0\x80" "a\nbc") && app.values[2] == 0);
+ CHECK(!strcmp(state_text(&app, 1), "\xea\xb0\x80" "a\nbc") && app.values[2].num == 0);
for (int i = 0; i < 6; ++i) CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK);
- CHECK(app.values[2] == 7); /* stops at the end */
+ CHECK(app.values[2].num == 7); /* stops at the end */
CHECK(number_view(&app, 0) == 5 && number_view(&app, 2) == 2 && !strcmp(view_text(&app, 3), "bc"));
- CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK && app.values[2] == 4); /* up keeps column 2 */
+ CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 4); /* up keeps column 2 */
const char *han = "\xed\x95\x9c";
CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)han, 3}) == PURE_OK);
- CHECK(!strcmp(state_text(&app, 1), "\xea\xb0\x80" "a\xed\x95\x9c\nbc") && app.values[2] == 7 && app.values[0] == 1);
- CHECK(pure_app_input(&app, 5, PURE_EDGE_DOWN) == PURE_OK && app.values[2] == 4); /* back over 3 bytes */
- CHECK(pure_app_input(&app, 5, PURE_EDGE_DOWN) == PURE_OK && app.values[2] == 3);
+ CHECK(!strcmp(state_text(&app, 1), "\xea\xb0\x80" "a\xed\x95\x9c\nbc") && app.values[2].num == 7 && app.values[0].num == 1);
+ CHECK(pure_app_input(&app, 5, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 4); /* back over 3 bytes */
+ CHECK(pure_app_input(&app, 5, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 3);
CHECK(!strcmp(state_text(&app, 1), "\xea\xb0\x80\nbc"));
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[2] == 0);
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[2] == 0); /* stops at the start */
- CHECK(pure_app_input(&app, 5, PURE_EDGE_DOWN) == PURE_OK && app.values[2] == 0); /* nothing before */
- CHECK(pure_app_input(&app, 4, PURE_EDGE_DOWN) == PURE_OK && app.values[2] == 4); /* down to column 0 */
+ CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 0);
+ CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 0); /* stops at the start */
+ CHECK(pure_app_input(&app, 5, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 0); /* nothing before */
+ CHECK(pure_app_input(&app, 4, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 4); /* down to column 0 */
/* A failing event restores the text, the cursor and the states. */
CHECK(pure_app_input(&app, 7, PURE_EDGE_DOWN) == PURE_INVALID && app.fault == PURE_FAULT_DIVIDE);
- CHECK(!strcmp(state_text(&app, 1), "\xea\xb0\x80\nbc") && app.values[2] == 4 && app.values[0] == 1);
+ CHECK(!strcmp(state_text(&app, 1), "\xea\xb0\x80\nbc") && app.values[2].num == 4 && app.values[0].num == 1);
CHECK(!strcmp(view_text(&app, 4), "\xeb\xa9\x94\xeb\xaa\xa8 \xc2\xb7 4\xec\x9e\x90"));
CHECK(!strcmp(view_text(&app, 5), "0.3333333333333333"));
CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)"\xff", 1}) == PURE_INVALID);
CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)"\xed\xa0\x80", 3}) == PURE_INVALID); /* surrogate */
CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)"\xf0\x9f\x98\x80", 4}) == PURE_OK);
- CHECK(app.values[2] == 8 && number_view(&app, 1) == 3); /* offset in characters */
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[2] == 4); /* back over the emoji */
- CHECK(pure_app_input(&app, 6, PURE_EDGE_DOWN) == PURE_OK && app.values[2] == 0);
+ CHECK(app.values[2].num == 8 && number_view(&app, 1) == 3); /* offset in characters */
+ CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 4); /* back over the emoji */
+ CHECK(pure_app_input(&app, 6, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 0);
/* Replacement keeps the text and its cursor by name. */
static const S fresh[] = {{"fresh", 7}};
static const T kept[] = {{"note", 1, 0}, {"at", 2, 1}};
static const char *const other[] = {"ignored"};
Spec next = {fresh, 1, inputs, 0, number_views, 0, handlers, 0, code, 0, NULL, 0, kept, 2, other, 1, text_views, 0, NULL, 0, NULL, 0};
CHECK(load(next, &module_b) == PURE_OK);
- CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK && app.values[2] == 3);
+ CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 3);
CHECK(pure_app_replace(&app, &module_b) == PURE_OK);
- CHECK(app.values[0] == 7 && app.values[2] == 3 && !strcmp(state_text(&app, 1), "\xea\xb0\x80\n\xf0\x9f\x98\x80" "bc"));
+ CHECK(app.values[0].num == 7 && app.values[2].num == 3 && !strcmp(state_text(&app, 1), "\xea\xb0\x80\n\xf0\x9f\x98\x80" "bc"));
pure_app_stop(&app);
/* Verification of text operations. */
@@ -902,8 +902,8 @@ static void text_mutations(void) {
for (uint32_t s = 0; s < module_b.states; ++s) {
if (module_b.state_type[s] == PURE_STATE_CURSOR) {
PureDraft *text = app.texts[module_b.state_ref[s]];
- size_t length = pure_draft_size(text), at = (size_t)app.values[s];
- CHECK(app.values[s] >= 0 && at <= length && app.values[s] == (double)at);
+ size_t length = pure_draft_size(text), at = (size_t)app.values[s].num;
+ CHECK(app.values[s].num >= 0 && at <= length && app.values[s].num == (double)at);
if (at < length) { /* never inside a multi-byte character */
Text t = {{0}, 0};
pure_draft_read(text, at, 1, text_span, &t);
@@ -919,7 +919,7 @@ static void text_mutations(void) {
pure_utf8_complete(utf8);
}
CHECK(ok);
- } else CHECK(isfinite(app.values[s]));
+ } else CHECK(isfinite(app.values[s].num));
}
}
pure_app_stop(&app);
@@ -1113,7 +1113,7 @@ static void list_trees(void) {
size_t at = model.count ? next_random(&seed) % (model.count + 1) : 0;
ModelItem item = {(double)(next_random(&seed) % 100000) - 50000, "", next_random(&seed) % 11};
for (size_t k = 0; k < item.size; ++k) item.text[k] = (char)('a' + next_random(&seed) % 26);
- PureCell cells[2] = {{item.key, NULL, 0}, {0, (unsigned char *)item.text, item.size}};
+ PureCell cells[2] = {{.num = item.key}, {.text = (unsigned char *)item.text, .size = item.size}};
CHECK(pure_list_insert(list, at, cells, &undo) == PURE_OK);
model_insert(&model, at, item);
} else if (roll < grow + 25) {
@@ -1125,13 +1125,13 @@ static void list_trees(void) {
size_t at = next_random(&seed) % model.count;
if (next_random(&seed) % 2) {
double key = (double)(next_random(&seed) % 1000);
- CHECK(pure_list_set(list, at, 0, (PureCell){key, NULL, 0}, &undo) == PURE_OK);
+ CHECK(pure_list_set(list, at, 0, (PureCell){.num = key}, &undo) == PURE_OK);
model.items[at].key = key;
} else {
ModelItem *item = &model.items[at];
item->size = next_random(&seed) % 11;
for (size_t k = 0; k < item->size; ++k) item->text[k] = (char)('A' + next_random(&seed) % 26);
- CHECK(pure_list_set(list, at, 1, (PureCell){0, (unsigned char *)item->text, item->size}, &undo) == PURE_OK);
+ CHECK(pure_list_set(list, at, 1, (PureCell){.text = (unsigned char *)item->text, .size = item->size}, &undo) == PURE_OK);
}
}
}
@@ -1216,8 +1216,8 @@ static void list_trees(void) {
CHECK(numbers && read_back);
pure_list_journal(numbers, true);
for (size_t k = 0; k < sizeof(edges) / sizeof(*edges); ++k) {
- CHECK(pure_list_insert(numbers, k, (PureCell[]){{edges[k], NULL, 0}}, NULL) == PURE_OK);
- CHECK(pure_list_set(numbers, k, 0, (PureCell){edges[k], NULL, 0}, NULL) == PURE_OK);
+ CHECK(pure_list_insert(numbers, k, (PureCell[]){{.num = edges[k]}}, NULL) == PURE_OK);
+ CHECK(pure_list_set(numbers, k, 0, (PureCell){.num = edges[k]}, NULL) == PURE_OK);
}
PureSpan edge_records = pure_list_changes(numbers);
CHECK(edge_records.size < sizeof(edges) / sizeof(*edges) * 2 * 14 && pure_list_replay(read_back, edge_records.data, edge_records.size) == PURE_OK);
@@ -1251,7 +1251,7 @@ static void list_trees(void) {
}
double key = (double)(next_random(&seed) % 20000);
bool found;
- size_t at = pure_list_seek(keyed, 0, (PureCell){key, NULL, 0}, &found);
+ size_t at = pure_list_seek(keyed, 0, (PureCell){.num = key}, &found);
size_t low = 0, high = keys.count;
while (low < high) {
size_t middle = (low + high) / 2;
@@ -1260,7 +1260,7 @@ static void list_trees(void) {
}
CHECK(at == low && found == (low < keys.count && keys.items[low].key == key));
if (!found) {
- PureCell cells[2] = {{key, NULL, 0}, {0, (unsigned char *)"k", 1}};
+ PureCell cells[2] = {{.num = key}, {.text = (unsigned char *)"k", .size = 1}};
if (round % 7 == 0) { /* an insert undone first: the guides as they were */
PureUndo undone_insert = {0};
CHECK(pure_list_insert(keyed, at, cells, &undone_insert) == PURE_OK);
@@ -1331,31 +1331,31 @@ static void lists(void) {
CHECK(app.fault == PURE_FAULT_NONE); /* views leave faults alone */
CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)"a", 1}) == PURE_OK);
CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)"b", 1}) == PURE_OK);
- CHECK(pure_list_count(app.lists[1]) == 2 && app.values[2] == 1 && app.values[0] == 2);
- CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK && app.values[2] == 0);
- CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK && app.values[2] == 0); /* stops at the first */
+ CHECK(pure_list_count(app.lists[1]) == 2 && app.values[2].num == 1 && app.values[0].num == 2);
+ CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 0);
+ CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 0); /* stops at the first */
/* Inserting before the current item: the index follows it. */
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[2] == 1);
+ CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 1);
CHECK(!strcmp(item_title(&app, 1, 0), "new") && !strcmp(view_text(&app, 3), "a"));
CHECK(pure_app_input(&app, 5, PURE_EDGE_DOWN) == PURE_OK && number_view(&app, 2) == 1);
CHECK(pure_app_input(&app, 6, PURE_EDGE_DOWN) == PURE_OK && !strcmp(view_text(&app, 3), "ax"));
- CHECK(pure_app_input(&app, 4, PURE_EDGE_DOWN) == PURE_OK && app.values[2] == 2);
- CHECK(pure_app_input(&app, 4, PURE_EDGE_DOWN) == PURE_OK && app.values[2] == 2); /* stops at the last */
+ CHECK(pure_app_input(&app, 4, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 2);
+ CHECK(pure_app_input(&app, 4, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 2); /* stops at the last */
/* Removing the last item: the index moves to the new last. */
- CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK && app.values[2] == 1 && pure_list_count(app.lists[1]) == 2);
- CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK && app.values[2] == 0);
+ CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 1 && pure_list_count(app.lists[1]) == 2);
+ CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 0);
/* Removing the current item: the index names the next. */
- CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK && app.values[2] == 0 && !strcmp(view_text(&app, 3), "ax"));
+ CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 0 && !strcmp(view_text(&app, 3), "ax"));
/* A failing event undoes a set, a remove and an append. */
CHECK(pure_app_input(&app, 7, PURE_EDGE_DOWN) == PURE_INVALID && app.fault == PURE_FAULT_DIVIDE);
CHECK(pure_list_count(app.lists[1]) == 1 && !strcmp(item_title(&app, 1, 0), "ax") &&
- pure_list_get(app.lists[1], 0, 1).num == 1 && app.values[2] == 0 && app.values[0] == 2);
- CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK && pure_list_count(app.lists[1]) == 0 && app.values[2] == 0);
+ pure_list_get(app.lists[1], 0, 1).num == 1 && app.values[2].num == 0 && app.values[0].num == 2);
+ CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK && pure_list_count(app.lists[1]) == 0 && app.values[2].num == 0);
CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_NOT_FOUND && app.fault == PURE_FAULT_NOT_FOUND);
CHECK(pure_app_input(&app, 5, PURE_EDGE_DOWN) == PURE_NOT_FOUND);
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && pure_list_count(app.lists[1]) == 1 && app.values[2] == 0);
+ CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && pure_list_count(app.lists[1]) == 1 && app.values[2].num == 0);
for (int i = 0; i < 3; ++i) CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)"\xea\xb0\x80", 3}) == PURE_OK);
- CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK && app.values[2] == 2);
+ CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 2);
CHECK(pure_app_input(&app, 5, PURE_EDGE_DOWN) == PURE_OK);
/* Replacement keeps the list by name. The same record keeps the list
@@ -1366,13 +1366,13 @@ static void lists(void) {
text_views, 0, records, 1, NULL, 0};
CHECK(load(same, &module_b) == PURE_OK);
const PureList *before = app.lists[1];
- CHECK(pure_app_replace(&app, &module_b) == PURE_OK && app.lists[1] == before && app.values[2] == 2);
+ CHECK(pure_app_replace(&app, &module_b) == PURE_OK && app.lists[1] == before && app.values[2].num == 2);
static const RF changed_fields[] = {{"done", 0, 0}, {"note", 1, 1}, {"title", 0, 7}, {"rank", 0, 5}};
static const R changed[] = {{"task", changed_fields, 4}};
Spec next = same;
next.r = changed;
CHECK(load(next, &module_a) == PURE_OK);
- CHECK(pure_app_replace(&app, &module_a) == PURE_OK && pure_list_count(app.lists[1]) == 4 && app.values[2] == 2);
+ CHECK(pure_app_replace(&app, &module_a) == PURE_OK && pure_list_count(app.lists[1]) == 4 && app.values[2].num == 2);
PureCell cell = pure_list_get(app.lists[1], 2, 0);
CHECK(cell.num == 1); /* done kept by name */
cell = pure_list_get(app.lists[1], 2, 1);
@@ -1466,8 +1466,8 @@ static void list_mutations(void) {
for (uint32_t s = 0; s < module_b.states; ++s) {
if (module_b.state_type[s] == PURE_STATE_INDEX) {
size_t count = pure_list_count(app.lists[module_b.state_ref[s]]);
- CHECK(app.values[s] == floor(app.values[s]) && app.values[s] >= 0 &&
- app.values[s] <= (count ? (double)(count - 1) : 0));
+ CHECK(app.values[s].num == floor(app.values[s].num) && app.values[s].num >= 0 &&
+ app.values[s].num <= (count ? (double)(count - 1) : 0));
} else if (module_b.state_type[s] == PURE_STATE_LIST) {
const PureRecord *record = &module_b.record[module_b.state_ref[s]];
for (size_t at = 0; at < pure_list_count(app.lists[s]); ++at)
@@ -1477,7 +1477,7 @@ static void list_mutations(void) {
CHECK(pure_utf8_valid((PureSpan){cell.text, cell.size}));
else CHECK(isfinite(cell.num));
}
- } else if (module_b.state_type[s] == PURE_STATE_NUM) CHECK(isfinite(app.values[s]));
+ } else if (module_b.state_type[s] == PURE_STATE_NUM) CHECK(isfinite(app.values[s].num));
}
}
pure_app_stop(&app);
@@ -1709,7 +1709,7 @@ static void same_states(const PureApp *a, const PureApp *b) {
PureCell x = pure_list_get(a->lists[i], at, f), y = pure_list_get(b->lists[i], at, f);
CHECK(!memcmp(&x.num, &y.num, sizeof(x.num)) && x.size == y.size && (!x.size || !memcmp(x.text, y.text, x.size)));
}
- } else CHECK(!memcmp(&a->values[i], &b->values[i], sizeof(double)));
+ } else CHECK(!memcmp(&a->values[i].num, &b->values[i].num, sizeof(double)));
}
}
@@ -1768,9 +1768,9 @@ static const O crash_code[] = {
static const H crash_handlers[] = {{1, 0, 12}};
static unsigned consistent(const PureApp *app) {
- unsigned n = (unsigned)app->values[0];
- CHECK(app->values[0] == n && pure_draft_size(app->texts[NOTE]) == n && app->values[2] == n);
- CHECK(pure_list_count(app->lists[TASKS]) == n && app->values[4] == (n ? n - 1 : 0));
+ unsigned n = (unsigned)app->values[0].num;
+ CHECK(app->values[0].num == n && pure_draft_size(app->texts[NOTE]) == n && app->values[2].num == n);
+ CHECK(pure_list_count(app->lists[TASKS]) == n && app->values[4].num == (n ? n - 1 : 0));
for (unsigned k = 0; k < n; ++k) CHECK(pure_list_get(app->lists[TASKS], k, 1).num == k + 1);
return n;
}
@@ -1876,15 +1876,15 @@ static void uncertain_numbers(void) {
PureApp app;
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK);
fail_stage = PURE_BEFORE_DIR_SYNC;
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_UNCERTAIN && app.uncertain && app.values[0] == 0);
+ CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_UNCERTAIN && app.uncertain && app.values[0].num == 0);
fail_stage = -1;
CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_CONFLICT && !pure_list_count(app.lists[1]));
pure_app_stop(&app);
- CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[0] == 1 &&
+ CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[0].num == 1 &&
!pure_list_count(app.lists[1]));
CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && pure_list_get(app.lists[1], 0, 0).num == 1);
pure_app_stop(&app);
- CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[0] == 1 &&
+ CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[0].num == 1 &&
pure_list_count(app.lists[1]) == 1 && pure_list_get(app.lists[1], 0, 0).num == 1);
pure_app_stop(&app);
remove_store(path);
@@ -1903,15 +1903,15 @@ static void stored_frames(void) {
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK);
for (int i = 0; i < 5; ++i) CHECK(pure_app_frame(&app) == PURE_OK);
pure_app_stop(&app);
- CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[0] == 0);
+ CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[0].num == 0);
for (int i = 0; i < 3; ++i) CHECK(pure_app_frame(&app) == PURE_OK);
CHECK(pure_app_save(&app) == PURE_OK);
pure_app_stop(&app);
- CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[0] == 3);
+ CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[0].num == 3);
for (int i = 0; i < 2; ++i) CHECK(pure_app_frame(&app) == PURE_OK);
CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK);
pure_app_stop(&app);
- CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[0] == 5);
+ CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[0].num == 5);
pure_app_stop(&app);
remove_store(path);
}
@@ -1930,13 +1930,13 @@ static void stored_messages(void) {
unsigned char buffer[64];
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK);
CHECK(pure_app_message(&app, 1, "doc", 0, edit_payload(buffer, 0, 0, "hello")) == PURE_OK && app.revision == 1);
- CHECK(app.values[0] == 1);
- CHECK(pure_app_message(&app, 1, "doc", 0, edit_payload(buffer, 0, 0, "x")) == PURE_CONFLICT && app.values[0] == 1);
+ CHECK(app.values[0].num == 1);
+ CHECK(pure_app_message(&app, 1, "doc", 0, edit_payload(buffer, 0, 0, "x")) == PURE_CONFLICT && app.values[0].num == 1);
/* An edit past the size guard fails: neither the document nor the count moves. */
- CHECK(pure_app_message(&app, 3, "doc", 1, edit_payload(buffer, 5, 0, "-world")) == PURE_LIMIT && app.values[0] == 1);
- CHECK(pure_app_message(&app, 3, "doc", 1, edit_payload(buffer, 5, 0, "!")) == PURE_OK && app.values[0] == 2);
+ CHECK(pure_app_message(&app, 3, "doc", 1, edit_payload(buffer, 5, 0, "-world")) == PURE_LIMIT && app.values[0].num == 1);
+ CHECK(pure_app_message(&app, 3, "doc", 1, edit_payload(buffer, 5, 0, "!")) == PURE_OK && app.values[0].num == 2);
pure_app_stop(&app);
- CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[0] == 2);
+ CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[0].num == 2);
CHECK(pure_app_message(&app, 0, "doc", -1, (PureSpan){0}) == PURE_OK && app.revision == 2 && replied(&app, "hello!"));
pure_app_stop(&app);
/* Documents stay documents: the app's states are not among the keys. */
@@ -1968,7 +1968,7 @@ static void stored_migration(void) {
PureCell note = pure_list_get(app.lists[TASKS], 1, 1), title = pure_list_get(app.lists[TASKS], 1, 2);
CHECK(pure_list_get(app.lists[TASKS], 1, 0).num == 2 && pure_list_get(app.lists[TASKS], 1, 3).num == 5);
CHECK(note.size == 1 && note.text[0] == 'x' && title.size == 1 && title.text[0] == 'x');
- CHECK(app.values[0] == 2 && app.values[4] == 1 && pure_draft_size(app.texts[NOTE]) == 2);
+ CHECK(app.values[0].num == 2 && app.values[4].num == 1 && pure_draft_size(app.texts[NOTE]) == 2);
pure_app_stop(&app);
}
/* Back to the first record by replacing the module; the next save stores it. */
@@ -1998,7 +1998,7 @@ static void stored_migration(void) {
CHECK(pure_draft_splice(values, 0, pure_draft_size(values), (PureSpan){record, at}) == PURE_OK);
CHECK(pure_draft_commit(values, &revision) == PURE_OK);
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK);
- CHECK(app.values[0] == 9 && app.values[2] == 0 && app.values[4] == 0);
+ CHECK(app.values[0].num == 9 && app.values[2].num == 0 && app.values[4].num == 0);
pure_app_stop(&app);
/* Damaged stored lists (their images) and texts are refused, not replaced. */
char image[128];
@@ -2019,7 +2019,7 @@ static void stored_migration(void) {
CHECK(pure_draft_splice(draft, 0, 1, (PureSpan){0}) == PURE_OK && pure_draft_commit(draft, &revision) == PURE_OK);
pure_draft_close(draft);
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK);
- CHECK(pure_list_count(app.lists[TASKS]) == 3 && pure_draft_size(app.texts[NOTE]) == 3 && app.values[0] == 9);
+ CHECK(pure_list_count(app.lists[TASKS]) == 3 && pure_draft_size(app.texts[NOTE]) == 3 && app.values[0].num == 9);
pure_app_stop(&app);
pure_draft_close(values);
remove_store(path);
@@ -2150,10 +2150,10 @@ static void deferred_messages(void) {
snprintf(key, sizeof(key), "k%d", i);
CHECK(pure_app_message(&app, 1, key, 0, edit_payload(buffer, 0, 0, "ab")) == PURE_OK && app.revision == 1);
}
- CHECK(app.values[0] == 3 && app.pending_count == 3 && app.open);
+ CHECK(app.values[0].num == 3 && app.pending_count == 3 && app.open);
/* The same key again: the burst is saved first, then this edit joins a new one. */
CHECK(pure_app_message(&app, 1, "k0", 1, edit_payload(buffer, 2, 0, "c")) == PURE_OK && app.revision == 2);
- CHECK(app.pending_count == 1 && app.values[0] == 4);
+ CHECK(app.pending_count == 1 && app.values[0].num == 4);
CHECK(pure_app_message(&app, 1, "k1", 0, edit_payload(buffer, 0, 0, "x")) == PURE_CONFLICT);
CHECK(pure_app_save(&app) == PURE_OK && !app.open && !app.pending_count);
for (int i = 0; i < PURE_APP_PENDING + 1; ++i) {
@@ -2162,7 +2162,7 @@ static void deferred_messages(void) {
CHECK(app.pending_count == (i < PURE_APP_PENDING ? (unsigned)i + 1 : 1)); /* full: saved on the way */
}
pure_app_stop(&app); /* the last edit was never saved */
- CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[0] == 4 + PURE_APP_PENDING);
+ CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[0].num == 4 + PURE_APP_PENDING);
CHECK(pure_app_message(&app, 0, "k0", -1, (PureSpan){0}) == PURE_OK && app.revision == 2 && replied(&app, "abc"));
snprintf(key, sizeof(key), "m%d", PURE_APP_PENDING);
CHECK(pure_app_message(&app, 0, key, -1, (PureSpan){0}) == PURE_NOT_FOUND);
@@ -2234,12 +2234,12 @@ static void marks(void) {
PureApp app;
CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK);
CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)"x", 1}) == PURE_OK);
- CHECK(!strcmp(state_text(&app, 0), "xab") && app.values[1] == 1 && app.values[2] == 0); /* the mark stayed */
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[2] == 1);
+ CHECK(!strcmp(state_text(&app, 0), "xab") && app.values[1].num == 1 && app.values[2].num == 0); /* the mark stayed */
+ CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 1);
CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)"yz", 2}) == PURE_OK);
- CHECK(!strcmp(state_text(&app, 0), "xyzab") && app.values[1] == 3 && app.values[2] == 1);
- CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK && app.values[1] == 2 && app.values[2] == 1);
- CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK && app.values[1] == 1 && app.values[2] == 1); /* gathered */
+ CHECK(!strcmp(state_text(&app, 0), "xyzab") && app.values[1].num == 3 && app.values[2].num == 1);
+ CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK && app.values[1].num == 2 && app.values[2].num == 1);
+ CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK && app.values[1].num == 1 && app.values[2].num == 1); /* gathered */
pure_app_stop(&app);
/* A flag only on a cursor, and only this one. */
static const T wrong_flag[] = {{"note", 1, 0}, {"at", 2 | 2 << 8, 0}, {"mark", MARK, 0}};
@@ -2666,9 +2666,9 @@ static void lane_folds(void) {
for (size_t i = 0; i < l_items + m_items; ++i) {
bool in_l = i < l_items;
double flag = roll(&g, 2);
- PureCell cells_[4] = {{in_l ? 2.0 * (double)i : values[roll(&g, LEN(values))], NULL, 0},
- {values[roll(&g, LEN(values))], NULL, 0},
- {in_l ? values[roll(&g, LEN(values))] : flag, NULL, 0}, {flag, NULL, 0}};
+ PureCell cells_[4] = {{.num = in_l ? 2.0 * (double)i : values[roll(&g, LEN(values))]},
+ {.num = values[roll(&g, LEN(values))]},
+ {.num = in_l ? values[roll(&g, LEN(values))] : flag}, {.num = flag}};
PureList *list_a = a.lists[in_l ? LANE_L : LANE_M], *list_b = b.lists[in_l ? LANE_L : LANE_M];
CHECK(pure_list_insert(list_a, pure_list_count(list_a), cells_, NULL) == PURE_OK &&
pure_list_insert(list_b, pure_list_count(list_b), cells_, NULL) == PURE_OK);
@@ -2774,7 +2774,7 @@ typedef struct { StockItem stock[64]; size_t stocked; double log[4096][2]; size_
static bool same_shop(const PureApp *app, const Shop *shop) {
const PureList *stock = app->lists[W_STOCK], *log = app->lists[W_LOG];
if (pure_list_count(stock) != shop->stocked || pure_list_count(log) != shop->logged ||
- pure_list_count(app->lists[W_LINES]) || app->values[W_N] != shop->n || app->values[W_SUM] != shop->sum)
+ pure_list_count(app->lists[W_LINES]) || app->values[W_N].num != shop->n || app->values[W_SUM].num != shop->sum)
return false;
for (size_t i = 0; i < shop->stocked; ++i)
if (pure_list_get(stock, i, 0).num != shop->stock[i].item || pure_list_get(stock, i, 1).num != shop->stock[i].left)
@@ -3058,7 +3058,7 @@ static void view_arguments(void) {
}
}
put32(four, 2);
- CHECK(pure_app_message(&app, 0, NULL, -1, (PureSpan){four, 4}) == PURE_OK && app.values[A_N] == 10);
+ CHECK(pure_app_message(&app, 0, NULL, -1, (PureSpan){four, 4}) == PURE_OK && app.values[A_N].num == 10);
pure_app_stop(&app);
/* What arguments may not be. */
@@ -3134,7 +3134,7 @@ static void locals(void) {
double total = 1, below = 0;
for (size_t i = 0; i < count; ++i) total += items[i];
for (size_t i = 0; i < count; ++i) below += items[i] < total;
- CHECK(app.values[L_N] == below && app.values[L_M] == total + items[count - 1]);
+ CHECK(app.values[L_N].num == below && app.values[L_M].num == total + items[count - 1]);
}
pure_app_stop(&app);
@@ -3250,8 +3250,8 @@ static void ranges(void) {
double key = (double)(next_random(&seed) % 5);
for (size_t i = 0, items = next_random(&seed) % 400; i < items; ++i) {
key += 1 + next_random(&seed) % 3;
- PureCell cells[3] = {{key, NULL, 0}, {(double)(next_random(&seed) % 9) - 3, NULL, 0},
- {(double)(next_random(&seed) % 7) - 3, NULL, 0}};
+ PureCell cells[3] = {{.num = key}, {.num = (double)(next_random(&seed) % 9) - 3},
+ {.num = (double)(next_random(&seed) % 7) - 3}};
CHECK(pure_list_insert(a.lists[R_L], i, cells, NULL) == PURE_OK && pure_list_insert(b.lists[R_L], i, cells, NULL) == PURE_OK);
}
for (unsigned step = 0; step < 12; ++step) {
@@ -3354,16 +3354,16 @@ static void pointing(void) {
CHECK(pure_app_point(&app, 2, 0) == PURE_NOT_FOUND); /* nothing to point at */
for (int i = 0; i < 3; ++i) CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)"a", 1}) == PURE_OK);
uint64_t events = app.events;
- CHECK(app.values[2] == 2 && pure_app_point(&app, 2, 0) == PURE_OK && app.values[2] == 0 && app.events == events + 1);
- CHECK(pure_app_point(&app, 2, 3) == PURE_NOT_FOUND && app.values[2] == 0);
+ CHECK(app.values[2].num == 2 && pure_app_point(&app, 2, 0) == PURE_OK && app.values[2].num == 0 && app.events == events + 1);
+ CHECK(pure_app_point(&app, 2, 3) == PURE_NOT_FOUND && app.values[2].num == 0);
CHECK(pure_app_point(&app, 0, 1) == PURE_INVALID && pure_app_point(&app, 1, 1) == PURE_INVALID); /* a number, a list */
CHECK(pure_app_point(&app, 2, 1) == PURE_OK);
pure_app_stop(&app);
- CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[2] == 1);
- CHECK(pure_app_defer(&app, true) && pure_app_point(&app, 2, 2) == PURE_OK && app.open && app.values[2] == 2);
+ CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[2].num == 1);
+ CHECK(pure_app_defer(&app, true) && pure_app_point(&app, 2, 2) == PURE_OK && app.open && app.values[2].num == 2);
CHECK(pure_app_save(&app) == PURE_OK);
pure_app_stop(&app);
- CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[2] == 2);
+ CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[2].num == 2);
pure_app_stop(&app);
remove_store(path);
}
@@ -3442,23 +3442,23 @@ static void keyed(void) {
CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK);
for (const char *const *word = (const char *const[]){"m", "b", "z", "a", NULL}; *word; ++word)
CHECK(type_text(&app, 0, *word) == PURE_OK);
- CHECK(!strcmp(names_of(&app, PEOPLE), "a,b,m,z") && app.values[K_AT] == 0 && !strcmp(view_text(&app, 5), "a"));
+ CHECK(!strcmp(names_of(&app, PEOPLE), "a,b,m,z") && app.values[K_AT].num == 0 && !strcmp(view_text(&app, 5), "a"));
/* A key once: the add fails, and so does an event whose second add repeats its first. */
CHECK(type_text(&app, 0, "b") == PURE_CONFLICT && app.fault == PURE_FAULT_EXISTS);
- CHECK(type_text(&app, 8, "c") == PURE_CONFLICT && !strcmp(names_of(&app, PEOPLE), "a,b,m,z") && app.values[K_AT] == 0);
- CHECK(type_text(&app, 1, "z") == PURE_OK && app.values[K_AT] == 3 && !strcmp(view_text(&app, 5), "z"));
- CHECK(type_text(&app, 1, "q") == PURE_NOT_FOUND && app.fault == PURE_FAULT_NOT_FOUND && app.values[K_AT] == 3);
- CHECK(type_text(&app, 2, "m") == PURE_OK && app.values[K_FOUND] == 1);
- CHECK(type_text(&app, 2, "c") == PURE_OK && app.values[K_FOUND] == 0);
+ CHECK(type_text(&app, 8, "c") == PURE_CONFLICT && !strcmp(names_of(&app, PEOPLE), "a,b,m,z") && app.values[K_AT].num == 0);
+ CHECK(type_text(&app, 1, "z") == PURE_OK && app.values[K_AT].num == 3 && !strcmp(view_text(&app, 5), "z"));
+ CHECK(type_text(&app, 1, "q") == PURE_NOT_FOUND && app.fault == PURE_FAULT_NOT_FOUND && app.values[K_AT].num == 3);
+ CHECK(type_text(&app, 2, "m") == PURE_OK && app.values[K_FOUND].num == 1);
+ CHECK(type_text(&app, 2, "c") == PURE_OK && app.values[K_FOUND].num == 0);
CHECK(pure_app_input(&app, 4, PURE_EDGE_DOWN) == PURE_OK && pure_list_get(app.lists[PEOPLE], 3, 1).num == 1);
CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK && !strcmp(names_of(&app, PEOPLE), "a,b,m"));
- CHECK(app.values[K_AT] == 2 && number_view(&app, 2) == 0 && number_view(&app, 3) == 0); /* "m": not bob, not before m */
+ CHECK(app.values[K_AT].num == 2 && number_view(&app, 2) == 0 && number_view(&app, 3) == 0); /* "m": not bob, not before m */
CHECK(type_text(&app, 1, "b") == PURE_OK && number_view(&app, 2) == 0 && number_view(&app, 3) == 1);
CHECK(type_text(&app, 0, "bob") == PURE_OK && number_view(&app, 2) == 1 && number_view(&app, 3) == 1);
/* Bytes order texts: a text before the longer ones it begins, UTF-8 after ASCII, the empty text first. */
CHECK(type_text(&app, 0, "\xea\xb0\x80") == PURE_OK && type_text(&app, 0, "ab") == PURE_OK);
CHECK(type_text(&app, 0, "") == PURE_OK && type_text(&app, 0, "") == PURE_CONFLICT);
- CHECK(!strcmp(names_of(&app, PEOPLE), ",a,ab,b,bob,m,\xea\xb0\x80") && app.values[K_AT] == 0);
+ CHECK(!strcmp(names_of(&app, PEOPLE), ",a,ab,b,bob,m,\xea\xb0\x80") && app.values[K_AT].num == 0);
/* Numbers order by value, and -0 is the key 0. */
CHECK(pure_app_input(&app, 5, PURE_EDGE_DOWN) == PURE_OK && pure_app_input(&app, 6, PURE_EDGE_DOWN) == PURE_OK);
CHECK(pure_app_input(&app, 7, PURE_EDGE_DOWN) == PURE_CONFLICT && number_view(&app, 4) == 2);
@@ -3468,11 +3468,11 @@ static void keyed(void) {
/* Replacing: unkeyed, the list stays as it is and takes appends at its end. */
CHECK(load(keyed_spec(unkeyed_states, unkeyed_code, LEN(unkeyed_code), unkeyed_handlers, 1), &module_b) == PURE_OK);
CHECK(pure_app_replace(&app, &module_b) == PURE_OK && type_text(&app, 0, "c") == PURE_OK);
- CHECK(!strcmp(names_of(&app, PEOPLE), ",a,ab,b,bob,m,\xea\xb0\x80,c") && app.values[K_AT] == 7);
+ CHECK(!strcmp(names_of(&app, PEOPLE), ",a,ab,b,bob,m,\xea\xb0\x80,c") && app.values[K_AT].num == 7);
CHECK(pure_app_point(&app, K_AT, 6) == PURE_OK); /* at the Korean name */
/* Keyed again: put in order, the index following its item. */
CHECK(pure_app_replace(&app, &module_a) == PURE_OK && !strcmp(names_of(&app, PEOPLE), ",a,ab,b,bob,c,m,\xea\xb0\x80"));
- CHECK(app.values[K_AT] == 7 && !strcmp(view_text(&app, 5), "\xea\xb0\x80"));
+ CHECK(app.values[K_AT].num == 7 && !strcmp(view_text(&app, 5), "\xea\xb0\x80"));
/* Two items with one key cannot become keyed: the app keeps its module and items. */
CHECK(pure_app_replace(&app, &module_b) == PURE_OK && type_text(&app, 0, "a") == PURE_OK);
CHECK(pure_app_replace(&app, &module_a) == PURE_CONFLICT && app.module == &module_b && pure_list_count(app.lists[PEOPLE]) == 9);
@@ -3487,7 +3487,7 @@ static void keyed(void) {
pure_app_stop(&app);
for (int round = 0; round < 2; ++round) {
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK);
- CHECK(!strcmp(names_of(&app, PEOPLE), "b,m,z") && app.values[K_AT] == 0);
+ CHECK(!strcmp(names_of(&app, PEOPLE), "b,m,z") && app.values[K_AT].num == 0);
pure_app_stop(&app);
}
CHECK(pure_app_start(&app, &module_b, pure_tree_data(path)) == PURE_OK && type_text(&app, 0, "m") == PURE_OK);
@@ -3562,8 +3562,8 @@ static void keyed_mutations(void) {
for (uint32_t s = 0; s < module_b.states; ++s) {
if (module_b.state_type[s] == PURE_STATE_INDEX) {
size_t count = pure_list_count(app.lists[module_b.state_ref[s]]);
- CHECK(app.values[s] == floor(app.values[s]) && app.values[s] >= 0 &&
- app.values[s] <= (count ? (double)(count - 1) : 0));
+ CHECK(app.values[s].num == floor(app.values[s].num) && app.values[s].num >= 0 &&
+ app.values[s].num <= (count ? (double)(count - 1) : 0));
}
if (module_b.state_type[s] != PURE_STATE_LIST || !module_b.state_key[s]) continue;
unsigned key = module_b.state_key[s] - 1u;
@@ -3615,7 +3615,7 @@ static void describe(PureApp *app, Shown *s) {
else show(s, "%g", cell.num);
show(s, f + 1 < r->count ? "," : ";", 0);
}
- } else show(s, "%g", app->values[i]);
+ } else show(s, "%g", app->values[i].num);
show(s, "|", 0);
}
for (uint32_t v = 0; v < m->views; ++v) {
@@ -3888,7 +3888,7 @@ static void message_fields(void) {
memcpy(payload + 16, "\xea\xb0\x80\xeb\x82\x98" "xyz", 9);
size = 25;
CHECK(pure_app_message(&app, 0, NULL, -1, (PureSpan){payload, size}) == PURE_OK);
- CHECK(app.values[0] == 5.5 && !strcmp(state_text(&app, 1), "\xea\xb0\x80\xeb\x82\x98") && replied(&app, "xyz"));
+ CHECK(app.values[0].num == 5.5 && !strcmp(state_text(&app, 1), "\xea\xb0\x80\xeb\x82\x98") && replied(&app, "xyz"));
pure_app_finish(&app);
/* Empty variable fields. */
title_size = 0;
@@ -3909,7 +3909,7 @@ static void message_fields(void) {
memcpy(payload + 4, &points, 8);
CHECK(pure_app_message(&app, 0, NULL, -1, (PureSpan){payload, 16}) == PURE_INVALID && app.fault == PURE_FAULT_NON_FINITE);
CHECK(pure_app_message(&app, 0, NULL, -1, (PureSpan){payload, 15}) == PURE_LIMIT);
- CHECK(app.values[0] == 5.5 && !*state_text(&app, 1)); /* failed messages changed nothing */
+ CHECK(app.values[0].num == 5.5 && !*state_text(&app, 1)); /* failed messages changed nothing */
pure_app_stop(&app);
/* A text field is text and a bytes field bytes. */
static const O text_as_bytes[] = {{FIELD, 0, 0}, {LEN_, 0, 0}, {ST, 0, 0}};
@@ -3935,7 +3935,7 @@ static void message_fields(void) {
size = 16 + sizeof(body) - 1;
uint32_t placed[8] = {16 + 23, 6, 0, 0, 16 + 9, 3, 0, 0};
CHECK(pure_app_message_fields(&app, 0, NULL, -1, (PureSpan){payload, size}, placed) == PURE_OK);
- CHECK(app.values[0] == 5.5 && !strcmp(state_text(&app, 1), "\xea\xb0\x80\xeb\x82\x98") && replied(&app, "xyz"));
+ CHECK(app.values[0].num == 5.5 && !strcmp(state_text(&app, 1), "\xea\xb0\x80\xeb\x82\x98") && replied(&app, "xyz"));
pure_app_finish(&app);
/* A field that starts or ends past the payload, or a title that is not UTF-8 there. */
placed[0] = (uint32_t)size + 1;
@@ -3947,7 +3947,7 @@ static void message_fields(void) {
placed[1] = 2;
CHECK(pure_app_message_fields(&app, 0, NULL, -1, (PureSpan){payload, size}, placed) == PURE_INVALID &&
app.fault == PURE_FAULT_ENCODING);
- CHECK(app.values[0] == 5.5);
+ CHECK(app.values[0].num == 5.5);
pure_app_stop(&app);
/* Fields in groups: a name is a path of identifiers (user.name), and
@@ -4059,18 +4059,18 @@ static void links(void) {
CHECK(pure_app_start(&a, &module_a, (PureData){0}) == PURE_OK && pure_app_start(&b, &module_b, (PureData){0}) == PURE_OK);
CHECK(buy(&a, 0, "pen", 2) == PURE_OK && buy(&a, 1, "ink", 3) == PURE_LIMIT);
Outbox o = outbox_of(&a);
- CHECK(o.count == 1 && o.items[0].output == 2 && o.items[0].seq == 1 && o.items[0].size == 15 && a.values[0] == 1);
+ CHECK(o.count == 1 && o.items[0].output == 2 && o.items[0].seq == 1 && o.items[0].size == 15 && a.values[0].num == 1);
PureSpan sent = {o.items[0].payload, o.items[0].size};
- CHECK(pure_app_deliver(&b, 0, "orders.ordered", 1, sent, NULL) == PURE_OK && !b.duplicate && b.values[0] == 2 &&
+ CHECK(pure_app_deliver(&b, 0, "orders.ordered", 1, sent, NULL) == PURE_OK && !b.duplicate && b.values[0].num == 2 &&
!strcmp(state_text(&b, 1), "pen"));
pure_app_finish(&b);
- CHECK(pure_app_deliver(&b, 0, "orders.ordered", 1, sent, NULL) == PURE_OK && b.duplicate && b.values[0] == 2);
- CHECK(deliver(&b, "other", 1, "cup", 5) == PURE_OK && !b.duplicate && b.values[0] == 7);
- CHECK(deliver(&b, "orders.ordered", 3, "hat", 1) == PURE_OK && b.values[0] == 8); /* numbers may skip */
- CHECK(deliver(&b, "orders.ordered", 2, "old", 1) == PURE_OK && b.duplicate && b.values[0] == 8);
+ CHECK(pure_app_deliver(&b, 0, "orders.ordered", 1, sent, NULL) == PURE_OK && b.duplicate && b.values[0].num == 2);
+ CHECK(deliver(&b, "other", 1, "cup", 5) == PURE_OK && !b.duplicate && b.values[0].num == 7);
+ CHECK(deliver(&b, "orders.ordered", 3, "hat", 1) == PURE_OK && b.values[0].num == 8); /* numbers may skip */
+ CHECK(deliver(&b, "orders.ordered", 2, "old", 1) == PURE_OK && b.duplicate && b.values[0].num == 8);
/* Refused (count 100): delivered all the same, never applied again. */
- CHECK(deliver(&b, "other", 2, "big", 100) == PURE_LIMIT && b.values[0] == 7 + 1);
- CHECK(deliver(&b, "other", 2, "big", 1) == PURE_OK && b.duplicate && b.values[0] == 8);
+ CHECK(deliver(&b, "other", 2, "big", 100) == PURE_LIMIT && b.values[0].num == 7 + 1);
+ CHECK(deliver(&b, "other", 2, "big", 1) == PURE_OK && b.duplicate && b.values[0].num == 8);
/* Bad deliveries change nothing. */
CHECK(deliver(&b, "", 4, "x", 1) == PURE_INVALID && deliver(&b, "a b", 4, "x", 1) == PURE_INVALID);
CHECK(deliver(&b, "other", 0, "x", 1) == PURE_INVALID && pure_app_deliver(&a, 2, "s", 1, sent, NULL) == PURE_INVALID);
@@ -4093,7 +4093,7 @@ static void links(void) {
pure_app_stop(&a);
CHECK(pure_app_start(&a, &module_a, pure_tree_data(path_a)) == PURE_OK);
o = outbox_of(&a);
- CHECK(o.count == 2 && o.items[0].seq == 1 && o.items[1].seq == 2 && a.values[0] == 2);
+ CHECK(o.count == 2 && o.items[0].seq == 1 && o.items[1].seq == 2 && a.values[0].num == 2);
CHECK(pure_app_defer(&a, true));
for (int i = 0; i < 3; ++i) CHECK(buy(&a, 0, "box", 1) == PURE_OK);
CHECK(outbox_of(&a).count == 2 && pure_app_save(&a) == PURE_OK);
@@ -4105,7 +4105,7 @@ static void links(void) {
CHECK(pure_app_save(&a) == PURE_STORE_ERROR);
fail_stage = -1;
o = outbox_of(&a);
- CHECK(o.count == 5 && o.items[0].seq == 1 && a.values[0] == 5); /* the trim and the send undone */
+ CHECK(o.count == 5 && o.items[0].seq == 1 && a.values[0].num == 5); /* the trim and the send undone */
CHECK(buy(&a, 0, "next", 1) == PURE_OK && pure_app_save(&a) == PURE_OK);
o = outbox_of(&a);
CHECK(o.count == 6 && o.items[5].seq == 6); /* numbered on from 5 */
@@ -4118,15 +4118,15 @@ static void links(void) {
/* The receiver: heard numbers stay heard after a restart; a refused one too. */
CHECK(deliver(&b, "orders.ordered", 1, "pen", 2) == PURE_OK && deliver(&b, "x", 1, "big", 100) == PURE_LIMIT);
pure_app_stop(&b);
- CHECK(pure_app_start(&b, &module_b, pure_tree_data(path_b)) == PURE_OK && b.values[0] == 2);
- CHECK(deliver(&b, "orders.ordered", 1, "pen", 2) == PURE_OK && b.duplicate && b.values[0] == 2);
+ CHECK(pure_app_start(&b, &module_b, pure_tree_data(path_b)) == PURE_OK && b.values[0].num == 2);
+ CHECK(deliver(&b, "orders.ordered", 1, "pen", 2) == PURE_OK && b.duplicate && b.values[0].num == 2);
CHECK(deliver(&b, "x", 1, "big", 1) == PURE_OK && b.duplicate);
/* A save that fails forgets what it heard. */
- CHECK(pure_app_defer(&b, true) && deliver(&b, "orders.ordered", 2, "ink", 3) == PURE_OK && b.values[0] == 5);
+ CHECK(pure_app_defer(&b, true) && deliver(&b, "orders.ordered", 2, "ink", 3) == PURE_OK && b.values[0].num == 5);
fail_stage = PURE_AFTER_HEADER;
- CHECK(pure_app_save(&b) == PURE_STORE_ERROR && b.values[0] == 2);
+ CHECK(pure_app_save(&b) == PURE_STORE_ERROR && b.values[0].num == 2);
fail_stage = -1;
- CHECK(deliver(&b, "orders.ordered", 2, "ink", 3) == PURE_OK && !b.duplicate && b.values[0] == 5);
+ CHECK(deliver(&b, "orders.ordered", 2, "ink", 3) == PURE_OK && !b.duplicate && b.values[0].num == 5);
CHECK(pure_app_save(&b) == PURE_OK);
pure_app_stop(&b);
/* Links stored before the heard list kept its numbers: a start moves them
@@ -4281,9 +4281,9 @@ static void links(void) {
CHECK(pure_app_start(&a, &module_a, pure_tree_data(path_a)) == PURE_OK);
CHECK(pure_app_start(&b, &module_b, pure_tree_data(path_b)) == PURE_OK);
o = outbox_of(&a);
- CHECK(o.count == (size_t)a.values[0] && (!o.count || o.items[o.count - 1].seq == o.count));
+ CHECK(o.count == (size_t)a.values[0].num && (!o.count || o.items[o.count - 1].seq == o.count));
heard = pure_app_heard(&b, "orders.ordered");
- CHECK(b.values[0] == (double)heard);
+ CHECK(b.values[0].num == (double)heard);
kept[0] = (size_t)heard;
kept[1] = o.count;
pure_app_stop(&a);
@@ -4566,24 +4566,24 @@ static void bools(void) {
CHECK(module_a.state_type[B_ON] == PURE_STATE_BOOL && module_a.view_type[0] == PURE_VIEW_BOOL &&
module_a.input[4].prefix == 9 && module_a.input[B_TOLD].prefix == 10);
PureApp app;
- CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK && app.values[B_ON] == 1 && app.values[B_OFF] == 0);
+ CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK && app.values[B_ON].num == 1 && app.values[B_OFF].num == 0);
CHECK(number_view(&app, 0) == 1 && number_view(&app, 1) == 0 && number_view(&app, 2) == 0 && number_view(&app, 3) == 0);
CHECK(!strcmp(view_text(&app, 4), "true1"));
for (int k = 0; k < 3; ++k) CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK);
- CHECK(app.values[B_N] == 3 && number_view(&app, 1) == 1 && number_view(&app, 3) == 1);
- CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_LIMIT && app.values[B_OFF] == 0); /* require failed: undone */
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK && app.values[B_ON] == 0 && !strcmp(view_text(&app, 4), "false1"));
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[B_N] == 3 && number_view(&app, 2) == 1);
+ CHECK(app.values[B_N].num == 3 && number_view(&app, 1) == 1 && number_view(&app, 3) == 1);
+ CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_LIMIT && app.values[B_OFF].num == 0); /* require failed: undone */
+ CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK && app.values[B_ON].num == 0 && !strcmp(view_text(&app, 4), "false1"));
+ CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[B_N].num == 3 && number_view(&app, 2) == 1);
/* A message's bool is a byte, 0 or 1; any other byte fails the message. */
unsigned char payload[9] = {1};
double five = 5;
memcpy(payload + 1, &five, 8);
- CHECK(pure_app_message(&app, 4, NULL, -1, (PureSpan){payload, 9}) == PURE_OK && app.values[B_ON] == 1 && app.values[B_N] == 5);
+ CHECK(pure_app_message(&app, 4, NULL, -1, (PureSpan){payload, 9}) == PURE_OK && app.values[B_ON].num == 1 && app.values[B_N].num == 5);
pure_app_finish(&app);
payload[0] = 2;
five = 6;
memcpy(payload + 1, &five, 8);
- CHECK(pure_app_message(&app, 4, NULL, -1, (PureSpan){payload, 9}) == PURE_LIMIT && app.values[B_N] == 5);
+ CHECK(pure_app_message(&app, 4, NULL, -1, (PureSpan){payload, 9}) == PURE_LIMIT && app.values[B_N].num == 5);
pure_app_finish(&app);
/* A send holds its bools as bytes. */
CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK);
@@ -4631,10 +4631,10 @@ static void bools(void) {
Spec swapped = {swapped_nums, 1, bool_inputs, LEN(bool_inputs), NULL, 0, NULL, 0, NULL, 0, NULL, 0, swapped_states, 2,
NULL, 0, NULL, 0, NULL, 0, NULL, 0};
CHECK(load(swapped, &module_b) == PURE_OK);
- CHECK(pure_app_replace(&app, &module_b) == PURE_OK && app.values[0] == 1 && app.values[1] == 0 && app.values[2] == 0);
- app.values[0] = -0.0; /* on, a number: -0 is 0 */
- CHECK(pure_app_replace(&app, &module_a) == PURE_OK && app.values[B_N] == 0 && app.values[B_ON] == 0 &&
- !signbit(app.values[B_ON]) && app.values[B_OFF] == 0);
+ CHECK(pure_app_replace(&app, &module_b) == PURE_OK && app.values[0].num == 1 && app.values[1].num == 0 && app.values[2].num == 0);
+ app.values[0].num = -0.0; /* on, a number: -0 is 0 */
+ CHECK(pure_app_replace(&app, &module_a) == PURE_OK && app.values[B_N].num == 0 && app.values[B_ON].num == 0 &&
+ !signbit(app.values[B_ON].num) && app.values[B_OFF].num == 0);
pure_app_stop(&app);
/* What mixes bools and numbers is refused. */
@@ -4737,7 +4737,7 @@ static void bools(void) {
for (int k = 0; k < 3; ++k) CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK);
pure_app_stop(&app);
for (int round = 0; round < 2; ++round) { /* migrated, then stored as migrated */
- CHECK(pure_app_start(&app, &module_b, pure_tree_data(path)) == PURE_OK && app.values[0] == 0);
+ CHECK(pure_app_start(&app, &module_b, pure_tree_data(path)) == PURE_OK && app.values[0].num == 0);
CHECK(pure_list_count(app.lists[TASKS]) == 3 && pure_list_get(app.lists[TASKS], 0, 1).num == 1 &&
pure_list_get(app.lists[TASKS], 1, 1).num == 0 && pure_list_get(app.lists[TASKS], 2, 1).num == 0);
pure_app_stop(&app);
@@ -4771,10 +4771,10 @@ static void timers(void) {
PureApp app;
CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK);
app.now = 1700000123456.0;
- CHECK(pure_app_tick(&app, 0) == PURE_OK && app.values[0] == 1700000123456.0 &&
- app.values[1] == (double)(1700000123 % 60) && app.values[2] == 1);
+ CHECK(pure_app_tick(&app, 0) == PURE_OK && app.values[0].num == 1700000123456.0 &&
+ app.values[1].num == (double)(1700000123 % 60) && app.values[2].num == 1);
CHECK(pure_app_tick(&app, 1) == PURE_INVALID && pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_INVALID);
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[3] == 2 - 20);
+ CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[3].num == 2 - 20);
/* A zero takes the divisor's sign too. */
static const O zeros[] = {{C, 0, -6}, {C, 0, 3}, {MOD, 0, 0}, {ST, 0, 0}, {C, 0, 6}, {C, 0, -3}, {MOD, 0, 0}, {ST, 1, 0}};
static const H zero_handler[] = {{3, 0, 8}};
@@ -4783,7 +4783,7 @@ static void timers(void) {
CHECK(load(signs, &module_b) == PURE_OK);
PureApp zero;
CHECK(pure_app_start(&zero, &module_b, (PureData){0}) == PURE_OK && pure_app_input(&zero, 1, PURE_EDGE_DOWN) == PURE_OK);
- CHECK(zero.values[0] == 0 && !signbit(zero.values[0]) && zero.values[1] == 0 && signbit(zero.values[1]));
+ CHECK(zero.values[0].num == 0 && !signbit(zero.values[0].num) && zero.values[1].num == 0 && signbit(zero.values[1].num));
pure_app_stop(&zero);
CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_INVALID && app.fault == PURE_FAULT_DIVIDE);
pure_app_stop(&app);
diff --git a/serve/message-json.c b/serve/message-json.c
index 0ace68f..afbd541 100644
--- a/serve/message-json.c
+++ b/serve/message-json.c
@@ -349,7 +349,7 @@ static const char *rows_of(PureJsonMessage *b, unsigned char *json, size_t size,
for (unsigned k = 0; k < record->count; ++k) {
uint32_t at_field = record->first + k;
if (m->record_field_text[at_field]) {
- uint32_t constant = (uint32_t)m->record_field_default[at_field];
+ uint32_t constant = (uint32_t)m->record_field_default[at_field].num;
PureSpan text = given[k] ? texts[k]
: (PureSpan){m->constant_data + m->constant_offset[constant], m->constant_length[constant]};
if (!rows_room(b, 4 + text.size)) return "memory";
@@ -357,7 +357,7 @@ static const char *rows_of(PureJsonMessage *b, unsigned char *json, size_t size,
if (text.size) memcpy(b->rows + b->rows_size + 4, text.data, text.size);
b->rows_size += 4 + text.size;
} else {
- double value = given[k] ? numbers[k] : m->record_field_default[at_field];
+ double value = given[k] ? numbers[k] : m->record_field_default[at_field].num;
uint64_t bits;
memcpy(&bits, &value, 8);
if (!rows_room(b, 8)) return "memory";
diff --git a/tests/module-probe.c b/tests/module-probe.c
index 72d06b2..de1fbf8 100644
--- a/tests/module-probe.c
+++ b/tests/module-probe.c
@@ -103,7 +103,7 @@ int main(int argc, char **argv) {
printf("{\"inputs\":%d,\"ranged\":%u,\"states\":{", argc - 2, ranged);
for (uint32_t s = 0, shown = 0; s < modules[0].states; ++s)
if (modules[0].state_type[s] == PURE_STATE_NUM || modules[0].state_type[s] == PURE_STATE_INDEX)
- printf("%s\"%s\":%.17g", shown++ ? "," : "", modules[0].state_names[s], apps[0].values[s]);
+ printf("%s\"%s\":%.17g", shown++ ? "," : "", modules[0].state_names[s], apps[0].values[s].num);
printf("}}\n");
for (int k = 0; k < 2; ++k) {
pure_app_stop(&apps[k]);