skip to content
==> src/rsfmt.c <==
1/* Rust's Debug and Display formats, by Rust's own rules (core::fmt, core::time). */
2#include "mesh/rsfmt.h"
3
4#include <math.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8
9void mesh_rs_debug_str(mesh_buf *b, const char *s, size_t n) {
10    mesh_buf_putc(b, '"');
11    for (size_t i = 0; i < n; i++) {
12        unsigned char c = (unsigned char)s[i];
13        switch (c) {
14        case '"': mesh_buf_puts(b, "\\\""); break;
15        case '\\': mesh_buf_puts(b, "\\\\"); break;
16        case '\n': mesh_buf_puts(b, "\\n"); break;
17        case '\r': mesh_buf_puts(b, "\\r"); break;
18        case '\t': mesh_buf_puts(b, "\\t"); break;
19        case 0: mesh_buf_puts(b, "\\0"); break;
20        default:
21            if (c < 0x20 || c == 0x7f) mesh_buf_printf(b, "\\u{%x}", c);
22            else mesh_buf_putc(b, (char)c);
23        }
24    }
25    mesh_buf_putc(b, '"');
26}
27
28void mesh_rs_debug_cstr(mesh_buf *b, const char *s) { mesh_rs_debug_str(b, s, strlen(s)); }
29
30/* core::time's fmt_decimal without a precision: the integer part, then the
31 * fraction's significant digits (trailing zeros never printed). */
32static void decimal(mesh_buf *b, uint64_t integer, uint32_t frac, uint32_t divisor, const char *unit) {
33    mesh_buf_printf(b, "%llu", (unsigned long long)integer);
34    if (frac > 0) {
35        mesh_buf_putc(b, '.');
36        while (frac > 0 && divisor > 0) {
37            mesh_buf_putc(b, (char)('0' + frac / divisor));
38            frac %= divisor;
39            divisor /= 10;
40        }
41    }
42    mesh_buf_puts(b, unit);
43}
44
45void mesh_rs_duration(mesh_buf *b, uint64_t secs, uint32_t nanos) {
46    if (secs > 0) decimal(b, secs, nanos, 100000000u, "s");
47    else if (nanos >= 1000000u) decimal(b, nanos / 1000000u, nanos % 1000000u, 100000u, "ms");
48    else if (nanos >= 1000u) decimal(b, nanos / 1000u, nanos % 1000u, 100u, "\xc2\xb5s");
49    else decimal(b, nanos, 0, 1, "ns");
50}
51
52/* digits (no point) and the decimal exponent of the first digit: value = 0.d1d2.. x 10^(exp+1). */
53static void plain(mesh_buf *b, int neg, const char *digits, int exp) {
54    size_t n = strlen(digits);
55    if (neg) mesh_buf_putc(b, '-');
56    if (exp < 0) {
57        mesh_buf_puts(b, "0.");
58        for (int i = 0; i < -exp - 1; i++) mesh_buf_putc(b, '0');
59        mesh_buf_puts(b, digits);
60    } else if ((size_t)exp + 1 >= n) {
61        mesh_buf_puts(b, digits);
62        for (size_t i = n; i < (size_t)exp + 1; i++) mesh_buf_putc(b, '0');
63    } else {
64        mesh_buf_put(b, digits, (size_t)exp + 1);
65        mesh_buf_putc(b, '.');
66        mesh_buf_puts(b, digits + exp + 1);
67    }
68}
69
70/* The shortest %.{p}e that reads back to v, as digits and exponent. */
71static void shortest(mesh_buf *b, double v, int is_f32) {
72    if (isnan(v)) { mesh_buf_puts(b, "NaN"); return; }
73    if (isinf(v)) { mesh_buf_puts(b, v < 0 ? "-inf" : "inf"); return; }
74    int neg = signbit(v) != 0;
75    if (v == 0) { mesh_buf_puts(b, neg ? "-0" : "0"); return; }
76    double a = fabs(v);
77    char s[64];
78    for (int p = 0; p < 17; p++) {
79        snprintf(s, sizeof s, "%.*e", p, a);
80        int back = is_f32 ? (strtof(s, NULL) == (float)a) : (strtod(s, NULL) == a);
81        if (back || p == 16) break;
82    }
83    /* s is d[.ddd]e[+-]xx */
84    char digits[40];
85    size_t k = 0;
86    const char *e = strchr(s, 'e');
87    for (const char *c = s; c < e; c++) if (*c != '.') digits[k++] = *c;
88    digits[k] = 0;
89    while (k > 1 && digits[k - 1] == '0') digits[--k] = 0;
90    plain(b, neg, digits, atoi(e + 1));
91}
92
93void mesh_rs_f32(mesh_buf *b, float v) { shortest(b, v, 1); }
94void mesh_rs_f64(mesh_buf *b, double v) { shortest(b, v, 0); }
95
96void mesh_rs_fixed(mesh_buf *b, double v, int p) {
97    if (isnan(v)) { mesh_buf_puts(b, "NaN"); return; }
98    if (isinf(v)) { mesh_buf_puts(b, v < 0 ? "-inf" : "inf"); return; }
99    mesh_buf_printf(b, "%.*f", p, v);
100}
101
102/* The length of the valid UTF-8 sequence at s (n bytes left), or 0 when it is
103 * invalid; *bad then holds the length of the maximal invalid prefix to replace. */
104static size_t utf8_seq(const unsigned char *s, size_t n, size_t *bad) {
105    unsigned c = s[0];
106    size_t need;
107    unsigned lo = 0x80, hi = 0xbf;
108    if (c < 0x80) return 1;
109    if (c >= 0xc2 && c <= 0xdf) need = 1;
110    else if (c >= 0xe0 && c <= 0xef) {
111        need = 2;
112        if (c == 0xe0) lo = 0xa0;
113        if (c == 0xed) hi = 0x9f;
114    } else if (c >= 0xf0 && c <= 0xf4) {
115        need = 3;
116        if (c == 0xf0) lo = 0x90;
117        if (c == 0xf4) hi = 0x8f;
118    } else { *bad = 1; return 0; }
119    size_t i = 1;
120    for (; i <= need; i++) {
121        if (i >= n) { *bad = i; return 0; }
122        unsigned x = s[i];
123        unsigned l = i == 1 ? lo : 0x80, h = i == 1 ? hi : 0xbf;
124        if (x < l || x > h) { *bad = i; return 0; }
125    }
126    return need + 1;
127}
128
129void mesh_rs_lossy(mesh_buf *b, const void *bytes, size_t n) {
130    const unsigned char *s = bytes;
131    size_t i = 0;
132    while (i < n) {
133        size_t bad = 0, k = utf8_seq(s + i, n - i, &bad);
134        if (k) { mesh_buf_put(b, s + i, k); i += k; }
135        else { mesh_buf_puts(b, "\xef\xbf\xbd"); i += bad; }
136    }
137}
138
139int mesh_rs_utf8_valid(const void *bytes, size_t n) {
140    const unsigned char *s = bytes;
141    size_t i = 0;
142    while (i < n) {
143        size_t bad = 0, k = utf8_seq(s + i, n - i, &bad);
144        if (!k) return 0;
145        i += k;
146    }
147    return 1;
148}
149
150const char *mesh_rs_parse_error(int code) {
151    switch (code) {
152    case MESH_RS_EMPTY: return "cannot parse integer from empty string";
153    case MESH_RS_INVALID: return "invalid digit found in string";
154    case MESH_RS_OVERFLOW: return "number too large to fit in target type";
155    case MESH_RS_FLOAT_EMPTY: return "cannot parse float from empty string";
156    default: return "invalid float literal";
157    }
158}
159
160int mesh_rs_parse_u64(const char *s, size_t n, uint64_t *out) {
161    if (n == 0) return MESH_RS_EMPTY;
162    size_t i = 0;
163    if (s[0] == '+' && n > 1) i = 1; /* a lone sign is an invalid digit */
164    uint64_t x = 0;
165    int big = 0;
166    for (; i < n; i++) {
167        if (s[i] < '0' || s[i] > '9') return MESH_RS_INVALID;
168        unsigned d = (unsigned)(s[i] - '0');
169        if (x > (UINT64_MAX - d) / 10) big = 1;
170        else x = x * 10 + d;
171    }
172    if (big) return MESH_RS_OVERFLOW;
173    *out = x;
174    return 0;
175}
176
177int mesh_rs_parse_u32(const char *s, size_t n, uint32_t *out) {
178    uint64_t x;
179    int r = mesh_rs_parse_u64(s, n, &x);
180    if (r == MESH_RS_OVERFLOW || (r == 0 && x > UINT32_MAX)) return MESH_RS_OVERFLOW;
181    if (r) return r;
182    *out = (uint32_t)x;
183    return 0;
184}
185
186int mesh_rs_parse_i64(const char *s, size_t n, int64_t *out) {
187    if (n == 0) return MESH_RS_EMPTY;
188    int neg = s[0] == '-';
189    size_t i = (s[0] == '+' || s[0] == '-') && n > 1;
190    if (i == 0 && (s[0] == '+' || s[0] == '-')) return MESH_RS_INVALID;
191    uint64_t x = 0;
192    int big = 0;
193    for (; i < n; i++) {
194        if (s[i] < '0' || s[i] > '9') return MESH_RS_INVALID;
195        unsigned d = (unsigned)(s[i] - '0');
196        if (x > (UINT64_MAX - d) / 10) big = 1;
197        else x = x * 10 + d;
198    }
199    if (big || x > (uint64_t)INT64_MAX + (uint64_t)neg) return MESH_RS_OVERFLOW;
200    *out = neg ? (int64_t)(0 - x) : (int64_t)x;
201    return 0;
202}
203
204static int ieq(const char *s, size_t n, const char *w) {
205    if (strlen(w) != n) return 0;
206    for (size_t i = 0; i < n; i++) {
207        char c = s[i];
208        if (c >= 'A' && c <= 'Z') c = (char)(c - 'A' + 'a');
209        if (c != w[i]) return 0;
210    }
211    return 1;
212}
213
214/* core::num::dec2flt's grammar; the value from strtod/strtof (both correctly rounded). */
215static int parse_float(const char *s, size_t n, double *d, float *f) {
216    if (n == 0) return MESH_RS_FLOAT_EMPTY;
217    size_t i = s[0] == '+' || s[0] == '-';
218    int neg = s[0] == '-';
219    const char *b = s + i;
220    size_t m = n - i;
221    if (ieq(b, m, "inf") || ieq(b, m, "infinity")) {
222        if (d) *d = neg ? -HUGE_VAL : HUGE_VAL;
223        if (f) *f = neg ? -HUGE_VALF : HUGE_VALF;
224        return 0;
225    }
226    if (ieq(b, m, "nan")) {
227        if (d) *d = NAN;
228        if (f) *f = NAN;
229        return 0;
230    }
231    size_t j = 0, intd = 0, frad = 0;
232    while (j < m && b[j] >= '0' && b[j] <= '9') { j++; intd++; }
233    if (j < m && b[j] == '.') {
234        j++;
235        while (j < m && b[j] >= '0' && b[j] <= '9') { j++; frad++; }
236    }
237    if (intd + frad == 0) return MESH_RS_FLOAT_INVALID;
238    if (j < m && (b[j] == 'e' || b[j] == 'E')) {
239        j++;
240        if (j < m && (b[j] == '+' || b[j] == '-')) j++;
241        size_t e = 0;
242        while (j < m && b[j] >= '0' && b[j] <= '9') { j++; e++; }
243        if (!e) return MESH_RS_FLOAT_INVALID;
244    }
245    if (j != m) return MESH_RS_FLOAT_INVALID;
246    char *t = mesh_xstrndup(s, n);
247    if (d) *d = strtod(t, NULL);
248    if (f) *f = strtof(t, NULL);
249    free(t);
250    return 0;
251}
252
253int mesh_rs_parse_f64(const char *s, size_t n, double *out) { return parse_float(s, n, out, NULL); }
254int mesh_rs_parse_f32(const char *s, size_t n, float *out) { return parse_float(s, n, NULL, out); }