dte test coverage


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 50.0% high: ≥ 85.0%
Coverage Exec / Excl / Total
Lines: 100.0% 129 / 0 / 129
Functions: 100.0% 15 / 0 / 15
Branches: 91.8% 89 / 0 / 97

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