==> src/fleet_persist.c <==1/* fleet.rs Persist: encode and decode. */
2#include "mesh/fleet_persist.h"
3#include "mesh/parse.h"
4#include "mesh/rust_fmt.h"
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9
10mfp_persist mfp_clone(const mfp_persist *p) {
11 return (mfp_persist){p->term, p->has_voted, p->voted, mfd_doc_clone(&p->accepted), mfd_doc_clone(&p->committed)};
12}
13
14bool mfp_eq(const mfp_persist *a, const mfp_persist *b) {
15 return a->term == b->term && a->has_voted == b->has_voted && (!a->has_voted || a->voted == b->voted) && mfd_doc_eq(&a->accepted, &b->accepted) &&
16 mfd_doc_eq(&a->committed, &b->committed);
17}
18
19void mfp_free(mfp_persist *p) {
20 mfd_doc_free(&p->accepted);
21 mfd_doc_free(&p->committed);
22 *p = (mfp_persist){0};
23}
24
25char *mfp_encode(const mfp_persist *p, uint32_t n) {
26 char *a = mfd_encode(&p->accepted), *c = mfd_encode(&p->committed);
27 char voted[16];
28 if (p->has_voted) snprintf(voted, sizeof voted, "%u", p->voted);
29 else strcpy(voted, "-");
30 char *s = mt_fmt("# meshd fleet state: term, vote, the accepted and committed desired state\nsystems %u\nterm %llu\nvoted %s\naccepted %zu\n%scommitted %zu\n%s", n,
31 (unsigned long long)p->term, voted, strlen(a), a, strlen(c), c);
32 free(a);
33 free(c);
34 return s;
35}
36
37static char *bad(const char *line) {
38 char *q = rf_debug_str(line);
39 char *e = mt_fmt("bad line %s", q);
40 free(q);
41 return e;
42}
43
44bool mfp_decode(const char *text, uint32_t n, mfp_persist *out, char **err) {
45 mfp_persist p = {0};
46 const char *rest = text;
47 int docs = 0;
48 while (*rest) {
49 const char *nl = strchr(rest, '\n');
50 char *line = nl ? mt_strndup(rest, (size_t)(nl - rest)) : mt_strdup(rest);
51 rest = nl ? nl + 1 : rest + strlen(rest);
52 mt_strs w = mt_words(line);
53 bool ok = true;
54 uint64_t u;
55 if (w.n == 0 || w.v[0][0] == '#') {
56 } else if (w.n == 2 && strcmp(w.v[0], "systems") == 0) {
57 uint32_t s;
58 if (!mp_u32(w.v[1], &s)) ok = false, *err = bad(line);
59 else if (s != n) ok = false, *err = mt_fmt("written for a cluster of %u, this one has %u (remove the file to start the fleet afresh)", s, n);
60 } else if (w.n == 2 && strcmp(w.v[0], "term") == 0) {
61 if (!mp_u64(w.v[1], &p.term)) ok = false, *err = bad(line);
62 } else if (w.n == 2 && strcmp(w.v[0], "voted") == 0) {
63 if (strcmp(w.v[1], "-") == 0) p.has_voted = false;
64 else if (mp_u32(w.v[1], &p.voted)) p.has_voted = true;
65 else ok = false, *err = bad(line);
66 } else if (w.n == 2 && (strcmp(w.v[0], "accepted") == 0 || strcmp(w.v[0], "committed") == 0)) {
67 bool acc = strcmp(w.v[0], "accepted") == 0;
68 if (!mp_u64(w.v[1], &u)) ok = false, *err = bad(line);
69 else if (strlen(rest) < u) ok = false, *err = mt_fmt("%s: truncated", w.v[0]);
70 else {
71 char *t = mt_strndup(rest, (size_t)u);
72 mfd_doc d;
73 if (mfd_decode(t, &d, err)) {
74 mfd_doc *slot = acc ? &p.accepted : &p.committed;
75 mfd_doc_free(slot);
76 *slot = d;
77 rest += u;
78 docs++;
79 } else ok = false;
80 free(t);
81 }
82 } else {
83 ok = false;
84 *err = bad(line);
85 }
86 mt_strs_free(&w);
87 free(line);
88 if (!ok) {
89 mfp_free(&p);
90 return false;
91 }
92 }
93 if (docs != 2) {
94 *err = mt_strdup("incomplete (no accepted and committed documents)");
95 mfp_free(&p);
96 return false;
97 }
98 *out = p;
99 return true;
100}