diff options
| author | eunhhu <[email protected]> | 2026-09-26 07:29:11 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-09-26 07:29:11 +0100 |
| commit | 9c37c5fa0f58eefe517ca0456324f5618a13b87d (patch) | |
| tree | 6174666c8fb4084ff79a26e6f1e1dadfc0c511d0 /prototypes/connections/module-save.c | |
| parent | 46fd0601c89a535a6501b475ff08ae11b3137ea9 (diff) | |
| download | pure-9c37c5fa0f58eefe517ca0456324f5618a13b87d.tar.gz pure-9c37c5fa0f58eefe517ca0456324f5618a13b87d.zip | |
Compress the module executor's paths
The executor shrinks from 49.8 to 42.2 KB of -O2 text (module.c 34.0 to
27.0 KB) with the module format and API unchanged:
- The verifier reads its rules from the signature table: what each
opcode's argument names, and which text or list it works on. Cursor and
index operands must belong to that text or list and so does a pushed one;
other values belong to nothing, so a value that merely came from a text no
longer blocks a join. The loader and verifier fail through one path.
- The interpreter advances as it fetches and leaves through two exits: a
stated fault, or a change the data refused.
- Key, text and message events take one path; a message brings its base
revision and key. Starting an app opens its module over none, the path
replacing takes, so every new state starts from its stored value.
- Paths that run once per module or app are marked cold and built for size;
event paths stay built for speed (small GETs: 128k/s, as before).
Diffstat (limited to 'prototypes/connections/module-save.c')
| -rw-r--r-- | prototypes/connections/module-save.c | 62 |
1 files changed, 35 insertions, 27 deletions
diff --git a/prototypes/connections/module-save.c b/prototypes/connections/module-save.c index 573ac3f..a5eb95a 100644 --- a/prototypes/connections/module-save.c +++ b/prototypes/connections/module-save.c @@ -40,17 +40,26 @@ static PureResult read_whole(const PureDraft *draft, unsigned char **out, size_t return PURE_OK; } -static bool name_ok(const unsigned char *name, size_t length) { - if (!length || length >= PURE_MODULE_NAME) return false; +/* One named entry of a stored form: u8 kind, u8 name length, the name (an + * identifier), then width more bytes. */ +PURE_COLD static bool named(const unsigned char *bytes, size_t size, size_t *at, size_t width, uint8_t *kind, + char name[PURE_MODULE_NAME]) { + if (size - *at < 2) return false; + size_t length = bytes[*at + 1]; + if (size - *at - 2 < length + width || !length || length >= PURE_MODULE_NAME) return false; for (size_t i = 0; i < length; ++i) { - unsigned char c = name[i]; + unsigned char c = bytes[*at + 2 + i]; bool letter = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; if (!letter && !(i && c >= '0' && c <= '9')) return false; } + *kind = bytes[*at]; + memcpy(name, bytes + *at + 2, length); + name[length] = 0; + *at += 2 + length; return true; } -PureResult pure_save_open_text(PureApp *app, const PureModule *m, uint32_t state, PureDraft **out, +PURE_COLD PureResult pure_save_open_text(PureApp *app, const PureModule *m, uint32_t state, PureDraft **out, PureVersion *saved) { PureResult result = open_state(app, "text-", m->state_names[state], out); if (result != PURE_OK) return result; @@ -89,7 +98,7 @@ static size_t list_header(const PureModule *m, uint32_t record, unsigned char *o return at; } -PureResult pure_save_rewrite_list(const PureModule *m, uint32_t state, PureList *list, PureDraft *draft) { +PURE_COLD PureResult pure_save_rewrite_list(const PureModule *m, uint32_t state, PureList *list, PureDraft *draft) { unsigned char header[5 + PURE_RECORD_FIELDS * (2 + PURE_MODULE_NAME)]; size_t length = list_header(m, m->state_ref[state], header); PureVersion before = pure_draft_version(draft); @@ -105,7 +114,7 @@ PureResult pure_save_rewrite_list(const PureModule *m, uint32_t state, PureList return result; } -PureResult pure_save_open_list(PureApp *app, const PureModule *m, uint32_t state, PureList **out, PureDraft **draft, +PURE_COLD PureResult pure_save_open_list(PureApp *app, const PureModule *m, uint32_t state, PureList **out, PureDraft **draft, PureVersion *saved) { uint32_t record = m->state_ref[state]; const PureRecord *r = &m->record[record]; @@ -126,12 +135,8 @@ PureResult pure_save_open_list(PureApp *app, const PureModule *m, uint32_t state count = ok ? bytes[4] : 0; at = 5; for (unsigned f = 0; ok && f < count; ++f) { - ok = size - at >= 2 && bytes[at] <= 1 && size - at - 2 >= bytes[at + 1] && name_ok(bytes + at + 2, bytes[at + 1]); - if (!ok) break; - text[f] = bytes[at]; - memcpy(names[f], bytes + at + 2, bytes[at + 1]); + ok = named(bytes, size, &at, 0, &text[f], names[f]) && text[f] <= 1; for (unsigned k = 0; k < f; ++k) ok = ok && strcmp(names[k], names[f]); - at += 2 + bytes[at + 1]; } if (!ok) result = PURE_STORE_ERROR; } @@ -176,15 +181,15 @@ static bool scalar(uint8_t type) { /* A stored value still fits its state: a cursor on a character boundary of * its text, an index on an item of its list (or 0), a finite number. */ -static bool fits(const PureApp *app, const PureModule *m, uint32_t state, double value) { +PURE_COLD static bool fits(const PureModule *m, PureDraft *const *texts, PureList *const *lists, uint32_t state, double value) { if (!isfinite(value)) return false; if (m->state_type[state] == PURE_STATE_NUM) return true; if (value < 0 || value != floor(value)) return false; if (m->state_type[state] == PURE_STATE_INDEX) { - size_t count = pure_list_count(app->lists[m->state_ref[state]]); + size_t count = pure_list_count(lists[m->state_ref[state]]); return value <= (count ? (double)(count - 1) : 0); } - const PureDraft *text = app->texts[m->state_ref[state]]; + const PureDraft *text = texts[m->state_ref[state]]; size_t size = pure_draft_size(text), at = (size_t)value; if (value > (double)size) return false; if (at == size) return true; @@ -212,30 +217,33 @@ static size_t encode_values(const PureModule *m, const double *values, unsigned enum { VALUES_BYTES = 4 + PURE_MODULE_STATES * (2 + PURE_MODULE_NAME + 8) }; -PureResult pure_save_open_values(PureApp *app, const PureModule *m, double *values, bool load) { - bool any = false; - for (uint32_t i = 0; i < m->states; ++i) any = any || scalar(m->state_type[i]); - if (!any || app->stored_values) return PURE_OK; - PureResult result = open_state(app, "", "values", &app->stored_values); - if (result != PURE_OK || !load || !pure_draft_revision(app->stored_values)) return result; +PURE_COLD PureResult pure_save_open_values(PureApp *app, const PureModule *m, double *values, const int *kept, + PureDraft *const *texts, PureList *const *lists) { + bool scalars = false, fresh = false; + for (uint32_t i = 0; i < m->states; ++i) + if (scalar(m->state_type[i])) { + scalars = true; + fresh = fresh || kept[i] < 0; + } + if (!scalars) return PURE_OK; + PureResult result = app->stored_values ? PURE_OK : open_state(app, "", "values", &app->stored_values); + if (result != PURE_OK || !fresh || !pure_draft_revision(app->stored_values)) return result; unsigned char *bytes; size_t size; if ((result = read_whole(app->stored_values, &bytes, &size)) != PURE_OK) return result; bool ok = size >= 4 && !memcmp(bytes, values_magic, 4); for (size_t at = 4; ok && at < size;) { - ok = size - at >= 2 && size - at - 2 >= (size_t)bytes[at + 1] + 8 && name_ok(bytes + at + 2, bytes[at + 1]); - if (!ok) break; - char name[PURE_MODULE_NAME] = {0}; - memcpy(name, bytes + at + 2, bytes[at + 1]); - uint8_t type = bytes[at]; - at += 2 + bytes[at + 1]; + char name[PURE_MODULE_NAME]; + uint8_t type; + if (!(ok = named(bytes, size, &at, 8, &type, name))) break; uint64_t bits = 0; for (unsigned b = 0; b < 8; ++b) bits |= (uint64_t)bytes[at + b] << (8 * b); at += 8; double value; memcpy(&value, &bits, sizeof(value)); int state = pure_module_state(m, name); - if (state >= 0 && m->state_type[state] == type && scalar(type) && fits(app, m, (uint32_t)state, value)) + if (state >= 0 && kept[state] < 0 && m->state_type[state] == type && scalar(type) && + fits(m, texts, lists, (uint32_t)state, value)) values[state] = value; } free(bytes); |
