dte test coverage


Directory: ./
File: src/util/strtonum.c
Date: 2025-06-04 06:50:24
Exec Total Coverage
Lines: 123 123 100.0%
Functions: 14 14 100.0%
Branches: 77 85 90.6%

Line Branch Exec Source
1 #include <errno.h>
2 #include <string.h>
3 #include "strtonum.h"
4 #include "arith.h"
5 #include "ascii.h"
6 #include "debug.h"
7 #include "xstring.h"
8
9 enum {
10 A = 0xA, B = 0xB, C = 0xC,
11 D = 0xD, E = 0xE, F = 0xF,
12 I = HEX_INVALID
13 };
14
15 // Indices are offset by 48 ('0'; 0x30)
16 const uint8_t hex_decode_table[64] = {
17 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, I, I, I, I, I, I, // 0x30 0123456789......
18 I, A, B, C, D, E, F, I, I, I, I, I, I, I, I, I, // 0x40 .ABCDEF.........
19 I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, // 0x50 ................
20 I, A, B, C, D, E, F, I, I, I, I, I, I, I, I, I, // 0x60 .abcdef.........
21 };
22
23 UNITTEST {
24 static_assert((I & 0xF) == 0);
25 BUG_ON(hex_decode_table[sizeof(hex_decode_table) - 1] != I);
26 }
27
28 601 size_t buf_parse_uintmax(const char *str, size_t len, uintmax_t *valp)
29 {
30
4/4
✓ Branch 0 (2→3) taken 587 times.
✓ Branch 1 (2→12) taken 14 times.
✓ Branch 2 (3→4) taken 324 times.
✓ Branch 3 (3→12) taken 263 times.
601 if (unlikely(len == 0 || !ascii_isdigit(str[0]))) {
31 return 0;
32 }
33
34 324 uintmax_t val = str[0] - '0';
35 324 size_t i = 1;
36
37
4/4
✓ Branch 0 (9→10) taken 555 times.
✓ Branch 1 (9→11) taken 233 times.
✓ Branch 2 (10→5) taken 470 times.
✓ Branch 3 (10→11) taken 85 times.
788 while (i < len && ascii_isdigit(str[i])) {
38
2/2
✓ Branch 0 (6→7) taken 464 times.
✓ Branch 1 (6→12) taken 6 times.
470 if (unlikely(umax_multiply_overflows(val, 10, &val))) {
39 return 0;
40 }
41
1/2
✓ Branch 0 (8→9) taken 464 times.
✗ Branch 1 (8→12) not taken.
464 if (unlikely(umax_add_overflows(val, str[i++] - '0', &val))) {
42 return 0;
43 }
44 }
45
46 318 *valp = val;
47 318 return i;
48 }
49
50 31 size_t buf_parse_ulong(const char *str, size_t len, unsigned long *valp)
51 {
52 31 uintmax_t val;
53 31 size_t n = buf_parse_uintmax(str, len, &val);
54
2/2
✓ Branch 0 (3→4) taken 27 times.
✓ Branch 1 (3→5) taken 4 times.
31 if (n == 0 || val > ULONG_MAX) {
55 return 0;
56 }
57 27 *valp = (unsigned long)val;
58 27 return n;
59 }
60
61 2 size_t buf_parse_uint(const char *str, size_t len, unsigned int *valp)
62 {
63 2 uintmax_t val;
64 2 size_t n = buf_parse_uintmax(str, len, &val);
65
2/4
✓ Branch 0 (3→4) taken 2 times.
✗ Branch 1 (3→6) not taken.
✓ Branch 2 (4→5) taken 2 times.
✗ Branch 3 (4→6) not taken.
2 if (n == 0 || val > UINT_MAX) {
66 return 0;
67 }
68 2 *valp = (unsigned int)val;
69 2 return n;
70 }
71
72 25 size_t buf_parse_size(const char *str, size_t len, size_t *valp)
73 {
74 25 uintmax_t val;
75 25 size_t n = buf_parse_uintmax(str, len, &val);
76
2/2
✓ Branch 0 (3→4) taken 20 times.
✓ Branch 1 (3→5) taken 5 times.
25 if (n == 0 || val > SIZE_MAX) {
77 return 0;
78 }
79 20 *valp = (size_t)val;
80 20 return n;
81 }
82
83 57 static size_t buf_parse_long(const char *str, size_t len, long *valp)
84 {
85
1/2
✓ Branch 0 (2→3) taken 57 times.
✗ Branch 1 (2→13) not taken.
57 if (unlikely(len == 0)) {
86 return 0;
87 }
88
89 57 bool negative = false;
90 57 size_t skipped = 0;
91
3/3
✓ Branch 0 (3→4) taken 8 times.
✓ Branch 1 (3→5) taken 9 times.
✓ Branch 2 (3→6) taken 40 times.
57 switch (str[0]) {
92 8 case '-':
93 8 negative = true;
94 // Fallthrough
95 17 case '+':
96 17 skipped = 1;
97 17 str++;
98 17 len--;
99 17 break;
100 }
101
102 57 uintmax_t val;
103 57 size_t n = buf_parse_uintmax(str, len, &val);
104
3/4
✓ Branch 0 (7→8) taken 52 times.
✓ Branch 1 (7→13) taken 5 times.
✓ Branch 2 (8→9) taken 52 times.
✗ Branch 3 (8→13) not taken.
57 if (n == 0 || val > LONG_MAX) {
105 return 0;
106 }
107
108
2/2
✓ Branch 0 (9→10) taken 8 times.
✓ Branch 1 (9→11) taken 44 times.
52 if (negative) {
109 8 *valp = -((long)val);
110 } else {
111 44 *valp = (long)val;
112 }
113
114 52 return n + skipped;
115 }
116
117 58 bool str_to_int(const char *str, int *valp)
118 {
119 58 const size_t len = strlen(str);
120
2/2
✓ Branch 0 (2→3) taken 57 times.
✓ Branch 1 (2→8) taken 1 times.
58 if (unlikely(len == 0)) {
121 return false;
122 }
123 57 long val;
124 57 const size_t n = buf_parse_long(str, len, &val);
125
4/6
✓ Branch 0 (4→5) taken 51 times.
✓ Branch 1 (4→8) taken 6 times.
✓ Branch 2 (5→6) taken 51 times.
✗ Branch 3 (5→8) not taken.
✓ Branch 4 (6→7) taken 51 times.
✗ Branch 5 (6→8) not taken.
57 if (n != len || val < INT_MIN || val > INT_MAX) {
126 return false;
127 }
128 51 *valp = (int)val;
129 51 return true;
130 }
131
132 151 static bool str_to_uintmax(const char *str, uintmax_t *valp)
133 {
134 151 const size_t len = strlen(str);
135
2/2
✓ Branch 0 (2→3) taken 149 times.
✓ Branch 1 (2→6) taken 2 times.
151 if (unlikely(len == 0)) {
136 return false;
137 }
138 149 uintmax_t val;
139 149 const size_t n = buf_parse_uintmax(str, len, &val);
140
2/2
✓ Branch 0 (4→5) taken 131 times.
✓ Branch 1 (4→6) taken 18 times.
149 if (n != len) {
141 return false;
142 }
143 131 *valp = val;
144 131 return true;
145 }
146
147 64 bool str_to_uint(const char *str, unsigned int *valp)
148 {
149 64 uintmax_t x;
150
3/4
✓ Branch 0 (3→4) taken 55 times.
✓ Branch 1 (3→6) taken 9 times.
✓ Branch 2 (4→5) taken 55 times.
✗ Branch 3 (4→6) not taken.
64 if (!str_to_uintmax(str, &x) || x > UINT_MAX) {
151 return false;
152 }
153 55 *valp = (unsigned int)x;
154 55 return true;
155 }
156
157 6 bool str_to_ulong(const char *str, unsigned long *valp)
158 {
159 6 uintmax_t x;
160
2/2
✓ Branch 0 (3→4) taken 5 times.
✓ Branch 1 (3→5) taken 1 times.
6 if (!str_to_uintmax(str, &x) || x > ULONG_MAX) {
161 return false;
162 }
163 5 *valp = (unsigned long)x;
164 5 return true;
165 }
166
167 81 bool str_to_size(const char *str, size_t *valp)
168 {
169 81 uintmax_t x;
170
2/2
✓ Branch 0 (3→4) taken 71 times.
✓ Branch 1 (3→5) taken 10 times.
81 if (!str_to_uintmax(str, &x) || x > SIZE_MAX) {
171 return false;
172 }
173 71 *valp = (size_t)x;
174 71 return true;
175 }
176
177 // Parse line and column number from line[,col] or line[:col]
178 23 bool str_to_xfilepos(const char *str, size_t *linep, size_t *colp)
179 {
180 23 size_t len = strlen(str);
181 23 size_t line, col;
182 23 size_t i = buf_parse_size(str, len, &line);
183
4/4
✓ Branch 0 (3→4) taken 19 times.
✓ Branch 1 (3→14) taken 4 times.
✓ Branch 2 (4→5) taken 16 times.
✓ Branch 3 (4→14) taken 3 times.
23 if (i == 0 || line < 1) {
184 return false;
185 }
186
187 // If an explicit column wasn't specified in `str`, set *colp to 0
188 // (which is NOT a valid column number). Callers should be prepared
189 // to check this and substitute it for something more appropriate,
190 // or otherwise use str_to_filepos() instead.
191
2/2
✓ Branch 0 (5→6) taken 3 times.
✓ Branch 1 (5→7) taken 13 times.
16 if (i == len) {
192 3 col = 0;
193 3 goto out;
194 }
195
196 13 char c = str[i];
197
6/6
✓ Branch 0 (7→8) taken 10 times.
✓ Branch 1 (7→11) taken 3 times.
✓ Branch 2 (9→10) taken 5 times.
✓ Branch 3 (9→11) taken 5 times.
✓ Branch 4 (10→11) taken 1 times.
✓ Branch 5 (10→12) taken 4 times.
13 if ((c != ':' && c != ',') || !str_to_size(str + i + 1, &col) || col < 1) {
198 9 return false;
199 }
200
201 4 out:
202 7 *linep = line;
203 7 *colp = col;
204 7 return true;
205 }
206
207 // This is much like str_to_xfilepos(), except *colp is set to 1 if no
208 // explicit column number is specified in `str`. This is a convenience
209 // to callers that want a valid column number (when omitted) and don't
210 // need to do anything different for e.g. "32" vs. "32:1".
211 17 bool str_to_filepos(const char *str, size_t *linep, size_t *colp)
212 {
213 17 size_t col;
214 17 bool r = str_to_xfilepos(str, linep, &col);
215
2/2
✓ Branch 0 (3→4) taken 4 times.
✓ Branch 1 (3→5) taken 13 times.
17 if (r) {
216 4 *colp = col + !col;
217 }
218 17 return r;
219 }
220
221 // Return the number of decimal digits in `x`
222 13 size_t size_str_width(size_t x)
223 {
224 13 size_t width = 0;
225 29 do {
226 29 x /= 10;
227 29 width++;
228
2/2
✓ Branch 0 (3→4) taken 16 times.
✓ Branch 1 (3→5) taken 13 times.
29 } while (x);
229 13 return width;
230 }
231
232 // Convert a string of decimal digits with an optional KiB/MiB/GiB/etc.
233 // suffix to an intmax_t value representing the number of bytes, or a
234 // negated <errno.h> value in the case of errors
235 35 intmax_t parse_filesize(const char *str)
236 {
237 35 uintmax_t x;
238 35 size_t ndigits = buf_parse_uintmax(str, strlen(str), &x);
239
4/4
✓ Branch 0 (3→4) taken 33 times.
✓ Branch 1 (3→5) taken 2 times.
✓ Branch 2 (4→5) taken 1 times.
✓ Branch 3 (4→8) taken 32 times.
35 if (unlikely(ndigits == 0 || x > INTMAX_MAX)) {
240
2/2
✓ Branch 0 (5→6) taken 2 times.
✓ Branch 1 (5→7) taken 1 times.
5 return ascii_isdigit(str[0]) ? -EOVERFLOW : -EINVAL;
241 }
242
243 32 const char *suffix = str + ndigits;
244 32 unsigned int shift;
245
8/8
✓ Branch 0 (8→9) taken 8 times.
✓ Branch 1 (8→10) taken 9 times.
✓ Branch 2 (8→11) taken 1 times.
✓ Branch 3 (8→12) taken 1 times.
✓ Branch 4 (8→13) taken 2 times.
✓ Branch 5 (8→14) taken 4 times.
✓ Branch 6 (8→15) taken 5 times.
✓ Branch 7 (8→18) taken 2 times.
32 switch (suffix[0]) {
246 case 'K': shift = 10; break;
247 8 case 'M': shift = 20; break;
248 9 case 'G': shift = 30; break;
249 1 case 'T': shift = 40; break;
250 1 case 'P': shift = 50; break;
251 2 case 'E': shift = 60; break;
252 4 case '\0': return x;
253 default: return -EINVAL;
254 }
255
256
2/2
✓ Branch 0 (15→16) taken 13 times.
✓ Branch 1 (15→18) taken 13 times.
26 if (unlikely(!streq(suffix + 1, "iB"))) {
257 return -EINVAL;
258 }
259
260 13 uintmax_t val = x << shift;
261
2/2
✓ Branch 0 (16→17) taken 11 times.
✓ Branch 1 (16→18) taken 2 times.
13 if (unlikely(val >> shift != x || x > INTMAX_MAX)) {
262 return -EOVERFLOW;
263 }
264
265 11 return val;
266 }
267