skip to content
==> src/views.c <==
1/* crates/mesh-top/src/views.rs, ported. */
2#include "mesh/top_views.h"
3
4#include <ctype.h>
5#include <errno.h>
6#include <math.h>
7#include <stdlib.h>
8#include <string.h>
9
10static char *dup(const char *s, size_t n) {
11    char *d = malloc(n + 1);
12    if (!d) abort();
13    memcpy(d, s, n);
14    d[n] = 0;
15    return d;
16}
17
18const char *mt_vrec_get(const mt_vrec *r, const char *k) {
19    for (size_t i = 0; i < r->nkv; i++)
20        if (strcmp(r->kv[i].key, k) == 0) return strcmp(r->kv[i].value, "-") == 0 ? NULL : r->kv[i].value;
21    return NULL;
22}
23
24const char *mt_vrec_s(const mt_vrec *r, const char *k) {
25    const char *v = mt_vrec_get(r, k);
26    return v ? v : "-";
27}
28
29bool mt_parse_u64(const char *s, uint64_t *out) {
30    const char *p = s;
31    if (*p == '+') p++;
32    if (!*p) return false;
33    uint64_t v = 0;
34    for (; *p; p++) {
35        if (*p < '0' || *p > '9') return false;
36        uint64_t d = (uint64_t)(*p - '0');
37        if (v > (UINT64_MAX - d) / 10) return false;
38        v = v * 10 + d;
39    }
40    *out = v;
41    return true;
42}
43
44static bool ieq(const char *a, const char *b) {
45    for (; *a && *b; a++, b++)
46        if (tolower((unsigned char)*a) != tolower((unsigned char)*b)) return false;
47    return !*a && !*b;
48}
49
50bool mt_parse_f64(const char *s, double *out) {
51    const char *p = s;
52    bool neg = false;
53    if (*p == '+' || *p == '-') neg = *p++ == '-';
54    if (ieq(p, "inf") || ieq(p, "infinity")) {
55        *out = neg ? -HUGE_VAL : HUGE_VAL;
56        return true;
57    }
58    if (ieq(p, "nan")) {
59        *out = (double)NAN;
60        return true;
61    }
62    /* digits [. digits] | . digits, then an optional exponent: Rust's grammar (no hex). */
63    const char *q = p;
64    size_t ip = 0, fp = 0;
65    while (*q >= '0' && *q <= '9') q++, ip++;
66    if (*q == '.') {
67        q++;
68        while (*q >= '0' && *q <= '9') q++, fp++;
69    }
70    if (ip + fp == 0) return false;
71    if (*q == 'e' || *q == 'E') {
72        q++;
73        if (*q == '+' || *q == '-') q++;
74        if (!(*q >= '0' && *q <= '9')) return false;
75        while (*q >= '0' && *q <= '9') q++;
76    }
77    if (*q) return false;
78    errno = 0;
79    *out = strtod(s, NULL);
80    return true;
81}
82
83bool mt_vrec_u(const mt_vrec *r, const char *k, uint64_t *out) {
84    const char *v = mt_vrec_get(r, k);
85    return v && mt_parse_u64(v, out);
86}
87
88bool mt_vrec_f(const mt_vrec *r, const char *k, double *out) {
89    const char *v = mt_vrec_get(r, k);
90    return v && mt_parse_f64(v, out);
91}
92
93bool mt_vrec_flag(const mt_vrec *r, const char *k) {
94    const char *v = mt_vrec_get(r, k);
95    return v && strcmp(v, "1") == 0;
96}
97
98size_t mt_vrec_list(const mt_vrec *r, const char *k, uint32_t *out, size_t cap) {
99    const char *v = mt_vrec_get(r, k);
100    size_t n = 0;
101    if (!v) return 0;
102    while (*v && n < cap) {
103        const char *c = strchr(v, ',');
104        size_t len = c ? (size_t)(c - v) : strlen(v);
105        char tmp[32];
106        if (len < sizeof tmp) {
107            memcpy(tmp, v, len);
108            tmp[len] = 0;
109            uint64_t x;
110            if (mt_parse_u64(tmp, &x) && x <= UINT32_MAX) out[n++] = (uint32_t)x;
111        }
112        v += len + (c ? 1 : 0);
113        if (!c) break;
114    }
115    return n;
116}
117
118static bool is_ws(unsigned char c) { return c == ' ' || (c >= '\t' && c <= '\r'); }
119
120size_t mt_views_parse(const char *t, mt_vrec **out) {
121    size_t n = 0, cap = 0;
122    mt_vrec *recs = NULL;
123    size_t len = strlen(t);
124    for (size_t start = 0; start < len;) {
125        const char *nl = memchr(t + start, '\n', len - start);
126        size_t end = nl ? (size_t)(nl - t) : len, e = end;
127        if (nl && e > start && t[e - 1] == '\r') e--;
128        const char *line = t + start;
129        size_t ll = e - start;
130        start = nl ? end + 1 : len;
131        bool blank = true;
132        for (size_t i = 0; i < ll; i++)
133            if (!is_ws((unsigned char)line[i])) blank = false;
134        if (blank) continue;
135        size_t hl = ll;
136        const char *text = "";
137        size_t tl = 0;
138        for (size_t i = 0; i + 4 <= ll; i++)
139            if (memcmp(line + i, " -- ", 4) == 0) {
140                hl = i;
141                text = line + i + 4;
142                tl = ll - i - 4;
143                break;
144            }
145        if (n == cap) {
146            cap = cap ? cap * 2 : 16;
147            recs = realloc(recs, cap * sizeof *recs);
148            if (!recs) abort();
149        }
150        mt_vrec r = {NULL, NULL, 0, dup(text, tl)};
151        size_t kvcap = 0;
152        for (size_t i = 0; i < hl;) {
153            while (i < hl && is_ws((unsigned char)line[i])) i++;
154            if (i >= hl) break;
155            size_t w = i;
156            while (i < hl && !is_ws((unsigned char)line[i])) i++;
157            if (!r.kind) {
158                r.kind = dup(line + w, i - w);
159                continue;
160            }
161            const char *eq = memchr(line + w, '=', i - w);
162            if (r.nkv == kvcap) {
163                kvcap = kvcap ? kvcap * 2 : 8;
164                r.kv = realloc(r.kv, kvcap * sizeof *r.kv);
165                if (!r.kv) abort();
166            }
167            if (eq) r.kv[r.nkv++] = (mt_kv){dup(line + w, (size_t)(eq - (line + w))), dup(eq + 1, i - (size_t)(eq - line) - 1)};
168            else r.kv[r.nkv++] = (mt_kv){dup(line + w, i - w), dup("", 0)};
169        }
170        if (!r.kind) r.kind = dup("", 0);
171        recs[n++] = r;
172    }
173    *out = recs;
174    return n;
175}
176
177void mt_vrecs_free(mt_vrec *r, size_t n) {
178    for (size_t i = 0; i < n; i++) {
179        free(r[i].kind);
180        free(r[i].text);
181        for (size_t k = 0; k < r[i].nkv; k++) {
182            free(r[i].kv[k].key);
183            free(r[i].kv[k].value);
184        }
185        free(r[i].kv);
186    }
187    free(r);
188}
189
190mt_view mt_view_new(size_t machine, mt_instant at, const char *text) {
191    mt_view v = {machine, at, NULL, 0, NULL};
192    if (strncmp(text, "err ", 4) == 0) {
193        const char *s = text + 4;
194        size_t l = strlen(s);
195        while (l && is_ws((unsigned char)s[l - 1])) l--;
196        while (l && is_ws((unsigned char)*s)) s++, l--;
197        v.err = dup(s, l);
198        return v;
199    }
200    v.n = mt_views_parse(text, &v.recs);
201    return v;
202}
203
204void mt_view_free(mt_view *v) {
205    mt_vrecs_free(v->recs, v->n);
206    free(v->err);
207    v->recs = NULL;
208    v->n = 0;
209    v->err = NULL;
210}
211
212const mt_vrec *mt_view_first(const mt_view *v, const char *kind) { return mt_view_nth(v, kind, 0); }
213
214const mt_vrec *mt_view_nth(const mt_view *v, const char *kind, size_t i) {
215    for (size_t k = 0; k < v->n; k++)
216        if (strcmp(v->recs[k].kind, kind) == 0 && i-- == 0) return &v->recs[k];
217    return NULL;
218}
219
220size_t mt_view_count(const mt_view *v, const char *kind) {
221    size_t c = 0;
222    for (size_t k = 0; k < v->n; k++) c += strcmp(v->recs[k].kind, kind) == 0;
223    return c;
224}