skip to content
[readonly]
==> 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}