==> 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}