Coverage Report

Created: 2026-09-01 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/jansson/src/strconv.c
Line
Count
Source
1
#include "jansson_private.h"
2
#include "strbuffer.h"
3
#include <assert.h>
4
#include <errno.h>
5
#include <math.h>
6
#include <stdio.h>
7
#include <string.h>
8
9
/* need jansson_private_config.h to get the correct snprintf */
10
#ifdef HAVE_CONFIG_H
11
#include <jansson_private_config.h>
12
#endif
13
14
/*
15
  - This code assumes that the decimal separator is exactly one
16
    character.
17
18
  - If setlocale() is called by another thread between the call to
19
    get_decimal_point() and the call to sprintf() or strtod(), the
20
    result may be wrong. setlocale() is not thread-safe and should
21
    not be used this way. Multi-threaded programs should use
22
    uselocale() instead.
23
*/
24
0
static char get_decimal_point() {
25
0
    char buf[3];
26
0
    sprintf(buf, "%#.0f", 1.0); // "1." in the current locale
27
0
    return buf[1];
28
0
}
29
30
0
static void to_locale(strbuffer_t *strbuffer) {
31
0
    char point;
32
0
    char *pos;
33
34
0
    point = get_decimal_point();
35
0
    if (point == '.') {
36
        /* No conversion needed */
37
0
        return;
38
0
    }
39
40
0
    pos = strchr(strbuffer->value, '.');
41
0
    if (pos)
42
0
        *pos = point;
43
0
}
44
45
0
int jsonp_strtod(strbuffer_t *strbuffer, double *out) {
46
0
    double value;
47
0
    char *end;
48
49
0
    to_locale(strbuffer);
50
51
0
    errno = 0;
52
0
    value = strtod(strbuffer->value, &end);
53
0
    assert(end == strbuffer->value + strbuffer->length);
54
55
0
    if ((value == HUGE_VAL || value == -HUGE_VAL) && errno == ERANGE) {
56
        /* Overflow */
57
0
        return -1;
58
0
    }
59
60
0
    *out = value;
61
0
    return 0;
62
0
}
63
64
#if DTOA_ENABLED
65
/* see dtoa.c */
66
char *dtoa_r(double dd, int mode, int ndigits, int *decpt, int *sign, char **rve,
67
             char *buf, size_t blen);
