1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2010-2014 Intel Corporation
3 */
4
5 #include <stdio.h>
6 #include <string.h>
7 #include <inttypes.h>
8
9 #include <rte_string_fns.h>
10
11 #include <cmdline_parse.h>
12 #include <cmdline_parse_num.h>
13
14 #include "test_cmdline.h"
15
16 struct num_unsigned_str {
17 const char * str;
18 uint64_t result;
19 };
20
21 struct num_signed_str {
22 const char * str;
23 int64_t result;
24 };
25
26 const struct num_unsigned_str num_valid_positive_strs[] = {
27 /* decimal positive */
28 {"0", 0 },
29 {"127", INT8_MAX },
30 {"128", INT8_MAX + 1 },
31 {"255", UINT8_MAX },
32 {"256", UINT8_MAX + 1 },
33 {"32767", INT16_MAX },
34 {"32768", INT16_MAX + 1 },
35 {"65535", UINT16_MAX },
36 {"65536", UINT16_MAX + 1 },
37 {"2147483647", INT32_MAX },
38 {"2147483648", INT32_MAX + 1U },
39 {"4294967295", UINT32_MAX },
40 {"4294967296", UINT32_MAX + 1ULL },
41 {"9223372036854775807", INT64_MAX },
42 {"9223372036854775808", INT64_MAX + 1ULL},
43 {"18446744073709551615", UINT64_MAX },
44 /* hexadecimal (no leading zeroes) */
45 {"0x0", 0 },
46 {"0x7F", INT8_MAX },
47 {"0x80", INT8_MAX + 1 },
48 {"0xFF", UINT8_MAX },
49 {"0x100", UINT8_MAX + 1 },
50 {"0x7FFF", INT16_MAX },
51 {"0x8000", INT16_MAX + 1 },
52 {"0xFFFF", UINT16_MAX },
53 {"0x10000", UINT16_MAX + 1 },
54 {"0x7FFFFFFF", INT32_MAX },
55 {"0x80000000", INT32_MAX + 1U },
56 {"0xFFFFFFFF", UINT32_MAX },
57 {"0x100000000", UINT32_MAX + 1ULL },
58 {"0x7FFFFFFFFFFFFFFF", INT64_MAX },
59 {"0x8000000000000000", INT64_MAX + 1ULL},
60 {"0xFFFFFFFFFFFFFFFF", UINT64_MAX },
61 /* hexadecimal (with leading zeroes) */
62 {"0x00", 0 },
63 {"0x7F", INT8_MAX },
64 {"0x80", INT8_MAX + 1 },
65 {"0xFF", UINT8_MAX },
66 {"0x0100", UINT8_MAX + 1 },
67 {"0x7FFF", INT16_MAX },
68 {"0x8000", INT16_MAX + 1 },
69 {"0xFFFF", UINT16_MAX },
70 {"0x00010000", UINT16_MAX + 1 },
71 {"0x7FFFFFFF", INT32_MAX },
72 {"0x80000000", INT32_MAX + 1U },
73 {"0xFFFFFFFF", UINT32_MAX },
74 {"0x0000000100000000", UINT32_MAX + 1ULL },
75 {"0x7FFFFFFFFFFFFFFF", INT64_MAX },
76 {"0x8000000000000000", INT64_MAX + 1ULL},
77 {"0xFFFFFFFFFFFFFFFF", UINT64_MAX },
78 /* check all characters */
79 {"0x1234567890ABCDEF", 0x1234567890ABCDEFULL },
80 {"0x1234567890abcdef", 0x1234567890ABCDEFULL },
81 /* binary (no leading zeroes) */
82 {"0b0", 0 },
83 {"0b1111111", INT8_MAX },
84 {"0b10000000", INT8_MAX + 1 },
85 {"0b11111111", UINT8_MAX },
86 {"0b100000000", UINT8_MAX + 1 },
87 {"0b111111111111111", INT16_MAX },
88 {"0b1000000000000000", INT16_MAX + 1 },
89 {"0b1111111111111111", UINT16_MAX },
90 {"0b10000000000000000", UINT16_MAX + 1 },
91 {"0b1111111111111111111111111111111", INT32_MAX },
92 {"0b10000000000000000000000000000000", INT32_MAX + 1U },
93 {"0b11111111111111111111111111111111", UINT32_MAX },
94 {"0b100000000000000000000000000000000", UINT32_MAX + 1ULL },
95 {"0b111111111111111111111111111111111111111111111111111111111111111",
96 INT64_MAX },
97 {"0b1000000000000000000000000000000000000000000000000000000000000000",
98 INT64_MAX + 1ULL},
99 {"0b1111111111111111111111111111111111111111111111111111111111111111",
100 UINT64_MAX },
101 /* binary (with leading zeroes) */
102 {"0b01111111", INT8_MAX },
103 {"0b0000000100000000", UINT8_MAX + 1 },
104 {"0b0111111111111111", INT16_MAX },
105 {"0b00000000000000010000000000000000", UINT16_MAX + 1 },
106 {"0b01111111111111111111111111111111", INT32_MAX },
107 {"0b0000000000000000000000000000000100000000000000000000000000000000",
108 UINT32_MAX + 1ULL },
109 {"0b0111111111111111111111111111111111111111111111111111111111111111",
110 INT64_MAX },
111 /* octal */
112 {"00", 0 },
113 {"0177", INT8_MAX },
114 {"0200", INT8_MAX + 1 },
115 {"0377", UINT8_MAX },
116 {"0400", UINT8_MAX + 1 },
117 {"077777", INT16_MAX },
118 {"0100000", INT16_MAX + 1 },
119 {"0177777", UINT16_MAX },
120 {"0200000", UINT16_MAX + 1 },
121 {"017777777777", INT32_MAX },
122 {"020000000000", INT32_MAX + 1U },
123 {"037777777777", UINT32_MAX },
124 {"040000000000", UINT32_MAX + 1ULL },
125 {"0777777777777777777777", INT64_MAX },
126 {"01000000000000000000000", INT64_MAX + 1ULL},
127 {"01777777777777777777777", UINT64_MAX },
128 /* check all numbers */
129 {"012345670", 012345670 },
130 {"076543210", 076543210 },
131 };
132
133 const struct num_signed_str num_valid_negative_strs[] = {
134 /* deciman negative */
135 {"-128", INT8_MIN },
136 {"-129", INT8_MIN - 1 },
137 {"-32768", INT16_MIN },
138 {"-32769", INT16_MIN - 1 },
139 {"-2147483648", INT32_MIN },
140 {"-2147483649", INT32_MIN - 1LL },
141 {"-9223372036854775808", INT64_MIN },
142 };
143
144 const struct num_unsigned_str num_garbage_positive_strs[] = {
145 /* valid strings with garbage on the end, should still be valid */
146 /* decimal */
147 {"9223372036854775807\0garbage", INT64_MAX },
148 {"9223372036854775807\tgarbage", INT64_MAX },
149 {"9223372036854775807\rgarbage", INT64_MAX },
150 {"9223372036854775807\ngarbage", INT64_MAX },
151 {"9223372036854775807#garbage", INT64_MAX },
152 {"9223372036854775807 garbage", INT64_MAX },
153 /* hex */
154 {"0x7FFFFFFFFFFFFFFF\0garbage", INT64_MAX },
155 {"0x7FFFFFFFFFFFFFFF\tgarbage", INT64_MAX },
156 {"0x7FFFFFFFFFFFFFFF\rgarbage", INT64_MAX },
157 {"0x7FFFFFFFFFFFFFFF\ngarbage", INT64_MAX },
158 {"0x7FFFFFFFFFFFFFFF#garbage", INT64_MAX },
159 {"0x7FFFFFFFFFFFFFFF garbage", INT64_MAX },
160 /* binary */
161 {"0b1111111111111111111111111111111\0garbage", INT32_MAX },
162 {"0b1111111111111111111111111111111\rgarbage", INT32_MAX },
163 {"0b1111111111111111111111111111111\tgarbage", INT32_MAX },
164 {"0b1111111111111111111111111111111\ngarbage", INT32_MAX },
165 {"0b1111111111111111111111111111111#garbage", INT32_MAX },
166 {"0b1111111111111111111111111111111 garbage", INT32_MAX },
167 /* octal */
168 {"01777777777777777777777\0garbage", UINT64_MAX },
169 {"01777777777777777777777\rgarbage", UINT64_MAX },
170 {"01777777777777777777777\tgarbage", UINT64_MAX },
171 {"01777777777777777777777\ngarbage", UINT64_MAX },
172 {"01777777777777777777777#garbage", UINT64_MAX },
173 {"01777777777777777777777 garbage", UINT64_MAX },
174 };
175
176 const struct num_signed_str num_garbage_negative_strs[] = {
177 /* valid strings with garbage on the end, should still be valid */
178 {"-9223372036854775808\0garbage", INT64_MIN },
179 {"-9223372036854775808\rgarbage", INT64_MIN },
180 {"-9223372036854775808\tgarbage", INT64_MIN },
181 {"-9223372036854775808\ngarbage", INT64_MIN },
182 {"-9223372036854775808#garbage", INT64_MIN },
183 {"-9223372036854775808 garbage", INT64_MIN },
184 };
185
186 const char * num_invalid_strs[] = {
187 "18446744073709551616", /* out of range unsigned */
188 "-9223372036854775809", /* out of range negative signed */
189 "0x10000000000000000", /* out of range hex */
190 /* out of range binary */
191 "0b10000000000000000000000000000000000000000000000000000000000000000",
192 "020000000000000000000000", /* out of range octal */
193 /* wrong chars */
194 "0123456239",
195 "0x1234580AGE",
196 "0b0111010101g001",
197 "0b01110101017001",
198 /* false negative numbers */
199 "-12345F623",
200 "-0x1234580A",
201 "-0b0111010101",
202 /* too long (128+ chars) */
203 ("0b1111000011110000111100001111000011110000111100001111000011110000"
204 "1111000011110000111100001111000011110000111100001111000011110000"),
205 "1E3",
206 "0A",
207 "-B",
208 "+4",
209 "1.23G",
210 "",
211 " ",
212 "#",
213 "\r",
214 "\t",
215 "\n",
216 "\0",
217 };
218
219 static int
can_parse_unsigned(uint64_t expected_result,enum cmdline_numtype type)220 can_parse_unsigned(uint64_t expected_result, enum cmdline_numtype type)
221 {
222 switch (type) {
223 case RTE_UINT8:
224 if (expected_result > UINT8_MAX)
225 return 0;
226 break;
227 case RTE_UINT16:
228 if (expected_result > UINT16_MAX)
229 return 0;
230 break;
231 case RTE_UINT32:
232 if (expected_result > UINT32_MAX)
233 return 0;
234 break;
235 case RTE_INT8:
236 if (expected_result > INT8_MAX)
237 return 0;
238 break;
239 case RTE_INT16:
240 if (expected_result > INT16_MAX)
241 return 0;
242 break;
243 case RTE_INT32:
244 if (expected_result > INT32_MAX)
245 return 0;
246 break;
247 case RTE_INT64:
248 if (expected_result > INT64_MAX)
249 return 0;
250 break;
251 default:
252 return 1;
253 }
254 return 1;
255 }
256
257 static int
can_parse_signed(int64_t expected_result,enum cmdline_numtype type)258 can_parse_signed(int64_t expected_result, enum cmdline_numtype type)
259 {
260 switch (type) {
261 case RTE_UINT8:
262 if (expected_result > UINT8_MAX || expected_result < 0)
263 return 0;
264 break;
265 case RTE_UINT16:
266 if (expected_result > UINT16_MAX || expected_result < 0)
267 return 0;
268 break;
269 case RTE_UINT32:
270 if (expected_result > UINT32_MAX || expected_result < 0)
271 return 0;
272 break;
273 case RTE_UINT64:
274 if (expected_result < 0)
275 return 0;
276 break;
277 case RTE_INT8:
278 if (expected_result > INT8_MAX || expected_result < INT8_MIN)
279 return 0;
280 break;
281 case RTE_INT16:
282 if (expected_result > INT16_MAX || expected_result < INT16_MIN)
283 return 0;
284 break;
285 case RTE_INT32:
286 if (expected_result > INT32_MAX || expected_result < INT32_MIN)
287 return 0;
288 break;
289 default:
290 return 1;
291 }
292 return 1;
293 }
294
295 /* test invalid parameters */
296 int
test_parse_num_invalid_param(void)297 test_parse_num_invalid_param(void)
298 {
299 char buf[CMDLINE_TEST_BUFSIZE];
300 uint32_t result;
301 cmdline_parse_token_num_t token;
302 int ret = 0;
303
304 /* set up a token */
305 token.num_data.type = RTE_UINT32;
306
307 /* copy string to buffer */
308 strlcpy(buf, num_valid_positive_strs[0].str, sizeof(buf));
309
310 /* try all null */
311 ret = cmdline_parse_num(NULL, NULL, NULL, 0);
312 if (ret != -1) {
313 printf("Error: parser accepted null parameters!\n");
314 return -1;
315 }
316
317 /* try null token */
318 ret = cmdline_parse_num(NULL, buf, (void*)&result, sizeof(result));
319 if (ret != -1) {
320 printf("Error: parser accepted null token!\n");
321 return -1;
322 }
323
324 /* try null buf */
325 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token, NULL,
326 (void*)&result, sizeof(result));
327 if (ret != -1) {
328 printf("Error: parser accepted null string!\n");
329 return -1;
330 }
331
332 /* try null result */
333 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token, buf,
334 NULL, 0);
335 if (ret == -1) {
336 printf("Error: parser rejected null result!\n");
337 return -1;
338 }
339
340 /* test help function */
341 memset(&buf, 0, sizeof(buf));
342
343 /* try all null */
344 ret = cmdline_get_help_num(NULL, NULL, 0);
345 if (ret != -1) {
346 printf("Error: help function accepted null parameters!\n");
347 return -1;
348 }
349
350 /* try null token */
351 ret = cmdline_get_help_num(NULL, buf, sizeof(buf));
352 if (ret != -1) {
353 printf("Error: help function accepted null token!\n");
354 return -1;
355 }
356
357 /* coverage! */
358 ret = cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, buf, sizeof(buf));
359 if (ret < 0) {
360 printf("Error: help function failed with valid parameters!\n");
361 return -1;
362 }
363
364 return 0;
365 }
366 /* test valid parameters but invalid data */
367 int
test_parse_num_invalid_data(void)368 test_parse_num_invalid_data(void)
369 {
370 enum cmdline_numtype type;
371 int ret = 0;
372 unsigned i;
373 char buf[CMDLINE_TEST_BUFSIZE];
374 uint64_t result; /* pick largest buffer */
375 cmdline_parse_token_num_t token;
376
377 /* cycle through all possible parsed types */
378 for (type = RTE_UINT8; type <= RTE_INT64; type++) {
379 token.num_data.type = type;
380
381 /* test full strings */
382 for (i = 0; i < RTE_DIM(num_invalid_strs); i++) {
383
384 memset(&result, 0, sizeof(uint64_t));
385 memset(&buf, 0, sizeof(buf));
386
387 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token,
388 num_invalid_strs[i], (void*)&result, sizeof(result));
389 if (ret != -1) {
390 /* get some info about what we are trying to parse */
391 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
392 buf, sizeof(buf));
393
394 printf("Error: parsing %s as %s succeeded!\n",
395 num_invalid_strs[i], buf);
396 return -1;
397 }
398 }
399 }
400 return 0;
401 }
402
403 /* test valid parameters and data */
404 int
test_parse_num_valid(void)405 test_parse_num_valid(void)
406 {
407 int ret = 0;
408 enum cmdline_numtype type;
409 unsigned i;
410 char buf[CMDLINE_TEST_BUFSIZE];
411 uint64_t result;
412 cmdline_parse_token_num_t token;
413
414 /** valid strings **/
415
416 /* cycle through all possible parsed types */
417 for (type = RTE_UINT8; type <= RTE_INT64; type++) {
418 token.num_data.type = type;
419
420 /* test positive strings */
421 for (i = 0; i < RTE_DIM(num_valid_positive_strs); i++) {
422 result = 0;
423 memset(&buf, 0, sizeof(buf));
424
425 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
426 buf, sizeof(buf));
427
428 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
429 num_valid_positive_strs[i].str,
430 (void*)&result, sizeof(result));
431
432 /* if it should have passed but didn't, or if it should have failed but didn't */
433 if ((ret < 0) == (can_parse_unsigned(num_valid_positive_strs[i].result, type) > 0)) {
434 printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
435 num_valid_positive_strs[i].str, buf);
436 return -1;
437 }
438 /* check if result matches what it should have matched
439 * since unsigned numbers don't care about number of bits, we can just convert
440 * everything to uint64_t without any worries. */
441 if (ret > 0 && num_valid_positive_strs[i].result != result) {
442 printf("Error: parsing %s as %s failed: result mismatch!\n",
443 num_valid_positive_strs[i].str, buf);
444 return -1;
445 }
446 }
447
448 /* test negative strings */
449 for (i = 0; i < RTE_DIM(num_valid_negative_strs); i++) {
450 result = 0;
451 memset(&buf, 0, sizeof(buf));
452
453 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
454 buf, sizeof(buf));
455
456 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
457 num_valid_negative_strs[i].str,
458 (void*)&result, sizeof(result));
459
460 /* if it should have passed but didn't, or if it should have failed but didn't */
461 if ((ret < 0) == (can_parse_signed(num_valid_negative_strs[i].result, type) > 0)) {
462 printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
463 num_valid_negative_strs[i].str, buf);
464 return -1;
465 }
466 /* check if result matches what it should have matched
467 * the result is signed in this case, so we have to account for that */
468 if (ret > 0) {
469 /* detect negative */
470 switch (type) {
471 case RTE_INT8:
472 result = (int8_t) result;
473 break;
474 case RTE_INT16:
475 result = (int16_t) result;
476 break;
477 case RTE_INT32:
478 result = (int32_t) result;
479 break;
480 default:
481 break;
482 }
483 if (num_valid_negative_strs[i].result == (int64_t) result)
484 continue;
485 printf("Error: parsing %s as %s failed: result mismatch!\n",
486 num_valid_negative_strs[i].str, buf);
487 return -1;
488 }
489 }
490 }
491
492 /** garbage strings **/
493
494 /* cycle through all possible parsed types */
495 for (type = RTE_UINT8; type <= RTE_INT64; type++) {
496 token.num_data.type = type;
497
498 /* test positive garbage strings */
499 for (i = 0; i < RTE_DIM(num_garbage_positive_strs); i++) {
500 result = 0;
501 memset(&buf, 0, sizeof(buf));
502
503 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
504 buf, sizeof(buf));
505
506 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
507 num_garbage_positive_strs[i].str,
508 (void*)&result, sizeof(result));
509
510 /* if it should have passed but didn't, or if it should have failed but didn't */
511 if ((ret < 0) == (can_parse_unsigned(num_garbage_positive_strs[i].result, type) > 0)) {
512 printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
513 num_garbage_positive_strs[i].str, buf);
514 return -1;
515 }
516 /* check if result matches what it should have matched
517 * since unsigned numbers don't care about number of bits, we can just convert
518 * everything to uint64_t without any worries. */
519 if (ret > 0 && num_garbage_positive_strs[i].result != result) {
520 printf("Error: parsing %s as %s failed: result mismatch!\n",
521 num_garbage_positive_strs[i].str, buf);
522 return -1;
523 }
524 }
525
526 /* test negative strings */
527 for (i = 0; i < RTE_DIM(num_garbage_negative_strs); i++) {
528 result = 0;
529 memset(&buf, 0, sizeof(buf));
530
531 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
532 buf, sizeof(buf));
533
534 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
535 num_garbage_negative_strs[i].str,
536 (void*)&result, sizeof(result));
537
538 /* if it should have passed but didn't, or if it should have failed but didn't */
539 if ((ret < 0) == (can_parse_signed(num_garbage_negative_strs[i].result, type) > 0)) {
540 printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
541 num_garbage_negative_strs[i].str, buf);
542 return -1;
543 }
544 /* check if result matches what it should have matched
545 * the result is signed in this case, so we have to account for that */
546 if (ret > 0) {
547 /* detect negative */
548 switch (type) {
549 case RTE_INT8:
550 if (result & (INT8_MAX + 1))
551 result |= 0xFFFFFFFFFFFFFF00ULL;
552 break;
553 case RTE_INT16:
554 if (result & (INT16_MAX + 1))
555 result |= 0xFFFFFFFFFFFF0000ULL;
556 break;
557 case RTE_INT32:
558 if (result & (INT32_MAX + 1ULL))
559 result |= 0xFFFFFFFF00000000ULL;
560 break;
561 default:
562 break;
563 }
564 if (num_garbage_negative_strs[i].result == (int64_t) result)
565 continue;
566 printf("Error: parsing %s as %s failed: result mismatch!\n",
567 num_garbage_negative_strs[i].str, buf);
568 return -1;
569 }
570 }
571 }
572
573 memset(&buf, 0, sizeof(buf));
574
575 /* coverage! */
576 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
577 buf, sizeof(buf));
578
579 return 0;
580 }
581