skip to content
==> 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}