summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2022-02-02 04:38:16 +0100
committerGravatar Nikias Bassen2022-02-02 04:38:16 +0100
commit474c8eb82e776bfac804338247045b11fa389d8d (patch)
tree9c10d9469c2d6714ea111a5cecb49e2a236a01b0 /src
parentdb4635a081b03eb9a2ea310bfb2f994fd9d4ee94 (diff)
downloadlibplist-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')
-rw-r--r--src/jplist.c80
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
426static 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
426static plist_t parse_primitive(const char* js, jsmntok_info_t* ti, int* index) 446static 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));