skip to content
==> src/spec.c <==
1/* jobs.rs's Mode and JobSpec. */
2#include "mesh/jobs/spec.h"
3#include "mesh/debug_str.h"
4#include "mesh/rs_parse.h"
5
6#include <stdlib.h>
7#include <string.h>
8
9const char *mj_mode_word(mj_mode m) {
10    switch (m) {
11    case MJ_MODE_COLLECT: return "collect";
12    case MJ_MODE_NONZERO: return "nonzero";
13    default: return "none";
14    }
15}
16
17bool mj_mode_parse(const char *s, mj_mode *out) {
18    if (strcmp(s, "collect") == 0) *out = MJ_MODE_COLLECT;
19    else if (strcmp(s, "nonzero") == 0) *out = MJ_MODE_NONZERO;
20    else if (strcmp(s, "none") == 0) *out = MJ_MODE_NONE;
21    else return false;
22    return true;
23}
24
25mj_setup *mj_setup_clone(const mj_setup *s, size_t n) {
26    if (!n) return NULL;
27    mj_setup *c = mxalloc(n * sizeof *c);
28    memcpy(c, s, n * sizeof *c);
29    return c;
30}
31
32void mj_spec_free(mj_spec *s) {
33    free(s->program);
34    mj_ranges_free(&s->items);
35    free(s->setup);
36    memset(s, 0, sizeof *s);
37}
38
39mj_spec mj_spec_clone(const mj_spec *s) {
40    mj_spec c = *s;
41    c.program = mstrdup(s->program ? s->program : "");
42    c.items = mj_ranges_clone(&s->items);
43    c.setup = mj_setup_clone(s->setup, s->nsetup);
44    return c;
45}
46
47bool mj_spec_eq(const mj_spec *a, const mj_spec *b) {
48    return a->id == b->id && strcmp(a->program, b->program) == 0 && a->op == b->op && mj_ranges_eq(&a->items, &b->items) &&
49           a->priority == b->priority && a->attempts == b->attempts && a->deadline_ms == b->deadline_ms && a->mode == b->mode &&
50           a->nsetup == b->nsetup && (a->nsetup == 0 || memcmp(a->setup, b->setup, a->nsetup * sizeof *a->setup) == 0) &&
51           a->submitted_ms == b->submitted_ms && a->members == b->members;
52}
53
54bool mj_spec_endless(const mj_spec *s) {
55    uint64_t e;
56    return mj_ranges_end(&s->items, &e) && e == UINT64_MAX;
57}
58
59bool mj_spec_runs(const mj_spec *s, const char *name) {
60    size_t n = strlen(name);
61    const char *p = s->program;
62    for (;;) {
63        const char *c = strchr(p, ',');
64        size_t len = c ? (size_t)(c - p) : strlen(p);
65        if (len == n && memcmp(p, name, n) == 0) return true;
66        if (!c) return false;
67        p = c + 1;
68    }
69}
70
71size_t mj_spec_first_program(const mj_spec *s, const char **p) {
72    *p = s->program;
73    const char *c = strchr(s->program, ',');
74    return c ? (size_t)(c - s->program) : strlen(s->program);
75}
76
77void mj_spec_encode(const mj_spec *s, msb *out) {
78    msb_printf(out, "id %llu program %s op %u items ", (unsigned long long)s->id, s->program, s->op);
79    mj_ranges_word(&s->items, out);
80    msb_printf(out, " priority %d attempts %u deadline %llu mode %s setup ", s->priority, s->attempts,
81               (unsigned long long)s->deadline_ms, mj_mode_word(s->mode));
82    if (!s->nsetup) msb_putc(out, '-');
83    for (size_t i = 0; i < s->nsetup; i++) msb_printf(out, "%s%u:%u", i ? "," : "", s->setup[i].op, s->setup[i].arg);
84    msb_printf(out, " submitted %llu members 0x%x", (unsigned long long)s->submitted_ms, s->members);
85}
86
87/* Rust's char::is_whitespace for ASCII (what split_whitespace splits on). */
88static bool ws(char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; }
89
90typedef struct {
91    const char *p;
92    size_t n;
93} word;
94
95static size_t split(const char *t, word *w, size_t max) {
96    size_t k = 0;
97    const char *p = t;
98    while (*p) {
99        while (*p && ws(*p)) p++;
100        if (!*p) break;
101        const char *s = p;
102        while (*p && !ws(*p)) p++;
103        if (k < max) w[k] = (word){s, (size_t)(p - s)};
104        k++;
105    }
106    return k;
107}
108
109static bool is(word w, const char *s) { return strlen(s) == w.n && memcmp(w.p, s, w.n) == 0; }
110
111static bool bad(const char *t, char **err) {
112    if (err) {
113        msb b = {0};
114        msb_puts(&b, "job spec ");
115        mdebug_str(&b, t, strlen(t));
116        *err = msb_take(&b);
117    }
118    return false;
119}
120
121/* `o:a,o:a`: each a decimal u32 pair. */
122static bool parse_setup(const char *p, size_t n, mj_setup **out, size_t *count) {
123    mj_setup *v = NULL;
124    size_t k = 0;
125    const char *end = p + n;
126    for (;;) {
127        const char *c = memchr(p, ',', (size_t)(end - p));
128        const char *e = c ? c : end;
129        const char *colon = memchr(p, ':', (size_t)(e - p));
130        uint32_t o, a;
131        if (!colon || !rs_u32(p, (size_t)(colon - p), &o) || !rs_u32(colon + 1, (size_t)(e - colon - 1), &a)) {
132            free(v);
133            return false;
134        }
135        v = mxrealloc(v, (k + 1) * sizeof *v);
136        v[k++] = (mj_setup){o, a};
137        if (!c) break;
138        p = c + 1;
139    }
140    *out = v;
141    *count = k;
142    return true;
143}
144
145bool mj_spec_decode(const char *t, mj_spec *out, char **err) {
146    static const char *const keys[] = {"id", "program", "op", "items", "priority", "attempts", "deadline",
147                                       "mode", "setup", "submitted", "members"};
148    word w[22];
149    if (split(t, w, 22) != 22) return bad(t, err);
150    for (int i = 0; i < 11; i++)
151        if (!is(w[2 * i], keys[i])) return bad(t, err);
152    mj_spec s = {0};
153    /* In the Rust's order: members first, then the rest as written. */
154    word mm = w[21];
155    while (mm.n >= 2 && memcmp(mm.p, "0x", 2) == 0) mm.p += 2, mm.n -= 2;
156    uint64_t v;
157    if (!rs_parse_uint(mm.p, mm.n, 16, UINT32_MAX, &v)) return bad(t, err);
158    s.members = (uint32_t)v;
159    if (!rs_u64(w[1].p, w[1].n, &s.id)) return bad(t, err);
160    s.program = mstrndup(w[3].p, w[3].n);
161    char *it = mstrndup(w[7].p, w[7].n);
162    bool ok = rs_u32(w[5].p, w[5].n, &s.op);
163    if (!ok) {
164        free(it);
165        mj_spec_free(&s);
166        return bad(t, err);
167    }
168    ok = mj_ranges_parse(it, &s.items, err);
169    free(it);
170    if (!ok) {
171        mj_spec_free(&s);
172        return false;
173    }
174    char *mode = mstrndup(w[15].p, w[15].n);
175    ok = rs_i32(w[9].p, w[9].n, &s.priority) && rs_u32(w[11].p, w[11].n, &s.attempts) && rs_u64(w[13].p, w[13].n, &s.deadline_ms) &&
176         mj_mode_parse(mode, &s.mode) && (is(w[17], "-") || parse_setup(w[17].p, w[17].n, &s.setup, &s.nsetup)) &&
177         rs_u64(w[19].p, w[19].n, &s.submitted_ms);
178    free(mode);
179    if (!ok) {
180        mj_spec_free(&s);
181        return bad(t, err);
182    }
183    *out = s;
184    return true;
185}