aboutsummaryrefslogtreecommitdiffstats
path: root/prototypes/connections/module.c
diff options
context:
space:
mode:
Diffstat (limited to 'prototypes/connections/module.c')
-rw-r--r--prototypes/connections/module.c151
1 files changed, 116 insertions, 35 deletions
diff --git a/prototypes/connections/module.c b/prototypes/connections/module.c
index 3d3ee3e..362919b 100644
--- a/prototypes/connections/module.c
+++ b/prototypes/connections/module.c
@@ -1,4 +1,5 @@
#include "module.h"
+#include "module-save.h"
#include "module-text.h"
#include <math.h>
#include <stdio.h>
@@ -1056,14 +1057,27 @@ static PureList *new_list(const PureModule *m, uint32_t record) {
PureResult pure_app_start(PureApp *app, const PureModule *module, PureData data) {
*app = (PureApp){.module = module, .data = data, .fault_pc = UINT32_MAX};
memcpy(app->values, module->initial, sizeof(double) * module->states);
- for (uint32_t i = 0; i < module->states; ++i) {
- PureResult result = PURE_OK;
- if (module->state_type[i] == PURE_STATE_TEXT) result = new_text(&app->texts[i], module, module->state_ref[i]);
- if (module->state_type[i] == PURE_STATE_LIST && !(app->lists[i] = new_list(module, module->state_ref[i])))
- result = PURE_ALLOCATION;
- if (result != PURE_OK) { pure_app_stop(app); return result; }
+ bool kept = pure_save_enabled(app);
+ PureResult result = PURE_OK;
+ for (uint32_t i = 0; i < module->states && result == PURE_OK; ++i) {
+ if (module->state_type[i] == PURE_STATE_TEXT)
+ result = kept ? pure_save_open_text(app, module, i, &app->texts[i], &app->saved[i]) :
+ new_text(&app->texts[i], module, module->state_ref[i]);
+ if (module->state_type[i] == PURE_STATE_LIST) {
+ if (kept) result = pure_save_open_list(app, module, i, &app->lists[i], &app->list_drafts[i], &app->saved[i]);
+ else if (!(app->lists[i] = new_list(module, module->state_ref[i]))) result = PURE_ALLOCATION;
+ }
}
- return PURE_OK;
+ /* Stored numbers, cursors and indices apply once their texts and lists are in. */
+ if (result == PURE_OK && kept) result = pure_save_open_values(app, module, app->values, true);
+ if (result == PURE_OK) result = pure_app_save(app);
+ if (result != PURE_OK) pure_app_stop(app);
+ return result;
+}
+
+PureResult pure_app_save(PureApp *app) {
+ int64_t revision;
+ return pure_save_commit(app, app->values, NULL, &revision);
}
void pure_app_finish(PureApp *app) {
@@ -1081,33 +1095,45 @@ void pure_app_stop(PureApp *app) {
for (uint32_t i = 0; i < PURE_MODULE_STATES; ++i) {
pure_draft_close(app->texts[i]);
pure_list_free(app->lists[i]);
+ pure_draft_close(app->list_drafts[i]);
}
+ pure_draft_close(app->stored_values);
pure_undo_free(&app->undo);
free(app->arena.bytes);
free(app->view_arena.bytes);
memset(app, 0, sizeof(*app));
}
-/* Every text draft is held for the event; on failure each returns to the
- * version it had when the event began. */
-typedef struct { PureVersion before[PURE_MODULE_STATES]; } Texts;
+/* Every text draft and stored list draft is held for the event; on failure
+ * each returns to the version it had when the event began. */
+typedef struct { PureVersion before[PURE_MODULE_STATES], lists[PURE_MODULE_STATES]; } Texts;
static void hold_texts(PureApp *app, Texts *texts) {
- for (uint32_t i = 0; i < app->module->states; ++i)
+ for (uint32_t i = 0; i < app->module->states; ++i) {
if (app->texts[i]) {
pure_draft_hold(app->texts[i], true);
texts->before[i] = pure_draft_version(app->texts[i]);
}
+ if (app->list_drafts[i]) {
+ pure_draft_hold(app->list_drafts[i], true);
+ texts->lists[i] = pure_draft_version(app->list_drafts[i]);
+ }
+ }
}
-/* Texts return to their versions and lists undo their log, or both keep
- * what the event did. */
+/* Texts and stored lists return to their versions and lists undo their log,
+ * or all keep what the event did. */
static void settle(PureApp *app, const Texts *texts, bool keep) {
- for (uint32_t i = 0; i < app->module->states; ++i)
+ for (uint32_t i = 0; i < app->module->states; ++i) {
if (app->texts[i]) {
if (!keep) pure_draft_restore(app->texts[i], texts->before[i]);
pure_draft_hold(app->texts[i], false);
}
+ if (app->list_drafts[i]) {
+ if (!keep) pure_draft_restore(app->list_drafts[i], texts->lists[i]);
+ pure_draft_hold(app->list_drafts[i], false);
+ }
+ }
if (keep) pure_undo_commit(&app->undo);
else pure_undo_rollback(&app->undo);
}
@@ -1126,11 +1152,15 @@ static PureResult run_handlers(PureApp *app, uint32_t event, Event *ev, double *
return PURE_OK;
}
+/* A key or text event stores what it changed before it counts; a frame
+ * leaves that to the next event (frames come 60 times a second). */
static PureResult dispatch(PureApp *app, uint32_t event, Event *ev) {
double pending[PURE_MODULE_STATES];
Texts texts;
hold_texts(app, &texts);
PureResult result = run_handlers(app, event, ev, pending);
+ int64_t revision;
+ if (result == PURE_OK && event) result = pure_save_commit(app, pending, NULL, &revision);
settle(app, &texts, result == PURE_OK);
if (result == PURE_OK) {
memcpy(app->values, pending, sizeof(pending));
@@ -1208,8 +1238,8 @@ PureResult pure_app_message(PureApp *app, uint32_t input, const char *key, int64
if (result == PURE_OK && app->has_reply &&
((app->reply.source == FROM_DOCUMENT && ev.modified) || app->reply.source == FROM_TEXT))
result = keep_reply(app);
- if (result == PURE_OK && ev.modified)
- result = app->data.commit(app->data.context, app->document, &app->revision);
+ if (result == PURE_OK)
+ result = pure_save_commit(app, pending, ev.modified ? app->document : NULL, &app->revision);
/* States, texts and lists change only with a successful commit (or no document edit). */
settle(app, &texts, result == PURE_OK);
if (result == PURE_OK) {
@@ -1278,21 +1308,18 @@ PureResult pure_app_cell_text(PureApp *app, uint32_t column, size_t row, bool (*
return result == PURE_OK ? visit_value(app, &app->view_arena, slot.bytes, visit, context) : result;
}
-/* A kept list keeps its items. When its record changed, each field takes the
- * old field of the same name and kind, or its default. */
-static PureResult keep_list(PureList **out, PureList *from, const PureModule *old, uint32_t old_record,
- const PureModule *m, uint32_t record) {
- const PureRecord *r = &m->record[record], *o = &old->record[old_record];
+/* A list with record's fields: each takes the field of `from` with the same
+ * name and kind, or its default. */
+PureResult pure_list_migrate(PureList **out, const PureList *from, const char *const *names,
+ const uint8_t *text, unsigned count, const PureModule *m, uint32_t record) {
+ const PureRecord *r = &m->record[record];
int source[PURE_RECORD_FIELDS];
- bool same = r->count == o->count;
for (unsigned f = 0; f < r->count; ++f) {
source[f] = -1;
- for (unsigned k = 0; k < o->count; ++k)
- if (!strcmp(m->record_field_names[r->first + f], old->record_field_names[o->first + k]) &&
- m->record_field_text[r->first + f] == old->record_field_text[o->first + k]) source[f] = (int)k;
- same = same && source[f] == (int)f;
+ for (unsigned k = 0; k < count; ++k)
+ if (!strcmp(m->record_field_names[r->first + f], names[k]) && m->record_field_text[r->first + f] == text[k])
+ source[f] = (int)k;
}
- if (same) { *out = from; return PURE_OK; }
if (!(*out = new_list(m, record))) return PURE_ALLOCATION;
PureResult result = PURE_OK;
for (size_t at = 0; result == PURE_OK && at < pure_list_count(from); ++at) {
@@ -1311,26 +1338,55 @@ static PureResult keep_list(PureList **out, PureList *from, const PureModule *ol
return result;
}
+/* A kept list keeps its items; with the same record it is the same list. */
+static PureResult keep_list(PureList **out, PureList *from, const PureModule *old, uint32_t old_record,
+ const PureModule *m, uint32_t record) {
+ const PureRecord *r = &m->record[record], *o = &old->record[old_record];
+ const char *names[PURE_RECORD_FIELDS];
+ bool same = r->count == o->count;
+ for (unsigned k = 0; k < o->count; ++k) {
+ names[k] = old->record_field_names[o->first + k];
+ same = same && !strcmp(names[k], m->record_field_names[r->first + k]) &&
+ old->record_field_text[o->first + k] == m->record_field_text[r->first + k];
+ }
+ if (same) { *out = from; return PURE_OK; }
+ return pure_list_migrate(out, from, names, old->record_field_text + o->first, o->count, m, record);
+}
+
+/* States keep their values by name and kind. With stored states, a state new
+ * to this app starts from its stored value, a list whose record changed is
+ * stored again, and the next event or save publishes the new module's states;
+ * a state the new module drops stays stored as it was. */
PureResult pure_app_replace(PureApp *app, const PureModule *module) {
pure_app_finish(app);
const PureModule *old = app->module;
+ bool stored = pure_save_enabled(app);
double values[PURE_MODULE_STATES] = {0};
- PureDraft *texts[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}}, before[PURE_MODULE_STATES] = {{0, 0}};
int kept[PURE_MODULE_STATES];
+ bool rewrite[PURE_MODULE_STATES] = {false};
PureResult result = PURE_OK;
for (uint32_t i = 0; i < module->states; ++i) {
int previous = pure_module_state(old, module->state_names[i]);
kept[i] = previous >= 0 && old->state_type[previous] == module->state_type[i] ? previous : -1;
+ if (kept[i] >= 0) saved[i] = app->saved[kept[i]];
if (module->state_type[i] == PURE_STATE_TEXT) {
if (kept[i] >= 0) texts[i] = app->texts[kept[i]];
- else if (result == PURE_OK) result = new_text(&texts[i], module, module->state_ref[i]);
+ else if (result == PURE_OK)
+ result = stored ? pure_save_open_text(app, module, i, &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;
- if (kept[i] >= 0)
+ if (kept[i] >= 0) {
result = keep_list(&lists[i], app->lists[kept[i]], old, old->state_ref[kept[i]], module,
module->state_ref[i]);
- else if (!(lists[i] = new_list(module, module->state_ref[i]))) result = PURE_ALLOCATION;
+ list_drafts[i] = app->list_drafts[kept[i]];
+ rewrite[i] = result == PURE_OK && list_drafts[i] && lists[i] != app->lists[kept[i]];
+ } else if (stored) {
+ result = pure_save_open_list(app, module, i, &lists[i], &list_drafts[i], &saved[i]);
+ } 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];
}
/* A cursor or index keeps its position only while its text or list is the
@@ -1341,25 +1397,50 @@ PureResult pure_app_replace(PureApp *app, const PureModule *module) {
bool same = kept[i] >= 0 && kept[owner] >= 0 && old->state_ref[kept[i]] == (unsigned)kept[owner];
values[i] = same ? app->values[kept[i]] : 0;
}
+ if (result == PURE_OK && stored) result = pure_save_open_values(app, module, values, false);
+ /* Store the lists whose record changed last, so that a failure can put
+ * every draft back as it was. */
+ for (uint32_t i = 0; i < module->states; ++i)
+ if (rewrite[i]) {
+ before[i] = pure_draft_version(list_drafts[i]);
+ pure_draft_hold(list_drafts[i], true);
+ }
+ for (uint32_t i = 0; i < module->states && result == PURE_OK; ++i)
+ if (rewrite[i]) result = pure_save_rewrite_list(module, i, lists[i], list_drafts[i]);
+ for (uint32_t i = 0; i < module->states; ++i)
+ if (rewrite[i]) {
+ if (result != PURE_OK) pure_draft_restore(list_drafts[i], before[i]);
+ pure_draft_hold(list_drafts[i], false);
+ }
if (result != PURE_OK) {
for (uint32_t i = 0; i < module->states; ++i) {
- if (texts[i] && kept[i] < 0) pure_draft_close(texts[i]);
- if (lists[i] && (kept[i] < 0 || lists[i] != app->lists[kept[i]])) pure_list_free(lists[i]);
+ if (kept[i] >= 0) {
+ if (lists[i] && lists[i] != app->lists[kept[i]]) pure_list_free(lists[i]);
+ continue;
+ }
+ pure_draft_close(texts[i]);
+ pure_list_free(lists[i]);
+ pure_draft_close(list_drafts[i]);
}
return result;
}
for (uint32_t i = 0; i < old->states; ++i) {
- bool reused = false;
- for (uint32_t k = 0; k < module->states; ++k)
+ bool reused = false, draft_reused = false;
+ for (uint32_t k = 0; k < module->states; ++k) {
reused = reused || (texts[k] && texts[k] == app->texts[i]) || (lists[k] && lists[k] == app->lists[i]);
+ draft_reused = draft_reused || (list_drafts[k] && list_drafts[k] == app->list_drafts[i]);
+ }
if (!reused) {
pure_draft_close(app->texts[i]);
pure_list_free(app->lists[i]);
}
+ if (!draft_reused) pure_draft_close(app->list_drafts[i]);
}
memcpy(app->values, values, sizeof(values));
memcpy(app->texts, texts, sizeof(texts));
memcpy(app->lists, lists, sizeof(lists));
+ memcpy(app->list_drafts, list_drafts, sizeof(list_drafts));
+ memcpy(app->saved, saved, sizeof(saved));
app->module = module;
return PURE_OK;
}