==> src/rsfmt.c <==1/* Rust's float and padding formats on top of snprintf. */
2#include "rsfmt.h"
3
4#include <math.h>
5#include <stdio.h>
6#include <string.h>
7
8static int put(char *out, size_t cap, const char *s) {
9 size_t n = strlen(s);
10 if (cap) {
11 size_t k = n < cap - 1 ? n : cap - 1;
12 memcpy(out, s, k);
13 out[k] = 0;
14 }
15 return (int)n;
16}
17
18/* NaN and the infinities as Rust writes them; 0 when v is finite. */
19static const char *special(double v, int flags) {
20 if (isnan(v)) return "NaN";
21 if (isinf(v)) return v < 0 ? "-inf" : (flags & RSFMT_PLUS) ? "+inf" : "inf";
22 return NULL;
23}
24
25int rsfmt_fixed(char *out, size_t cap, double v, int prec, int flags) {
26 const char *sp = special(v, flags);
27 if (sp) return put(out, cap, sp);
28 return snprintf(out, cap, (flags & RSFMT_PLUS) ? "%+.*f" : "%.*f", prec, v);
29}
30
31int rsfmt_exp(char *out, size_t cap, double v, int prec, int flags) {
32 const char *sp = special(v, flags);
33 if (sp) return put(out, cap, sp);
34 char tmp[64];
35 snprintf(tmp, sizeof tmp, (flags & RSFMT_PLUS) ? "%+.*e" : "%.*e", prec, v);
36 /* "2.500e-01" -> "2.500e-1", "1.0e+21" -> "1.0e21". */
37 char *e = strchr(tmp, 'e');
38 char res[80];
39 size_t m = (size_t)(e - tmp);
40 memcpy(res, tmp, m);
41 res[m++] = 'e';
42 const char *x = e + 1;
43 if (*x == '-') res[m++] = *x++;
44 else if (*x == '+') x++;
45 while (*x == '0' && x[1]) x++;
46 while (*x) res[m++] = *x++;
47 res[m] = 0;
48 return put(out, cap, res);
49}
50
51size_t rsfmt_chars(const char *s) {
52 size_t n = 0;
53 for (; *s; s++) n += ((unsigned char)*s & 0xc0) != 0x80;
54 return n;
55}
56
57size_t rsfmt_prefix(const char *s, size_t n) {
58 size_t i = 0;
59 while (s[i]) {
60 if (((unsigned char)s[i] & 0xc0) != 0x80) {
61 if (n == 0) break;
62 n--;
63 }
64 i++;
65 }
66 return i;
67}
68
69int rsfmt_pad(char *out, size_t cap, const char *s, int width, char align, char fill) {
70 size_t len = strlen(s), chars = rsfmt_chars(s);
71 size_t pad = width > 0 && (size_t)width > chars ? (size_t)width - chars : 0;
72 size_t pre = align == '>' ? pad : align == '^' ? pad / 2 : 0;
73 size_t post = pad - pre;
74 size_t total = pre + len + post, k = 0;
75 if (cap) {
76 for (size_t i = 0; i < pre && k + 1 < cap; i++) out[k++] = fill;
77 for (size_t i = 0; i < len && k + 1 < cap; i++) out[k++] = s[i];
78 for (size_t i = 0; i < post && k + 1 < cap; i++) out[k++] = fill;
79 out[k] = 0;
80 }
81 return (int)total;
82}
83
84int rsfmt_fixed_w(char *out, size_t cap, double v, int prec, int width, char align, int flags) {
85 char num[400];
86 rsfmt_fixed(num, sizeof num, v, prec, flags);
87 if (!(flags & RSFMT_ZERO) || isnan(v) || isinf(v)) return rsfmt_pad(out, cap, num, width, align, ' ');
88 /* Zero padding goes after the sign, whatever the alignment. */
89 const char *digits = num;
90 char sign[2] = {0, 0};
91 if (*digits == '-' || *digits == '+') sign[0] = *digits++;
92 size_t len = strlen(num);
93 char res[480];
94 size_t k = 0, zeros = width > 0 && (size_t)width > len ? (size_t)width - len : 0;
95 if (sign[0]) res[k++] = sign[0];
96 for (size_t i = 0; i < zeros && k < sizeof res - 1; i++) res[k++] = '0';
97 for (const char *p = digits; *p && k < sizeof res - 1; p++) res[k++] = *p;
98 res[k] = 0;
99 return put(out, cap, res);
100}