diff options
| author | eunhhu <[email protected]> | 2026-10-19 04:42:05 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-10-19 04:42:05 +0100 |
| commit | 6596533b0d5fe10f3cab028d7fa69a40e5416ecd (patch) | |
| tree | 9c5a64858ac749d892e673b7aab04928cd3bbe60 /core | |
| parent | 5a2292a389989b71b99ea582cf306cf1719d8aae (diff) | |
| download | pure-6596533b0d5fe10f3cab028d7fa69a40e5416ecd.tar.gz pure-6596533b0d5fe10f3cab028d7fa69a40e5416ecd.zip | |
028's bytes alone as a state, made: module state type 6 read as a text state flagged PURE_TEXT_BYTES in state_flags (so serve's same_states compares it for queries and replaces), its TEXT_LOAD giving bytes and its TEXT_SET taking them, a cursor's ops on it refused (anchor in bytes) and an anchor in it refused at load; one store file text-NAME, the values record marking each bytes state (kind 7, its name, 8 zero bytes), a text state's file that is no UTF-8 its initial value when marked and store_error when not (as before); a replace carrying bytes to a text only when its draft is UTF-8, else opening the store's draft anew; pure-run shows a bytes state's hex; unit test bytes_states (seven starts from a store: 0xff and 0x00 kept, bytes to text to bytes to text with no UTF-8 and with it; three replaces; four refused); counts: b47 against b46 BFS with batches +0.12%, the rest within 0.001%; the bank's queries (s45 and s47 in turns, six each, bytes compared among them) in_credit -0.31%, per_branch -0.18%, ranks -0.11%, the rest within 0.01%, the seven answers the same bytes; Pure, Vega and aup checks green; 008, 028
Diffstat (limited to 'core')
| -rw-r--r-- | core/module-save.c | 55 | ||||
| -rw-r--r-- | core/module-save.h | 5 | ||||
| -rw-r--r-- | core/module.c | 48 | ||||
| -rw-r--r-- | core/module.h | 7 | ||||
| -rw-r--r-- | core/test-module.c | 106 |
5 files changed, 198 insertions, 23 deletions
diff --git a/core/module-save.c b/core/module-save.c index 1055b4f..6e204d1 100644 --- a/core/module-save.c +++ b/core/module-save.c @@ -131,25 +131,54 @@ PURE_COLD static bool named(const unsigned char *bytes, size_t size, size_t *at, return true; } -PURE_COLD PureResult pure_save_open_text(PureApp *app, const PureModule *m, uint32_t state, PureDraft **out, +/* A bytes state's mark among the values (028): its kind past every state type, its name, 8 bytes of 0. A text state's + * stored bytes that are no UTF-8 are then a move from bytes (its initial value), not a store gone wrong. */ +enum { SAVED_BYTES = 7 }; +static bool bytes_state(const PureModule *m, uint32_t i) { + return m->state_type[i] == PURE_STATE_TEXT && m->state_flags[i] & PURE_TEXT_BYTES; +} + +/* Whether the values record marks the state of that name as bytes (028). */ +PURE_COLD static bool marked_bytes(PureApp *app, const char *name) { + if (!app->stored_values && open_state(app, "", "values", &app->stored_values) != PURE_OK) return false; + unsigned char *bytes; + size_t size; + if (!pure_draft_revision(app->stored_values) || read_whole(app->stored_values, &bytes, &size) != PURE_OK) return false; + bool found = false, ok = size >= 4 && !memcmp(bytes, values_magic, 4); + for (size_t at = 4; ok && !found && at < size; at += 8) { + char entry[PURE_MODULE_NAME]; + uint8_t type; + if (!(ok = named(bytes, size, &at, 8, &type, entry))) break; + found = type == SAVED_BYTES && !strcmp(entry, name); + } + free(bytes); + return found; +} + +PURE_COLD PureResult pure_save_open_text(PureApp *app, const PureModule *m, uint32_t state, bool anew, PureDraft **out, PureVersion *saved) { PureResult result = open_state(app, "text-", m->state_names[state], out); if (result != PURE_OK) return result; *saved = pure_draft_version(*out); - if (!pure_draft_revision(*out)) { - uint32_t constant = m->state_ref[state]; - PureSpan initial = {m->constant_data + m->constant_offset[constant], m->constant_length[constant]}; - result = pure_draft_splice(*out, 0, pure_draft_size(*out), initial); - *saved = unsaved; - } else { + bool initial = anew || !pure_draft_revision(*out); + if (!initial && !bytes_state(m, state)) { /* a text's bytes are UTF-8, or a bytes state's were (a move: anew) */ unsigned char *bytes; size_t size; result = read_whole(*out, &bytes, &size); if (result == PURE_OK) { - if (!pure_utf8_valid((PureSpan){bytes, size})) result = PURE_STORE_ERROR; + if (!pure_utf8_valid((PureSpan){bytes, size})) { + if (marked_bytes(app, m->state_names[state])) initial = true; + else result = PURE_STORE_ERROR; + } free(bytes); } } + if (result == PURE_OK && initial) { + uint32_t constant = m->state_ref[state]; + PureSpan value = {m->constant_data + m->constant_offset[constant], m->constant_length[constant]}; + result = pure_draft_splice(*out, 0, pure_draft_size(*out), value); + *saved = unsaved; + } if (result != PURE_OK) { pure_draft_close(*out); *out = NULL; } return result; } @@ -789,11 +818,11 @@ static size_t encode_values(const PureModule *m, const PureValue *values, unsign size_t at = 4; memcpy(out, values_magic, 4); for (uint32_t i = 0; i < m->states; ++i) { - if (!scalar(m->state_type[i]) || pure_state_transient(m, i)) continue; + if ((!scalar(m->state_type[i]) && !bytes_state(m, i)) || pure_state_transient(m, i)) continue; size_t length = strlen(m->state_names[i]); - uint64_t bits; - memcpy(&bits, &values[i], sizeof(bits)); - out[at++] = m->state_type[i]; + uint64_t bits = 0; + if (!bytes_state(m, i)) memcpy(&bits, &values[i], sizeof(bits)); + out[at++] = bytes_state(m, i) ? (unsigned char)SAVED_BYTES : m->state_type[i]; out[at++] = (unsigned char)length; memcpy(out + at, m->state_names[i], length); at += length; @@ -813,6 +842,8 @@ PURE_COLD PureResult pure_save_open_values(PureApp *app, const PureModule *m, Pu if (scalar(m->state_type[i]) && !pure_state_transient(m, i)) { scalars = true; fresh = fresh || kept[i] < 0; + } else if (bytes_state(m, i) && !pure_state_transient(m, i)) { + scalars = true; /* its mark is among the values */ } if (!scalars) return PURE_OK; PureResult result = app->stored_values ? PURE_OK : open_state(app, "", "values", &app->stored_values); diff --git a/core/module-save.h b/core/module-save.h index 44d3b95..2a82d10 100644 --- a/core/module-save.h +++ b/core/module-save.h @@ -10,7 +10,10 @@ bool pure_save_enabled(const PureApp *); /* A text state of m: its stored value, or its initial constant when none. */ -PureResult pure_save_open_text(PureApp *, const PureModule *m, uint32_t state, PureDraft **out, PureVersion *saved); +/* A text state's draft in the store, its initial value where it has none, where anew says (a move that keeps nothing, + * 028), or where its bytes are no UTF-8 but the values record marks it bytes; store_error where they are no UTF-8 + * otherwise. A bytes state's asks no UTF-8. */ +PureResult pure_save_open_text(PureApp *, const PureModule *m, uint32_t state, bool anew, PureDraft **out, PureVersion *saved); /* Stored lists (module-save.c): at a start, the log opened and read for the * lists (pure_save_open_log), each list opened from its image with its * logged changes made again (pure_save_open_list; saved says what its image diff --git a/core/module.c b/core/module.c index 64ec8f6..621a510 100644 --- a/core/module.c +++ b/core/module.c @@ -575,6 +575,10 @@ PURE_COLD static const char *verify_code(const PureModule *m, PureBlock block, i (truth ? !canonical_bool(op->value) : integer ? false : constant ? !isfinite(op->value) : op->integer != 0)) REFUSE("non-canonical operand"); if (!names_arg(m, kind, arg, shape, row, outer)) REFUSE("operand names no such state, field, constant or unit"); + /* A bytes state (028) is read whole and set whole: no anchor is in it, so no op of a text's anchors takes it. */ + if ((kind == A_STATE + PURE_STATE_TEXT || kind == A_IN_TEXT) && op->op != PURE_OP_TEXT_LOAD && op->op != PURE_OP_TEXT_SET && + m->state_flags[arg & 0xFFFF] & PURE_TEXT_BYTES) + REFUSE("anchor in bytes"); /* A keyed list places its items by key, and an item's key stays. */ if (op->op == PURE_OP_LIST_INSERT && m->state_key[arg]) REFUSE("insert by position into a keyed list"); /* A walk ends: no list a walk walks grows while it runs, and only the innermost walk's may shrink (it moves with it). */ @@ -672,6 +676,8 @@ PURE_COLD static const char *verify_code(const PureModule *m, PureBlock block, i need[0] = (arg & 3) == 1 ? T_BOOL : T_I64; } else if ((op->op == PURE_OP_CONCAT && arg) || (op->op == PURE_OP_TEXT_COMPARE && arg & PURE_COMPARE_BYTES)) { need[0] = need[1] = T_BYTES; + } else if (op->op == PURE_OP_TEXT_SET && m->state_flags[arg] & PURE_TEXT_BYTES) { + need[0] = T_BYTES; /* a bytes state's whole value (028) */ } else if (op->op == PURE_OP_BYTES_SLICE && arg) { need[1] = need[2] = T_I64; /* the positions i64s */ } else if (op->op == PURE_OP_BYTES_STORE && (arg & 0xFF) >= PURE_HELD_TEXT) { @@ -729,7 +735,9 @@ PURE_COLD static const char *verify_code(const PureModule *m, PureBlock block, i if (out == T_STATE) out = state_type(m, arg); if (truth) out = T_BOOL; if (integer) out = T_I64; - if ((op->op == PURE_OP_CONCAT && arg) || (op->op == PURE_OP_BYTES_LOAD && (arg & 0xFF) < PURE_HELD_TEXT)) out = T_BYTES; + if ((op->op == PURE_OP_CONCAT && arg) || (op->op == PURE_OP_BYTES_LOAD && (arg & 0xFF) < PURE_HELD_TEXT) || + (op->op == PURE_OP_TEXT_LOAD && m->state_flags[arg] & PURE_TEXT_BYTES)) + out = T_BYTES; else if (op->op == PURE_OP_BYTES_LOAD) out = T_TEXT; if (op->op == PURE_OP_CALL) /* its results */ for (uint32_t k = 0, first = m->function[arg].first + m->function[arg].params; k < m->function[arg].results; ++k) { @@ -1799,11 +1807,12 @@ PURE_COLD PureResult pure_module_load(const void *bytes, size_t size, PureModule * be in a list (below). */ static const uint8_t held_as[] = {[PURE_MODULE_NUM] = PURE_STATE_NUM, [PURE_MODULE_TEXT] = PURE_STATE_TEXT, [PURE_MODULE_ANCHOR] = PURE_STATE_CURSOR, [PURE_MODULE_LIST] = PURE_STATE_LIST, - [PURE_MODULE_BOOL] = PURE_STATE_BOOL, [PURE_MODULE_I64] = PURE_STATE_I64}; + [PURE_MODULE_BOOL] = PURE_STATE_BOOL, [PURE_MODULE_I64] = PURE_STATE_I64, + [PURE_MODULE_BYTES] = PURE_STATE_TEXT}; const unsigned char *states_at = at; for (uint32_t i = 0; i < out->states; ++i, at += STATE_BYTES) { uint32_t type = read32(at + 4); - if ((type & ~(uint32_t)PURE_STATE_TRANSIENT) > PURE_MODULE_I64) REFUSE("state type", i); + if ((type & ~(uint32_t)PURE_STATE_TRANSIENT) > PURE_MODULE_BYTES) REFUSE("state type", i); out->state_type[i] = held_as[type & 0xFF]; out->state_transient[i] = type & PURE_STATE_TRANSIENT; /* A number's, a bool's or an i64's value is its 8 bytes; others have flags: @@ -1833,7 +1842,8 @@ PURE_COLD PureResult pure_module_load(const void *bytes, size_t size, PureModule if ((flags && !(cursor && flags == PURE_CURSOR_STAYS) && !(list && key <= PURE_RECORD_FIELDS)) || read32(at + 8) > UINT16_MAX) REFUSE("state reference", i); out->state_ref[i] = (uint16_t)read32(at + 8); - out->state_flags[i] = cursor ? (uint8_t)flags : (uint8_t)((rows ? PURE_LIST_ROWS >> 8 : 0) | (bound ? PURE_LIST_BOUND >> 8 : 0)); + out->state_flags[i] = cursor ? (uint8_t)flags : list ? (uint8_t)((rows ? PURE_LIST_ROWS >> 8 : 0) | (bound ? PURE_LIST_BOUND >> 8 : 0)) + : (type & 0xFF) == PURE_MODULE_BYTES ? (uint8_t)PURE_TEXT_BYTES : 0; out->state_key[i] = (uint8_t)key; } } @@ -1960,7 +1970,8 @@ PURE_COLD PureResult pure_module_load(const void *bytes, size_t size, PureModule at = fields_at + (size_t)out->record_fields * RECORD_FIELD_BYTES; for (uint32_t i = 0; i < out->states; ++i) if ((out->state_type[i] == PURE_STATE_TEXT && out->state_ref[i] >= out->constants) || - (out->state_type[i] == PURE_STATE_CURSOR && !state_of(out, out->state_ref[i], PURE_STATE_TEXT)) || + (out->state_type[i] == PURE_STATE_CURSOR && + (!state_of(out, out->state_ref[i], PURE_STATE_TEXT) || out->state_flags[out->state_ref[i]] & PURE_TEXT_BYTES)) || (out->state_type[i] == PURE_STATE_LIST && (out->state_ref[i] >= out->records || out->state_key[i] > out->record[out->state_ref[i]].count || (out->state_key[i] && @@ -4981,6 +4992,23 @@ PURE_COLD static PureResult new_text(PureDraft **out, const PureModule *m, uint3 } /* A stored draft given a transient text's bytes, to be saved (saved: none yet). */ +/* Whether a draft's bytes are UTF-8 (a bytes state's, before it is a text's: 028). */ +typedef struct { unsigned char *bytes; size_t used; } Drafted; +static bool drafted(void *context, PureSpan span) { + Drafted *d = context; + if (span.size) memcpy(d->bytes + d->used, span.data, span.size); + d->used += span.size; + return true; +} +PURE_COLD static bool draft_utf8(const PureDraft *draft) { + size_t size = pure_draft_size(draft); + Drafted into = {malloc(size ? size : 1), 0}; + bool valid = into.bytes && pure_draft_read(draft, 0, size, drafted, &into) == PURE_OK && into.used == size && + pure_utf8_valid((PureSpan){into.bytes, size}); + free(into.bytes); + return valid; +} + PURE_COLD static PureResult copy_text(PureDraft *to, const PureDraft *from, PureVersion *saved) { size_t size = pure_draft_size(from); unsigned char *bytes = size ? malloc(size) : NULL; @@ -6445,17 +6473,21 @@ PURE_COLD static PureResult open_module(PureApp *app, const PureModule *module) /* A number, a bool and an i64 carry over each other when the value fits (pure_cell_as), else start anew. */ uint8_t was = previous >= 0 ? old->state_type[previous] : 0, now = module->state_type[i]; bool valued = pure_value_kind(was) != 0xFF && pure_value_kind(now) != 0xFF; - kept[i] = previous >= 0 && (was == now || valued) ? previous : -1; + /* A text and bytes (028) carry over each other when exact: bytes become a text when they are UTF-8, else it is new. */ + bool anew = previous >= 0 && was == PURE_STATE_TEXT && now == PURE_STATE_TEXT && + old->state_flags[previous] & PURE_TEXT_BYTES && !(module->state_flags[i] & PURE_TEXT_BYTES) && + !draft_utf8(app->texts[previous]); + kept[i] = previous >= 0 && (was == now || valued) && !anew ? previous : -1; if (kept[i] >= 0) saved[i] = app->saved[kept[i]]; /* A transient state is never stored (018): one that was stored is now as a dropped one is. */ bool transient = pure_state_transient(module, i), was_transient = kept[i] >= 0 && pure_state_transient(old, kept[i]); if (module->state_type[i] == PURE_STATE_TEXT) { if (kept[i] >= 0 && (transient || !was_transient || !stored)) texts[i] = app->texts[kept[i]]; else if (result == PURE_OK && kept[i] >= 0 && /* a text stored from now on: its draft in the store holds it */ - (result = pure_save_open_text(app, module, i, &texts[i], &saved[i])) == PURE_OK) + (result = pure_save_open_text(app, module, i, false, &texts[i], &saved[i])) == PURE_OK) result = copy_text(texts[i], app->texts[kept[i]], &saved[i]); else if (result == PURE_OK) - result = stored && !transient ? pure_save_open_text(app, module, i, &texts[i], &saved[i]) : + result = stored && !transient ? pure_save_open_text(app, module, i, anew, &texts[i], &saved[i]) : new_text(&texts[i], module, module->state_ref[i]); } else if (module->state_type[i] == PURE_STATE_LIST) { if (result != PURE_OK) continue; diff --git a/core/module.h b/core/module.h index 6fad0ec..43d9ccb 100644 --- a/core/module.h +++ b/core/module.h @@ -259,8 +259,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, PURE_STATE_I64 = 6 } PureStateType; -/* A state's type in a module: a number, a text, an anchor, a list, a bool or an i64. */ -enum { PURE_MODULE_NUM, PURE_MODULE_TEXT, PURE_MODULE_ANCHOR, PURE_MODULE_LIST, PURE_MODULE_BOOL, PURE_MODULE_I64 }; +/* A state's type in a module: a number, a text, an anchor, a list, a bool, an i64 or bytes (028: held as a text is, its + * flag PURE_TEXT_BYTES, UTF-8 asked of none of it). */ +enum { PURE_MODULE_NUM, PURE_MODULE_TEXT, PURE_MODULE_ANCHOR, PURE_MODULE_LIST, PURE_MODULE_BOOL, PURE_MODULE_I64, + PURE_MODULE_BYTES }; +#define PURE_TEXT_BYTES 1u /* a text state's flag (state_flags): its type is bytes (028) */ /* A number's, a bool's or an i64's state as a list cell's kind (PURE_CELL_..., * for pure_cell_as), or 0xFF for any other state. */ static inline uint8_t pure_value_kind(uint8_t state_type) { diff --git a/core/test-module.c b/core/test-module.c index 8795bf8..9762c48 100644 --- a/core/test-module.c +++ b/core/test-module.c @@ -102,7 +102,9 @@ static unsigned char *put_double(unsigned char *p, double value) { * module has their anchor (027's 4c), its type 2, and its instructions, which name the text or list (bits 0-15 of a * MOVE's and a COUNT's argument: IN), so a test gives that where a step does not name it. */ #define IN(target, unit) ((uint32_t)(target) | (uint32_t)(unit) << PURE_ANCHOR_UNIT) +#define T_BYTES_STATE 64 /* in a T's type: a bytes state (028), ref its constant as a text's */ static uint32_t module_type(uint32_t type) { + if (type == T_BYTES_STATE) return PURE_MODULE_BYTES; static const uint8_t types[] = {[PURE_STATE_NUM] = PURE_MODULE_NUM, [PURE_STATE_TEXT] = PURE_MODULE_TEXT, [PURE_STATE_CURSOR] = PURE_MODULE_ANCHOR, [PURE_STATE_LIST] = PURE_MODULE_LIST, [PURE_STATE_INDEX] = PURE_MODULE_ANCHOR, [PURE_STATE_BOOL] = PURE_MODULE_BOOL, @@ -6164,6 +6166,109 @@ static void bits_and_bytes(void) { printf("{\"bits_and_bytes\":{\"bits\":%zu,\"reads\":%zu,\"refused\":%zu}}\n", LEN(cases), LEN(reads), LEN(refused)); } +/* ---- Bytes alone as a state (docs/design/028) ---------------------------- + * A bytes state is held as a text is (one draft) and read and set whole as + * bytes, UTF-8 asked of none of it: 0xff and 0x00 kept across a restart. + * Text and bytes of a name carry over each other when exact, on a replace + * and on a start from a store: a text always, bytes when UTF-8, else the + * initial value (the values record marks a bytes state, so a text's bytes + * that are no UTF-8 are a move, not a store gone wrong). Refused: an anchor + * in it, a cursor's op on it, a text set into it, its bytes into a text. */ +typedef struct { unsigned char *at; size_t used; } Gathered64; +static bool gathered64(void *context, PureSpan span) { + Gathered64 *g = context; + if (g->used + span.size > 64) return false; + if (span.size) memcpy(g->at + g->used, span.data, span.size); + g->used += span.size; + return true; +} +static size_t draft_bytes(const PureApp *app, uint32_t state, unsigned char *out) { + Gathered64 g = {out, 0}; + return pure_draft_read(app->texts[state], 0, pure_draft_size(app->texts[state]), gathered64, &g) == PURE_OK ? g.used + : (size_t)-1; +} + +static void bytes_states(void) { + enum { TS = PURE_OP_TEXT_SET, CAT_ = PURE_OP_CONCAT, BYTE_ = PURE_OP_BYTE, LEN_ = PURE_OP_LEN, CH = PURE_OP_CHARS }; + static const S nums[] = {{"n", 0}}; + static const char *const constants[] = {"Az\xc3\xa9", ""}; + static const I inputs[] = {KEY("go", "KeyG"), KEY("set", "KeyS")}; + static const H handlers[] = {{1, 0, 11}, {3, 11, 6}}, text_handlers[] = {{1, 0, 3}, {3, 3, 5}}; + /* As bytes: go appends ff 00 (n its length), set gives it "Az\xc3\xa9"'s bytes. */ + const O as_bytes[] = {{TL, 1, 0}, IC(0xFF), {BYTE_, 0, 0}, {CAT_, 1, 0}, IC(0), {BYTE_, 0, 0}, {CAT_, 1, 0}, {TS, 1, 0}, + {TL, 1, 0}, {LEN_, 0, 0}, {ST, 0, 0}, + {TK, 0, 0}, {PURE_OP_BYTES_FROM, 0, 0}, {TS, 1, 0}, {TL, 1, 0}, {LEN_, 0, 0}, {ST, 0, 0}}; + /* As a text: go counts its characters, set gives it "Az\xc3\xa9". */ + static const O as_text[] = {{TL, 1, 0}, {CH, 0, 0}, {ST, 0, 0}, {TK, 0, 0}, {TS, 1, 0}, {TL, 1, 0}, {CH, 0, 0}, {ST, 0, 0}}; + static const T bytes_state[] = {{"data", T_BYTES_STATE, 1}}, text_state[] = {{"data", 1, 1}}; + Spec bytes_spec = {nums, 1, inputs, 2, NULL, 0, handlers, 2, as_bytes, LEN(as_bytes), NULL, 0, bytes_state, 1, constants, + 2, NULL, 0, NULL, 0, NULL, 0}; + Spec text_spec = {nums, 1, inputs, 2, NULL, 0, text_handlers, 2, as_text, LEN(as_text), NULL, 0, text_state, 1, constants, + 2, NULL, 0, NULL, 0, NULL, 0}; + static PureModule bytes_module, text_module; + if (load(bytes_spec, &bytes_module) != PURE_OK) { fprintf(stderr, "%s\n", error); abort(); } + if (load(text_spec, &text_module) != PURE_OK) { fprintf(stderr, "%s\n", error); abort(); } + CHECK(bytes_module.state_type[1] == PURE_STATE_TEXT && bytes_module.state_flags[1] & PURE_TEXT_BYTES && + !(text_module.state_flags[1] & PURE_TEXT_BYTES)); + unsigned char got[64]; + static const unsigned char twice[] = {0xFF, 0, 0xFF, 0}, word[] = {'A', 'z', 0xC3, 0xA9}; + /* Stored: 0xff and 0x00 kept across a restart. */ + char *path = new_store(); + PureApp app; + CHECK(pure_app_start(&app, &bytes_module, pure_tree_data(path)) == PURE_OK); + CHECK(press(&app, 0) == PURE_OK && press(&app, 0) == PURE_OK && app.values[0].num == 4); + pure_app_stop(&app); + CHECK(pure_app_start(&app, &bytes_module, pure_tree_data(path)) == PURE_OK); + CHECK(draft_bytes(&app, 1, got) == 4 && !memcmp(got, twice, 4)); + pure_app_stop(&app); + /* Bytes to a text across a start: no UTF-8, the record says bytes: its initial value (not a store gone wrong). */ + CHECK(pure_app_start(&app, &text_module, pure_tree_data(path)) == PURE_OK); + CHECK(pure_draft_size(app.texts[1]) == 0 && press(&app, 1) == PURE_OK && app.values[0].num == 3); + pure_app_stop(&app); + /* Back to bytes: the text's bytes; then ff 00 after them; then to a text again: no UTF-8 now, initial again. */ + CHECK(pure_app_start(&app, &bytes_module, pure_tree_data(path)) == PURE_OK); + CHECK(draft_bytes(&app, 1, got) == 4 && !memcmp(got, word, 4) && press(&app, 0) == PURE_OK && app.values[0].num == 6); + pure_app_stop(&app); + CHECK(pure_app_start(&app, &text_module, pure_tree_data(path)) == PURE_OK && pure_draft_size(app.texts[1]) == 0); + pure_app_stop(&app); + /* Bytes that are UTF-8 become the text. */ + CHECK(pure_app_start(&app, &bytes_module, pure_tree_data(path)) == PURE_OK && press(&app, 1) == PURE_OK); + pure_app_stop(&app); + CHECK(pure_app_start(&app, &text_module, pure_tree_data(path)) == PURE_OK); + CHECK(!strcmp(state_text(&app, 1), "Az\xc3\xa9") && press(&app, 0) == PURE_OK && app.values[0].num == 3); + pure_app_stop(&app); + remove_store(path); + /* In memory, by replace: bytes that are no UTF-8 become a new text; UTF-8 ones the text; a text the bytes. */ + CHECK(pure_app_start(&app, &bytes_module, (PureData){0}) == PURE_OK && press(&app, 0) == PURE_OK); + CHECK(pure_app_replace(&app, &text_module) == PURE_OK && pure_draft_size(app.texts[1]) == 0); + CHECK(press(&app, 1) == PURE_OK && pure_app_replace(&app, &bytes_module) == PURE_OK); + CHECK(draft_bytes(&app, 1, got) == 4 && !memcmp(got, word, 4) && press(&app, 1) == PURE_OK); + CHECK(pure_app_replace(&app, &text_module) == PURE_OK && !strcmp(state_text(&app, 1), "Az\xc3\xa9")); + pure_app_stop(&app); + /* Refused: a text set into it, its bytes into a text, a cursor's op on it, an anchor in it. */ + const O refused[][2] = { + {{TK, 0, 0}, {TS, 1, 0}}, /* a text into the bytes state */ + {{PURE_OP_CUR_START, 1, 0}, {PURE_OP_TEXT_LINE, 1, 0}}, /* a cursor of it */ + }; + for (size_t k = 0; k < LEN(refused); ++k) { + H handler = {1, 0, 2}; + Spec spec = {nums, 1, inputs, 1, NULL, 0, &handler, 1, refused[k], 2, NULL, 0, bytes_state, 1, constants, 2, NULL, 0, + NULL, 0, NULL, 0}; + if (load(spec, &module_b) == PURE_OK) { fprintf(stderr, "bytes states: refused %zu loaded\n", k); abort(); } + } + static const O into_text[] = {{TL, 1, 0}, {TS, 2, 0}, {C, 0, 0}}; /* the bytes state's bytes into a text */ + static const T both[] = {{"data", T_BYTES_STATE, 1}, {"note", 1, 1}}; + H handler = {1, 0, 2}; + Spec spec = {nums, 1, inputs, 1, NULL, 0, &handler, 1, into_text, 2, NULL, 0, both, 2, constants, 2, NULL, 0, NULL, 0, NULL, 0}; + CHECK(load(spec, &module_b) != PURE_OK); + static const T anchored[] = {{"data", T_BYTES_STATE, 1}, {"at", PURE_STATE_CURSOR, 1}}; + static const O nothing[] = {{C, 0, 0}, {ST, 0, 0}}; + Spec with_anchor = {nums, 1, inputs, 1, NULL, 0, &handler, 1, nothing, 2, NULL, 0, anchored, 2, constants, 2, NULL, 0, NULL, 0, + NULL, 0}; + CHECK(load(with_anchor, &module_b) != PURE_OK); + printf("{\"bytes_states\":{\"starts\":7,\"replaces\":3,\"refused\":%zu}}\n", LEN(refused) + 2); +} + /* ---- Bytes compared (after research 140's DNS and HTTP) -------------------- * TEXT_COMPARE with PURE_COMPARE_BYTES relates two bytes by their bytes, as * it relates texts (a shorter before a longer it begins), UTF-8 asked of @@ -7391,6 +7496,7 @@ int main(void) { integers(); bits_and_bytes(); bytes_compared(); + bytes_states(); formats_and_held(); lists_in_records(); functions_and_characters(); |
