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