skip to content
==> src/infer_wire.c <==
1/* Frames over a socket, messages, layers. */
2#include "mesh/infer_wire.h"
3#include "mesh/chan.h"
4#include "mesh/rsfmt.h"
5
6#include <errno.h>
7#include <stdarg.h>
8#include <stdlib.h>
9#include <string.h>
10#include <unistd.h>
11
12static int write_all(int fd, const void *p, size_t n) {
13    const char *c = p;
14    while (n) {
15        ssize_t w = write(fd, c, n);
16        if (w < 0 && errno == EINTR) continue;
17        if (w <= 0) return -1;
18        c += w;
19        n -= (size_t)w;
20    }
21    return 0;
22}
23
24int mesh_infer_write_frame(int fd, uint8_t kind, const void *body, size_t len) {
25    uint8_t head[5];
26    uint32_t n = (uint32_t)len + 1;
27    for (int i = 0; i < 4; i++) head[i] = (uint8_t)(n >> (8 * i));
28    head[4] = kind;
29    if (len < 4096) {
30        mesh_buf b = MESH_BUF_INIT;
31        mesh_buf_put(&b, head, 5);
32        mesh_buf_put(&b, body, len);
33        int r = write_all(fd, b.data, b.len);
34        mesh_buf_free(&b);
35        return r;
36    }
37    if (write_all(fd, head, 5) != 0) return -1;
38    return write_all(fd, body, len);
39}
40
41/* std's io::Error text for a failed read_exact. */
42static char *io_err(int e, int eof) {
43    if (eof) return mesh_xstrdup("read: failed to fill whole buffer");
44    return mesh_buf_fmt("read: %s (os error %d)", strerror(e), e);
45}
46
47static int read_exact(int fd, void *p, size_t n, char **err) {
48    char *c = p;
49    while (n) {
50        ssize_t r = read(fd, c, n);
51        if (r < 0 && errno == EINTR) continue;
52        if (r <= 0) { *err = io_err(errno, r == 0); return -1; }
53        c += r;
54        n -= (size_t)r;
55    }
56    return 0;
57}
58
59int mesh_infer_read_frame(int fd, uint8_t *kind, mesh_buf *body, int64_t *wire_us, char **err) {
60    uint8_t head[5];
61    if (read_exact(fd, head, 5, err) != 0) return -1;
62    int64_t t0 = mesh_mono_us();
63    uint32_t n = (uint32_t)head[0] | (uint32_t)head[1] << 8 | (uint32_t)head[2] << 16 | (uint32_t)head[3] << 24;
64    if (n == 0 || n - 1 > MESH_INFER_MAX_FRAME) { *err = mesh_buf_fmt("read: a frame of %u bytes", n); return -1; }
65    size_t at = body->len;
66    mesh_buf_zeros(body, n - 1);
67    if (read_exact(fd, body->data + at, n - 1, err) != 0) { mesh_buf_truncate(body, at); return -1; }
68    *kind = head[4];
69    if (wire_us) *wire_us = mesh_mono_us() - t0;
70    return 0;
71}
72
73void mesh_infer_msg_init(mesh_infer_msg *m) {
74    memset(m, 0, sizeof *m);
75    m->body = mesh_xstrdup("");
76}
77
78void mesh_infer_msg_free(mesh_infer_msg *m) {
79    for (size_t i = 0; i < m->n; i++) { free(m->k[i]); free(m->v[i]); }
80    free(m->k);
81    free(m->v);
82    free(m->body);
83    memset(m, 0, sizeof *m);
84}
85
86void mesh_infer_msg_set(mesh_infer_msg *m, const char *k, const char *v) {
87    size_t i = 0;
88    while (i < m->n && strcmp(m->k[i], k) < 0) i++;
89    if (i < m->n && strcmp(m->k[i], k) == 0) {
90        free(m->v[i]);
91        m->v[i] = mesh_xstrdup(v);
92        return;
93    }
94    if (m->n == m->cap) {
95        m->cap = m->cap ? 2 * m->cap : 8;
96        m->k = mesh_xrealloc(m->k, m->cap * sizeof *m->k);
97        m->v = mesh_xrealloc(m->v, m->cap * sizeof *m->v);
98    }
99    memmove(m->k + i + 1, m->k + i, (m->n - i) * sizeof *m->k);
100    memmove(m->v + i + 1, m->v + i, (m->n - i) * sizeof *m->v);
101    m->k[i] = mesh_xstrdup(k);
102    m->v[i] = mesh_xstrdup(v);
103    m->n++;
104}
105
106void mesh_infer_msg_setf(mesh_infer_msg *m, const char *k, const char *fmt, ...) {
107    mesh_buf b = MESH_BUF_INIT;
108    va_list ap;
109    va_start(ap, fmt);
110    mesh_buf_vprintf(&b, fmt, ap);
111    va_end(ap);
112    mesh_infer_msg_set(m, k, mesh_buf_str(&b));
113    mesh_buf_free(&b);
114}
115
116void mesh_infer_msg_set_body(mesh_infer_msg *m, const char *body) {
117    free(m->body);
118    m->body = mesh_xstrdup(body);
119}
120
121const char *mesh_infer_msg_get(const mesh_infer_msg *m, const char *k) {
122    for (size_t i = 0; i < m->n; i++) if (strcmp(m->k[i], k) == 0) return m->v[i];
123    return NULL;
124}
125
126int mesh_infer_msg_num(const mesh_infer_msg *m, const char *k, uint64_t *out, char **err) {
127    const char *v = mesh_infer_msg_get(m, k);
128    if (!v) { *err = mesh_buf_fmt("missing %s", k); return -1; }
129    const char *p = v + (*v == '+');
130    uint64_t x = 0;
131    int ok = *p != 0;
132    for (; ok && *p; p++) {
133        ok = *p >= '0' && *p <= '9' && x <= (UINT64_MAX - (uint64_t)(*p - '0')) / 10;
134        x = x * 10 + (uint64_t)(*p - '0');
135    }
136    if (!ok) { *err = mesh_buf_fmt("%s: not a number", k); return -1; }
137    *out = x;
138    return 0;
139}
140
141void mesh_infer_msg_encode(const mesh_infer_msg *m, mesh_buf *out) {
142    for (size_t i = 0; i < m->n; i++) {
143        mesh_buf_printf(out, "%s=", m->k[i]);
144        for (const char *c = m->v[i]; *c; c++) mesh_buf_putc(out, *c == '\n' ? ' ' : *c);
145        mesh_buf_putc(out, '\n');
146    }
147    mesh_buf_putc(out, '\n');
148    mesh_buf_puts(out, m->body);
149}
150
151int mesh_infer_msg_decode(const void *bytes, size_t n, mesh_infer_msg *out, char **err) {
152    mesh_infer_msg_init(out);
153    if (!mesh_rs_utf8_valid(bytes, n) || memchr(bytes, 0, n)) {
154        /* A NUL is valid UTF-8 in Rust; C strings cannot hold it: refused the same way. */
155        *err = mesh_xstrdup("a message that is not UTF-8");
156        mesh_infer_msg_free(out);
157        return -1;
158    }
159    char *s = mesh_xstrndup(bytes, n);
160    char *sep = strstr(s, "\n\n");
161    const char *body = "";
162    if (sep) { *sep = 0; body = sep + 2; }
163    else if (n && s[n - 1] == '\n') s[n - 1] = 0;
164    char *line = s;
165    int rc = 0;
166    while (line) {
167        char *nl = strchr(line, '\n');
168        if (nl) *nl = 0;
169        size_t ln = strlen(line);
170        if (ln && line[ln - 1] == '\r') line[--ln] = 0;
171        if (ln) {
172            char *eq = strchr(line, '=');
173            if (!eq) {
174                mesh_buf e = MESH_BUF_INIT;
175                mesh_buf_puts(&e, "a header line without '=': ");
176                mesh_rs_debug_cstr(&e, line);
177                *err = mesh_buf_take(&e);
178                rc = -1;
179                break;
180            }
181            *eq = 0;
182            mesh_infer_msg_set(out, line, eq + 1);
183        }
184        line = nl ? nl + 1 : NULL;
185    }
186    if (rc == 0) mesh_infer_msg_set_body(out, body);
187    else mesh_infer_msg_free(out);
188    free(s);
189    return rc;
190}
191
192uint64_t mesh_infer_layer_sum(const void *bytes, size_t n) {
193    const uint8_t *b = bytes;
194    uint64_t s = 0;
195    for (size_t i = 0; i < n; i += 8) {
196        uint64_t w = 0;
197        for (size_t k = 0; k < 8 && i + k < n; k++) w |= (uint64_t)b[i + k] << (8 * k);
198        s += w;
199    }
200    return s;
201}
202
203void mesh_infer_layer_frame(mesh_buf *out, uint32_t layer, uint32_t rows, const void *bytes, size_t n) {
204    mesh_buf_reserve(out, 16 + n);
205    mesh_buf_u32le(out, layer);
206    mesh_buf_u32le(out, rows);
207    mesh_buf_u64le(out, mesh_infer_layer_sum(bytes, n));
208    mesh_buf_put(out, bytes, n);
209}
210
211int mesh_infer_parse_layer(const void *bytes, size_t n, uint32_t *layer, uint32_t *rows, const uint8_t **data, size_t *len,
212                           char **err) {
213    const uint8_t *b = bytes;
214    if (n < 16) { *err = mesh_xstrdup("a short KV layer frame"); return -1; }
215    uint32_t l = 0, r = 0;
216    uint64_t sum = 0;
217    for (int i = 0; i < 4; i++) { l |= (uint32_t)b[i] << (8 * i); r |= (uint32_t)b[4 + i] << (8 * i); }
218    for (int i = 0; i < 8; i++) sum |= (uint64_t)b[8 + i] << (8 * i);
219    if (mesh_infer_layer_sum(b + 16, n - 16) != sum) { *err = mesh_buf_fmt("KV layer %u: checksum mismatch (%zu bytes)", l, n - 16); return -1; }
220    *layer = l;
221    *rows = r;
222    *data = b + 16;
223    *len = n - 16;
224    return 0;
225}