68
69
0
int jsonp_dtostr(char *buffer, size_t size, double value, int precision) {
70
    /* adapted from `format_float_short()` in
71
     * https://github.com/python/cpython/blob/2cf18a44303b6d84faa8ecffaecc427b53ae121e/Python/pystrtod.c#L969
72
     */
73
0
    char digits[25];
74
0
    char *digits_end;
75
0
    int mode = precision == 0 ? 0 : 2;
76
0
    int decpt, sign, exp_len, exp = 0, use_exp = 0;
77
0
    int digits_len, vdigits_start, vdigits_end;
78
0
    char *p;
79
80
0
    if (dtoa_r(value, mode, precision, &decpt, &sign, &digits_end, digits, 25) == NULL) {
81
        // digits is too short => should not happen
82
0
        return -1;
83
0
    }
84
85
0
    digits_len = digits_end - digits;
86
0
    if (decpt <= -4 || decpt > 16) {
87
0
        use_exp = 1;
88
0
        exp = decpt - 1;
89
0
        decpt = 1;
90
0
    }
91
92
0
    vdigits_start = decpt <= 0 ? decpt - 1 : 0;
93
0
    vdigits_end = digits_len;
94
0
    if (!use_exp) {
95
        /* decpt + 1 to add ".0" if value is an integer */
96
0
        vdigits_end = vdigits_end > decpt ? vdigits_end : decpt + 1;
97
0
    } else {
98
0
        vdigits_end = vdigits_end > decpt ? vdigits_end : decpt;
99
0
    }
100
101
0
    if (
102
        /* sign, decimal point and trailing 0 byte */
103
0
        (size_t)(3 +
104
105
                 /* total digit count (including zero padding on both sides) */
106
0
                 (vdigits_end - vdigits_start) +
107
108
                 /* exponent "e+100", max 3 numerical digits */
109
0
                 (use_exp ? 5 : 0)) > size) {
110
        /* buffer is too short */
111
0
        return -1;
112
0
    }
113
114
0
    p = buffer;
115
0
    if (sign == 1) {
116
0
        *p++ = '-';
117
0
    }
118
119
    /* note that exactly one of the three 'if' conditions is true,
120
      so we include exactly one decimal point */
121
    /* Zero padding on left of digit string */
122
0
    if (decpt <= 0) {
123
0
        memset(p, '0', decpt - vdigits_start);
124
0
        p += decpt - vdigits_start;
125
0
        *p++ = '.';
126
0
        memset(p, '0', 0 - decpt);
127
0
        p += 0 - decpt;
128
0
    } else {
129
0
        memset(p, '0', 0 - vdigits_start);
130
0
        p += 0 - vdigits_start;
131
0
    }
132
133
    /* Digits, with included decimal point */
134
0
    if (0 < decpt && decpt <= digits_len) {
135
0
        strncpy(p, digits, decpt - 0);
136
0
        p += decpt - 0;
137
0
        *p++ = '.';
138
0
        strncpy(p, digits + decpt, digits_len - decpt);
139
0
        p += digits_len - decpt;
140
0
    } else {
141
0
        strncpy(p, digits, digits_len);
142
0
        p += digits_len;
143
0
    }
144
145
    /* And zeros on the right */
146
0
    if (digits_len < decpt) {
147
0
        memset(p, '0', decpt - digits_len);
148
0
        p += decpt - digits_len;
149
0
        *p++ = '.';
150
0
        memset(p, '0', vdigits_end - decpt);
151
0
        p += vdigits_end - decpt;
152
0
    } else {
153
0
        memset(p, '0', vdigits_end - digits_len);
154
0
        p += vdigits_end - digits_len;
155
0
    }
156
157
0
    if (p[-1] == '.')
158
0
        p--;
159
160
0
    if (use_exp) {
161
0
        *p++ = 'e';
162
0
        exp_len = sprintf(p, "%d", exp);
163
0
        p += exp_len;
164
0
    }
165
0
    *p = '\0';
166
167
0
    return (int)(p - buffer);
168
0
}
169
#else /* DTOA_ENABLED == 0 */
170
static void from_locale(char *buffer) {
171
    char point;
172
    char *pos;
173
174
    point = get_decimal_point();
175
    if (point == '.') {
176
        /* No conversion needed */
177
        return;
178
    }
179
180
    pos = strchr(buffer, point);
181
    if (pos)
182
        *pos = '.';
183
}
184
185
int jsonp_dtostr(char *buffer, size_t size, double value, int precision) {
186
    int ret;
187
    char *start, *end;
188
    size_t length;
189
190
    if (precision == 0)
191
        precision = 17;
192
193
    ret = snprintf(buffer, size, "%.*g", precision, value);
194
    if (ret < 0)
195
        return -1;
196
197
    length = (size_t)ret;
198
    if (length >= size)
199
        return -1;
200
201
    from_locale(buffer);
202
203
    /* Make sure there's a dot or 'e' in the output. Otherwise
204
       a real is converted to an integer when decoding */
205
    if (strchr(buffer, '.') == NULL && strchr(buffer, 'e') == NULL) {
206
        if (length + 3 >= size) {
207
            /* No space to append ".0" */
208
            return -1;
209
        }
210
        buffer[length] = '.';
211
        buffer[length + 1] = '0';
212
        buffer[length + 2] = '\0';
213
        length += 2;
214
    }
215
216
    /* Remove leading '+' from positive exponent. Also remove leading
217
       zeros from exponents (added by some printf() implementations) */
218
    start = strchr(buffer, 'e');
219
    if (start) {
220
        start++;
221
        end = start + 1;
222
223
        if (*start == '-')
224
            start++;
225
226
        while (*end == '0')
227
            end++;
228
229
        if (end != start) {
230
            memmove(start, end, length - (size_t)(end - buffer));
231
            length -= (size_t)(end - start);
232
        }
233
    }
234
235
    return (int)length;
236
}
237
#endif