diff options
Diffstat (limited to 'core')
| -rw-r--r-- | core/module-list.c | 8 | ||||
| -rw-r--r-- | core/module-list.h | 2 | ||||
| -rw-r--r-- | core/module-save.c | 8 | ||||
| -rw-r--r-- | core/module-save.h | 4 | ||||
| -rw-r--r-- | core/module.c | 107 | ||||
| -rw-r--r-- | core/module.h | 15 | ||||
| -rw-r--r-- | core/test-module.c | 300 |
7 files changed, 226 insertions, 218 deletions
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); |
