dte test coverage


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