skip to content
==> src/wire.c <==
1/* jobs.rs's W and R. */
2#include "mesh/jobs/wire.h"
3
4#include <stdlib.h>
5#include <string.h>
6
7void mj_w_u8(msb *w, uint8_t v) { msb_put(w, &v, 1); }
8
9void mj_w_u32(msb *w, uint32_t v) {
10    uint8_t b[4];
11    for (int i = 0; i < 4; i++) b[i] = (uint8_t)(v >> 8 * i);
12    msb_put(w, b, 4);
13}
14
15void mj_w_u64(msb *w, uint64_t v) {
16    uint8_t b[8];
17    for (int i = 0; i < 8; i++) b[i] = (uint8_t)(v >> 8 * i);
18    msb_put(w, b, 8);
19}
20
21void mj_w_bytes(msb *w, const void *b, size_t n) {
22    mj_w_u32(w, (uint32_t)n);
23    msb_put(w, b, n);
24}
25
26void mj_w_str(msb *w, const char *s) { mj_w_bytes(w, s, strlen(s)); }
27
28void mj_w_ranges(msb *w, const mj_ranges *r) {
29    mj_w_u32(w, (uint32_t)r->n);
30    for (size_t i = 0; i < r->n; i++) {
31        mj_w_u64(w, r->v[i][0]);
32        mj_w_u64(w, r->v[i][1]);
33    }
34}
35
36void mj_rd_free(mj_rd *r) {
37    free(r->err);
38    r->err = NULL;
39}
40
41char *mj_rd_take_err(mj_rd *r) {
42    char *e = r->err ? r->err : mstrdup("job frame: bad");
43    r->err = NULL;
44    return e;
45}
46
47bool mj_rd_fail(mj_rd *r, char *msg) {
48    if (r->err) free(msg);
49    else r->err = msg;
50    return false;
51}
52
53bool mj_rd_take(mj_rd *r, size_t n, const uint8_t **p) {
54    if (r->err) return false;
55    if (r->n - r->at < n)
56        return mj_rd_fail(r, mfmt("job frame: short (%zu of %zu bytes at %zu)", r->n - r->at, n, r->at));
57    *p = r->b + r->at;
58    r->at += n;
59    return true;
60}
61
62bool mj_rd_u8(mj_rd *r, uint8_t *v) {
63    const uint8_t *p;
64    if (!mj_rd_take(r, 1, &p)) return false;
65    *v = p[0];
66    return true;
67}
68
69bool mj_rd_u32(mj_rd *r, uint32_t *v) {
70    const uint8_t *p;
71    if (!mj_rd_take(r, 4, &p)) return false;
72    *v = (uint32_t)p[0] | (uint32_t)p[1] << 8 | (uint32_t)p[2] << 16 | (uint32_t)p[3] << 24;
73    return true;
74}
75
76bool mj_rd_u64(mj_rd *r, uint64_t *v) {
77    const uint8_t *p;
78    if (!mj_rd_take(r, 8, &p)) return false;
79    uint64_t x = 0;
80    for (int i = 7; i >= 0; i--) x = x << 8 | p[i];
81    *v = x;
82    return true;
83}
84
85bool mj_rd_bytes(mj_rd *r, uint8_t **b, size_t *n) {
86    uint32_t len;
87    const uint8_t *p;
88    if (!mj_rd_u32(r, &len) || !mj_rd_take(r, len, &p)) return false;
89    *b = mxalloc(len);
90    memcpy(*b, p, len);
91    *n = len;
92    return true;
93}
94
95bool mj_utf8_ok(const uint8_t *b, size_t n) {
96    size_t i = 0;
97    while (i < n) {
98        uint8_t c = b[i];
99        if (c < 0x80) {
100            i++;
101            continue;
102        }
103        size_t k;
104        uint32_t cp, min;
105        if ((c & 0xe0) == 0xc0) k = 1, cp = c & 0x1f, min = 0x80;
106        else if ((c & 0xf0) == 0xe0) k = 2, cp = c & 0x0f, min = 0x800;
107        else if ((c & 0xf8) == 0xf0) k = 3, cp = c & 0x07, min = 0x10000;
108        else return false;
109        if (n - i <= k) return false;
110        for (size_t j = 1; j <= k; j++) {
111            if ((b[i + j] & 0xc0) != 0x80) return false;
112            cp = cp << 6 | (b[i + j] & 0x3f);
113        }
114        if (cp < min || cp > 0x10ffff || (cp >= 0xd800 && cp <= 0xdfff)) return false;
115        i += k + 1;
116    }
117    return true;
118}
119
120/* The length of the valid UTF-8 sequence at b (1..4), or 0 with *bad the length of the maximal
121 * invalid prefix to replace (Unicode's "maximal subpart", as Rust's Utf8Chunks does). */
122static size_t seq_at(const uint8_t *b, size_t n, size_t *bad) {
123    uint8_t c = b[0];
124    if (c < 0x80) return 1;
125    size_t k;
126    uint8_t lo = 0x80, hi = 0xbf;
127    if (c >= 0xc2 && c <= 0xdf) k = 1;
128    else if (c >= 0xe0 && c <= 0xef) {
129        k = 2;
130        if (c == 0xe0) lo = 0xa0;
131        if (c == 0xed) hi = 0x9f;
132    } else if (c >= 0xf0 && c <= 0xf4) {
133        k = 3;
134        if (c == 0xf0) lo = 0x90;
135        if (c == 0xf4) hi = 0x8f;
136    } else {
137        *bad = 1;
138        return 0;
139    }
140    size_t i = 1;
141    for (; i <= k && i < n; i++) {
142        uint8_t x = b[i];
143        if (i == 1 ? (x < lo || x > hi) : (x & 0xc0) != 0x80) break;
144    }
145    if (i == k + 1) return k + 1;
146    *bad = i;
147    return 0;
148}
149
150char *mj_utf8_lossy(const uint8_t *b, size_t n) {
151    msb out = {0};
152    size_t i = 0;
153    while (i < n) {
154        size_t bad = 0, k = seq_at(b + i, n - i, &bad);
155        if (k) {
156            msb_put(&out, b + i, k);
157            i += k;
158        } else {
159            msb_puts(&out, "\xef\xbf\xbd");
160            i += bad;
161        }
162    }
163    return msb_take(&out);
164}
165
166bool mj_rd_str(mj_rd *r, char **s) {
167    uint32_t len;
168    const uint8_t *p;
169    if (!mj_rd_u32(r, &len) || !mj_rd_take(r, len, &p)) return false;
170    if (!mj_utf8_ok(p, len)) return mj_rd_fail(r, mstrdup("job frame: not UTF-8"));
171    *s = mstrndup((const char *)p, len);
172    return true;
173}
174
175bool mj_rd_ranges(mj_rd *r, mj_ranges *out) {
176    uint32_t n;
177    if (!mj_rd_u32(r, &n)) return false;
178    mj_ranges x = {0};
179    for (uint32_t i = 0; i < n; i++) {
180        uint64_t s, e;
181        if (!mj_rd_u64(r, &s) || !mj_rd_u64(r, &e)) {
182            mj_ranges_free(&x);
183            return false;
184        }
185        mj_ranges_insert(&x, s, e);
186    }
187    *out = x;
188    return true;
189}