diff options
| author | 2022-02-02 04:38:16 +0100 | |
|---|---|---|
| committer | 2022-02-02 04:38:16 +0100 | |
| commit | 474c8eb82e776bfac804338247045b11fa389d8d (patch) | |
| tree | 9c10d9469c2d6714ea111a5cecb49e2a236a01b0 /src/jplist.c | |
| parent | db4635a081b03eb9a2ea310bfb2f994fd9d4ee94 (diff) | |
| download | libplist-474c8eb82e776bfac804338247045b11fa389d8d.tar.gz libplist-474c8eb82e776bfac804338247045b11fa389d8d.tar.bz2 | |
jplist: Improve numerical value parsing without copying data to stack buffer
Instead of calling strtoll() and atof(), the code now parses the numerical
values directly to handle cases of non-0-terminated string data.
The floating point value parsing is probably not ideal, but sufficient for
our purposes.
Diffstat (limited to 'src/jplist.c')
| -rw-r--r-- | src/jplist.c | 80 |
1 files changed, 62 insertions, 18 deletions
diff --git a/src/jplist.c b/src/jplist.c index 65eb528..1629f59 100644 --- a/src/jplist.c +++ b/src/jplist.c | |||
| @@ -423,6 +423,26 @@ typedef struct { | |||
| 423 | int count; | 423 | int count; |
| 424 | } jsmntok_info_t; | 424 | } jsmntok_info_t; |
| 425 | 425 | ||
| 426 | static long long parse_decimal(const char* str, const char* str_end, char** endp) | ||
| 427 | { | ||
| 428 | long long x = 0; | ||
| 429 | int is_neg = 0; | ||
| 430 | *endp = (char*)str; | ||
| 431 | |||
| 432 | if (str[0] == '-') { | ||
| 433 | is_neg = 1; | ||
| 434 | (*endp)++; | ||
| 435 | } | ||
| 436 | while (*endp < str_end && isdigit(**endp)) { | ||
| 437 | x = x * 10 + (**endp - '0'); | ||
| 438 | (*endp)++; | ||
| 439 | } | ||
| 440 | if (is_neg) { | ||
| 441 | x = -x; | ||
| 442 | } | ||
| 443 | return x; | ||
| 444 | } | ||
| 445 | |||
| 426 | static plist_t parse_primitive(const char* js, jsmntok_info_t* ti, int* index) | 446 | static plist_t parse_primitive(const char* js, jsmntok_info_t* ti, int* index) |
| 427 | { | 447 | { |
| 428 | if (ti->tokens[*index].type != JSMN_PRIMITIVE) { | 448 | if (ti->tokens[*index].type != JSMN_PRIMITIVE) { |
| @@ -441,27 +461,51 @@ static plist_t parse_primitive(const char* js, jsmntok_info_t* ti, int* index) | |||
| 441 | plist_data_t data = plist_new_plist_data(); | 461 | plist_data_t data = plist_new_plist_data(); |
| 442 | data->type = PLIST_NULL; | 462 | data->type = PLIST_NULL; |
| 443 | val = plist_new_node(data); | 463 | val = plist_new_node(data); |
| 444 | } else if (str_val[0] == '-' || isdigit(str_val[0])) { | 464 | } else if (isdigit(str_val[0]) || (str_val[0] == '-' && str_end > str_val && isdigit(str_val[1]))) { |
| 445 | char* endp = NULL; | 465 | char* endp = (char*)str_val; |
| 446 | char cbuf[48]; | 466 | long long intpart = parse_decimal(str_val, str_end, &endp); |
| 447 | size_t maxlen = str_end-str_val; | ||
| 448 | if (maxlen >= sizeof(cbuf)) maxlen = sizeof(cbuf)-1; | ||
| 449 | strncpy(cbuf, str_val, maxlen); | ||
| 450 | cbuf[maxlen] = '\0'; | ||
| 451 | long long intpart = strtoll(cbuf, &endp, 10); | ||
| 452 | endp = (char*)str_val + (endp-&cbuf[0]); | ||
| 453 | if (endp >= str_end) { | 467 | if (endp >= str_end) { |
| 454 | /* integer */ | 468 | /* integer */ |
| 455 | val = plist_new_uint((uint64_t)intpart); | 469 | val = plist_new_uint((uint64_t)intpart); |
| 456 | } else if (*endp == '.' && endp+1 < str_end && isdigit(*(endp+1))) { | 470 | } else if ((*endp == '.' && endp+1 < str_end && isdigit(*(endp+1))) || ((*endp == 'e' || *endp == 'E') && endp < str_end && (isdigit(*(endp+1)) || ((*(endp+1) == '-') && endp+1 < str_end && isdigit(*(endp+2)))))) { |
| 457 | /* float */ | 471 | /* floating point */ |
| 458 | char* fendp = endp+1; | 472 | double dval = (double)intpart; |
| 459 | while (isdigit(*fendp) && fendp < str_end) fendp++; | 473 | char* fendp = endp; |
| 460 | if ((fendp > endp+1 && fendp >= str_end) || (fendp+2 < str_end && (*fendp == 'e' || *fendp == 'E') && (*(fendp+1) == '+' || *(fendp+1) == '-') && isdigit(*(fendp+2)))) { | 474 | int err = 0; |
| 461 | double dval = atof(cbuf); | 475 | do { |
| 462 | val = plist_new_real(dval); | 476 | if (*endp == '.') { |
| 463 | } else { | 477 | fendp++; |
| 464 | PLIST_JSON_ERR("%s: invalid character at offset %d when parsing floating point value\n", __func__, (int)(fendp - js)); | 478 | int is_neg = (str_val[0] == '-'); |
| 479 | double frac = 0; | ||
| 480 | double p = 0.1; | ||
| 481 | while (isdigit(*fendp) && fendp < str_end) { | ||
| 482 | frac = frac + (*fendp - '0') * p; | ||
| 483 | p *= 0.1; | ||
| 484 | fendp++; | ||
| 485 | } | ||
| 486 | if (is_neg) { | ||
| 487 | dval -= frac; | ||
| 488 | } else { | ||
| 489 | dval += frac; | ||
| 490 | } | ||
| 491 | } | ||
| 492 | if (fendp >= str_end) { | ||
| 493 | break; | ||
| 494 | } | ||
| 495 | if (fendp+1 < str_end && (*fendp == 'e' || *fendp == 'E') && (isdigit(*(fendp+1)) || ((*(fendp+1) == '-') && fendp+2 < str_end && isdigit(*(fendp+2))))) { | ||
| 496 | double exp = (double)parse_decimal(fendp+1, str_end, &fendp); | ||
| 497 | dval = dval * pow(10, exp); | ||
| 498 | } else { | ||
| 499 | PLIST_JSON_ERR("%s: invalid character at offset %d when parsing floating point value\n", __func__, (int)(fendp - js)); | ||
| 500 | err++; | ||
| 501 | } | ||
| 502 | } while (0); | ||
| 503 | if (!err) { | ||
| 504 | if (isinf(dval) || isnan(dval)) { | ||
| 505 | PLIST_JSON_ERR("%s: unrepresentable floating point value at offset %d when parsing numerical value\n", __func__, (int)(str_val - js)); | ||
| 506 | } else { | ||
| 507 | val = plist_new_real(dval); | ||
| 508 | } | ||
| 465 | } | 509 | } |
| 466 | } else { | 510 | } else { |
| 467 | PLIST_JSON_ERR("%s: invalid character at offset %d when parsing numerical value\n", __func__, (int)(endp - js)); | 511 | PLIST_JSON_ERR("%s: invalid character at offset %d when parsing numerical value\n", __func__, (int)(endp - js)); |
