dte test coverage


Directory: ./
File: src/util/strtonum.c
Date: 2025-02-14 16:55:22
Exec Total Coverage
Lines: 122 122 100.0%
Functions: 14 14 100.0%
Branches: 79 87 90.8%

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 583 size_t buf_parse_uintmax(const char *str, size_t len, uintmax_t *valp)
34 {
35
4/4
✓ Branch 0 (2→3) taken 569 times.
✓ Branch 1 (2→12) taken 14 times.
✓ Branch 2 (3→4) taken 306 times.
✓ Branch 3 (3→12) taken 263 times.
583 if (unlikely(len == 0 || !ascii_isdigit(str[0]))) {
36 return 0;
37 }
38
39 306 uintmax_t val = str[0] - '0';
40 306 size_t i = 1;
41
42
4/4
✓ Branch 0 (9→10) taken 538 times.
✓ Branch 1 (9→11) taken 225 times.
✓ Branch 2 (10→5) taken 463 times.
✓ Branch 3 (10→11) taken 75 times.
763 while (i < len && ascii_isdigit(str[i])) {
43
2/2
✓ Branch 0 (6→7) taken 457 times.
✓ Branch 1 (6→12) taken 6 times.
463 if (unlikely(umax_multiply_overflows(val, 10, &val))) {
44 return 0;
45 }
46
1/2
✓ Branch 0 (8→9) taken 457 times.
✗ Branch 1 (8→12) not taken.
457 if (unlikely(umax_add_overflows(val, str[i++] - '0', &val))) {
47 return 0;
48 }
49 }
50
51 300 *valp = val;
52 300 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 53 static size_t buf_parse_long(const char *str, size_t len, long *valp)
89 {
90
1/2
✓ Branch 0 (2→3) taken 53 times.
✗ Branch 1 (2→13) not taken.
53 if (unlikely(len == 0)) {
91 return 0;
92 }
93
94 53 bool negative = false;
95 53 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 36 times.
53 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 53 uintmax_t val;
108 53 size_t n = buf_parse_uintmax(str, len, &val);
109
3/4
✓ Branch 0 (7→8) taken 48 times.
✓ Branch 1 (7→13) taken 5 times.
✓ Branch 2 (8→9) taken 48 times.
✗ Branch 3 (8→13) not taken.
53 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 40 times.
48 if (negative) {
114 8 *valp = -((long)val);
115 } else {
116 40 *valp = (long)val;
117 }
118
119 48 return n + skipped;
120 }
121
122 54 bool str_to_int(const char *str, int *valp)
123 {
124 54 const size_t len = strlen(str);
125
2/2
✓ Branch 0 (2→3) taken 53 times.
✓ Branch 1 (2→8) taken 1 times.
54 if (unlikely(len == 0)) {
126 return false;
127 }
128 53 long val;
129 53 const size_t n = buf_parse_long(str, len, &val);
130
4/6
✓ Branch 0 (4→5) taken 47 times.
✓ Branch 1 (4→8) taken 6 times.
✓ Branch 2 (5→6) taken 47 times.
✗ Branch 3 (5→8) not taken.
✓ Branch 4 (6→7) taken 47 times.
✗ Branch 5 (6→8) not taken.
53 if (n != len || val < INT_MIN || val > INT_MAX) {
131 return false;
132 }
133 47 *valp = (int)val;
134 47 return true;
135 }
136
137 142 static bool str_to_uintmax(const char *str, uintmax_t *valp)
138 {
139 142 const size_t len = strlen(str);
140
2/2
✓ Branch 0 (2→3) taken 140 times.
✓ Branch 1 (2→6) taken 2 times.
142 if (unlikely(len == 0)) {
141 return false;
142 }
143 140 uintmax_t val;
144 140 const size_t n = buf_parse_uintmax(str, len, &val);
145
2/2
✓ Branch 0 (4→5) taken 127 times.
✓ Branch 1 (4→6) taken 13 times.
140 if (n != len) {
146 return false;
147 }
148 127 *valp = val;
149 127 return true;
150 }
151
152 56 bool str_to_uint(const char *str, unsigned int *valp)
153 {
154 56 uintmax_t x;
155
3/4
✓ Branch 0 (3→4) taken 52 times.
✓ Branch 1 (3→6) taken 4 times.
✓ Branch 2 (4→5) taken 52 times.
✗ Branch 3 (4→6) not taken.
56 if (!str_to_uintmax(str, &x) || x > UINT_MAX) {
156 return false;
157 }
158 52 *valp = (unsigned int)x;
159 52 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 80 bool str_to_size(const char *str, size_t *valp)
173 {
174 80 uintmax_t x;
175
2/2
✓ Branch 0 (3→4) taken 70 times.
✓ Branch 1 (3→5) taken 10 times.
80 if (!str_to_uintmax(str, &x) || x > SIZE_MAX) {
176 return false;
177 }
178 70 *valp = (size_t)x;
179 70 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 bool r = str_to_xfilepos(str, linep, colp);
219
4/4
✓ Branch 0 (3→4) taken 4 times.
✓ Branch 1 (3→6) taken 13 times.
✓ Branch 2 (4→5) taken 1 times.
✓ Branch 3 (4→6) taken 3 times.
17 if (r && *colp == 0) {
220 1 *colp = 1;
221 }
222 17 return r;
223 }
224
225 // Return the number of decimal digits in `x`
226 13 size_t size_str_width(size_t x)
227 {
228 13 size_t width = 0;
229 29 do {
230 29 x /= 10;
231 29 width++;
232
2/2
✓ Branch 0 (3→4) taken 16 times.
✓ Branch 1 (3→5) taken 13 times.
29 } while (x);
233 13 return width;
234 }
235
236 // Convert a string of decimal digits with an optional KiB/MiB/GiB/etc.
237 // suffix to an intmax_t value representing the number of bytes, or a
238 // negated <errno.h> value in the case of errors
239 30 intmax_t parse_filesize(const char *str)
240 {
241 30 uintmax_t x;
242 30 size_t ndigits = buf_parse_uintmax(str, strlen(str), &x);
243
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)) {
244
2/2
✓ Branch 0 (5→6) taken 2 times.
✓ Branch 1 (5→7) taken 1 times.
5 return ascii_isdigit(str[0]) ? -EOVERFLOW : -EINVAL;
245 }
246
247 27 const char *suffix = str + ndigits;
248 27 unsigned int shift;
249
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]) {
250 case 'K': shift = 10; break;
251 3 case 'M': shift = 20; break;
252 9 case 'G': shift = 30; break;
253 1 case 'T': shift = 40; break;
254 1 case 'P': shift = 50; break;
255 2 case 'E': shift = 60; break;
256 4 case '\0': return x;
257 default: return -EINVAL;
258 }
259
260
2/2
✓ Branch 0 (15→16) taken 13 times.
✓ Branch 1 (15→18) taken 8 times.
21 if (unlikely(!streq(suffix + 1, "iB"))) {
261 return -EINVAL;
262 }
263
264 13 uintmax_t val = x << shift;
265
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)) {
266 return -EOVERFLOW;
267 }
268
269 11 return val;
270 }
271