diff options
Diffstat (limited to 'src/lj_strscan.c')
-rw-r--r-- | src/lj_strscan.c | 80 |
1 files changed, 67 insertions, 13 deletions
diff --git a/src/lj_strscan.c b/src/lj_strscan.c index 914cfb7a..1d1c1c74 100644 --- a/src/lj_strscan.c +++ b/src/lj_strscan.c | |||
@@ -80,7 +80,7 @@ static void strscan_double(uint64_t x, TValue *o, int32_t ex2, int32_t neg) | |||
80 | /* Avoid double rounding for denormals. */ | 80 | /* Avoid double rounding for denormals. */ |
81 | if (LJ_UNLIKELY(ex2 <= -1075 && x != 0)) { | 81 | if (LJ_UNLIKELY(ex2 <= -1075 && x != 0)) { |
82 | /* NYI: all of this generates way too much code on 32 bit CPUs. */ | 82 | /* NYI: all of this generates way too much code on 32 bit CPUs. */ |
83 | #if defined(__GNUC__) && LJ_64 | 83 | #if (defined(__GNUC__) || defined(__clang__)) && LJ_64 |
84 | int32_t b = (int32_t)(__builtin_clzll(x)^63); | 84 | int32_t b = (int32_t)(__builtin_clzll(x)^63); |
85 | #else | 85 | #else |
86 | int32_t b = (x>>32) ? 32+(int32_t)lj_fls((uint32_t)(x>>32)) : | 86 | int32_t b = (x>>32) ? 32+(int32_t)lj_fls((uint32_t)(x>>32)) : |
@@ -94,7 +94,7 @@ static void strscan_double(uint64_t x, TValue *o, int32_t ex2, int32_t neg) | |||
94 | } | 94 | } |
95 | 95 | ||
96 | /* Convert to double using a signed int64_t conversion, then rescale. */ | 96 | /* Convert to double using a signed int64_t conversion, then rescale. */ |
97 | lua_assert((int64_t)x >= 0); | 97 | lj_assertX((int64_t)x >= 0, "bad double conversion"); |
98 | n = (double)(int64_t)x; | 98 | n = (double)(int64_t)x; |
99 | if (neg) n = -n; | 99 | if (neg) n = -n; |
100 | if (ex2) n = ldexp(n, ex2); | 100 | if (ex2) n = ldexp(n, ex2); |
@@ -142,7 +142,7 @@ static StrScanFmt strscan_hex(const uint8_t *p, TValue *o, | |||
142 | break; | 142 | break; |
143 | } | 143 | } |
144 | 144 | ||
145 | /* Reduce range then convert to double. */ | 145 | /* Reduce range, then convert to double. */ |
146 | if ((x & U64x(c0000000,0000000))) { x = (x >> 2) | (x & 3); ex2 += 2; } | 146 | if ((x & U64x(c0000000,0000000))) { x = (x >> 2) | (x & 3); ex2 += 2; } |
147 | strscan_double(x, o, ex2, neg); | 147 | strscan_double(x, o, ex2, neg); |
148 | return fmt; | 148 | return fmt; |
@@ -264,7 +264,7 @@ static StrScanFmt strscan_dec(const uint8_t *p, TValue *o, | |||
264 | uint32_t hi = 0, lo = (uint32_t)(xip-xi); | 264 | uint32_t hi = 0, lo = (uint32_t)(xip-xi); |
265 | int32_t ex2 = 0, idig = (int32_t)lo + (ex10 >> 1); | 265 | int32_t ex2 = 0, idig = (int32_t)lo + (ex10 >> 1); |
266 | 266 | ||
267 | lua_assert(lo > 0 && (ex10 & 1) == 0); | 267 | lj_assertX(lo > 0 && (ex10 & 1) == 0, "bad lo %d ex10 %d", lo, ex10); |
268 | 268 | ||
269 | /* Handle simple overflow/underflow. */ | 269 | /* Handle simple overflow/underflow. */ |
270 | if (idig > 310/2) { if (neg) setminfV(o); else setpinfV(o); return fmt; } | 270 | if (idig > 310/2) { if (neg) setminfV(o); else setpinfV(o); return fmt; } |
@@ -328,10 +328,55 @@ static StrScanFmt strscan_dec(const uint8_t *p, TValue *o, | |||
328 | return fmt; | 328 | return fmt; |
329 | } | 329 | } |
330 | 330 | ||
331 | /* Parse binary number. */ | ||
332 | static StrScanFmt strscan_bin(const uint8_t *p, TValue *o, | ||
333 | StrScanFmt fmt, uint32_t opt, | ||
334 | int32_t ex2, int32_t neg, uint32_t dig) | ||
335 | { | ||
336 | uint64_t x = 0; | ||
337 | uint32_t i; | ||
338 | |||
339 | if (ex2 || dig > 64) return STRSCAN_ERROR; | ||
340 | |||
341 | /* Scan binary digits. */ | ||
342 | for (i = dig; i; i--, p++) { | ||
343 | if ((*p & ~1) != '0') return STRSCAN_ERROR; | ||
344 | x = (x << 1) | (*p & 1); | ||
345 | } | ||
346 | |||
347 | /* Format-specific handling. */ | ||
348 | switch (fmt) { | ||
349 | case STRSCAN_INT: | ||
350 | if (!(opt & STRSCAN_OPT_TONUM) && x < 0x80000000u+neg) { | ||
351 | o->i = neg ? -(int32_t)x : (int32_t)x; | ||
352 | return STRSCAN_INT; /* Fast path for 32 bit integers. */ | ||
353 | } | ||
354 | if (!(opt & STRSCAN_OPT_C)) { fmt = STRSCAN_NUM; break; } | ||
355 | /* fallthrough */ | ||
356 | case STRSCAN_U32: | ||
357 | if (dig > 32) return STRSCAN_ERROR; | ||
358 | o->i = neg ? -(int32_t)x : (int32_t)x; | ||
359 | return STRSCAN_U32; | ||
360 | case STRSCAN_I64: | ||
361 | case STRSCAN_U64: | ||
362 | o->u64 = neg ? (uint64_t)-(int64_t)x : x; | ||
363 | return fmt; | ||
364 | default: | ||
365 | break; | ||
366 | } | ||
367 | |||
368 | /* Reduce range, then convert to double. */ | ||
369 | if ((x & U64x(c0000000,0000000))) { x = (x >> 2) | (x & 3); ex2 += 2; } | ||
370 | strscan_double(x, o, ex2, neg); | ||
371 | return fmt; | ||
372 | } | ||
373 | |||
331 | /* Scan string containing a number. Returns format. Returns value in o. */ | 374 | /* Scan string containing a number. Returns format. Returns value in o. */ |
332 | StrScanFmt lj_strscan_scan(const uint8_t *p, TValue *o, uint32_t opt) | 375 | StrScanFmt lj_strscan_scan(const uint8_t *p, MSize len, TValue *o, |
376 | uint32_t opt) | ||
333 | { | 377 | { |
334 | int32_t neg = 0; | 378 | int32_t neg = 0; |
379 | const uint8_t *pe = p + len; | ||
335 | 380 | ||
336 | /* Remove leading space, parse sign and non-numbers. */ | 381 | /* Remove leading space, parse sign and non-numbers. */ |
337 | if (LJ_UNLIKELY(!lj_char_isdigit(*p))) { | 382 | if (LJ_UNLIKELY(!lj_char_isdigit(*p))) { |
@@ -349,7 +394,7 @@ StrScanFmt lj_strscan_scan(const uint8_t *p, TValue *o, uint32_t opt) | |||
349 | p += 3; | 394 | p += 3; |
350 | } | 395 | } |
351 | while (lj_char_isspace(*p)) p++; | 396 | while (lj_char_isspace(*p)) p++; |
352 | if (*p) return STRSCAN_ERROR; | 397 | if (*p || p < pe) return STRSCAN_ERROR; |
353 | o->u64 = tmp.u64; | 398 | o->u64 = tmp.u64; |
354 | return STRSCAN_NUM; | 399 | return STRSCAN_NUM; |
355 | } | 400 | } |
@@ -366,8 +411,12 @@ StrScanFmt lj_strscan_scan(const uint8_t *p, TValue *o, uint32_t opt) | |||
366 | 411 | ||
367 | /* Determine base and skip leading zeros. */ | 412 | /* Determine base and skip leading zeros. */ |
368 | if (LJ_UNLIKELY(*p <= '0')) { | 413 | if (LJ_UNLIKELY(*p <= '0')) { |
369 | if (*p == '0' && casecmp(p[1], 'x')) | 414 | if (*p == '0') { |
370 | base = 16, cmask = LJ_CHAR_XDIGIT, p += 2; | 415 | if (casecmp(p[1], 'x')) |
416 | base = 16, cmask = LJ_CHAR_XDIGIT, p += 2; | ||
417 | else if (casecmp(p[1], 'b')) | ||
418 | base = 2, cmask = LJ_CHAR_DIGIT, p += 2; | ||
419 | } | ||
371 | for ( ; ; p++) { | 420 | for ( ; ; p++) { |
372 | if (*p == '0') { | 421 | if (*p == '0') { |
373 | hasdig = 1; | 422 | hasdig = 1; |
@@ -396,6 +445,7 @@ StrScanFmt lj_strscan_scan(const uint8_t *p, TValue *o, uint32_t opt) | |||
396 | 445 | ||
397 | /* Handle decimal point. */ | 446 | /* Handle decimal point. */ |
398 | if (dp) { | 447 | if (dp) { |
448 | if (base == 2) return STRSCAN_ERROR; | ||
399 | fmt = STRSCAN_NUM; | 449 | fmt = STRSCAN_NUM; |
400 | if (dig) { | 450 | if (dig) { |
401 | ex = (int32_t)(dp-(p-1)); dp = p-1; | 451 | ex = (int32_t)(dp-(p-1)); dp = p-1; |
@@ -406,7 +456,7 @@ StrScanFmt lj_strscan_scan(const uint8_t *p, TValue *o, uint32_t opt) | |||
406 | } | 456 | } |
407 | 457 | ||
408 | /* Parse exponent. */ | 458 | /* Parse exponent. */ |
409 | if (casecmp(*p, (uint32_t)(base == 16 ? 'p' : 'e'))) { | 459 | if (base >= 10 && casecmp(*p, (uint32_t)(base == 16 ? 'p' : 'e'))) { |
410 | uint32_t xx; | 460 | uint32_t xx; |
411 | int negx = 0; | 461 | int negx = 0; |
412 | fmt = STRSCAN_NUM; p++; | 462 | fmt = STRSCAN_NUM; p++; |
@@ -445,6 +495,7 @@ StrScanFmt lj_strscan_scan(const uint8_t *p, TValue *o, uint32_t opt) | |||
445 | while (lj_char_isspace(*p)) p++; | 495 | while (lj_char_isspace(*p)) p++; |
446 | if (*p) return STRSCAN_ERROR; | 496 | if (*p) return STRSCAN_ERROR; |
447 | } | 497 | } |
498 | if (p < pe) return STRSCAN_ERROR; | ||
448 | 499 | ||
449 | /* Fast path for decimal 32 bit integers. */ | 500 | /* Fast path for decimal 32 bit integers. */ |
450 | if (fmt == STRSCAN_INT && base == 10 && | 501 | if (fmt == STRSCAN_INT && base == 10 && |
@@ -466,6 +517,8 @@ StrScanFmt lj_strscan_scan(const uint8_t *p, TValue *o, uint32_t opt) | |||
466 | return strscan_oct(sp, o, fmt, neg, dig); | 517 | return strscan_oct(sp, o, fmt, neg, dig); |
467 | if (base == 16) | 518 | if (base == 16) |
468 | fmt = strscan_hex(sp, o, fmt, opt, ex, neg, dig); | 519 | fmt = strscan_hex(sp, o, fmt, opt, ex, neg, dig); |
520 | else if (base == 2) | ||
521 | fmt = strscan_bin(sp, o, fmt, opt, ex, neg, dig); | ||
469 | else | 522 | else |
470 | fmt = strscan_dec(sp, o, fmt, opt, ex, neg, dig); | 523 | fmt = strscan_dec(sp, o, fmt, opt, ex, neg, dig); |
471 | 524 | ||
@@ -481,18 +534,19 @@ StrScanFmt lj_strscan_scan(const uint8_t *p, TValue *o, uint32_t opt) | |||
481 | 534 | ||
482 | int LJ_FASTCALL lj_strscan_num(GCstr *str, TValue *o) | 535 | int LJ_FASTCALL lj_strscan_num(GCstr *str, TValue *o) |
483 | { | 536 | { |
484 | StrScanFmt fmt = lj_strscan_scan((const uint8_t *)strdata(str), o, | 537 | StrScanFmt fmt = lj_strscan_scan((const uint8_t *)strdata(str), str->len, o, |
485 | STRSCAN_OPT_TONUM); | 538 | STRSCAN_OPT_TONUM); |
486 | lua_assert(fmt == STRSCAN_ERROR || fmt == STRSCAN_NUM); | 539 | lj_assertX(fmt == STRSCAN_ERROR || fmt == STRSCAN_NUM, "bad scan format"); |
487 | return (fmt != STRSCAN_ERROR); | 540 | return (fmt != STRSCAN_ERROR); |
488 | } | 541 | } |
489 | 542 | ||
490 | #if LJ_DUALNUM | 543 | #if LJ_DUALNUM |
491 | int LJ_FASTCALL lj_strscan_number(GCstr *str, TValue *o) | 544 | int LJ_FASTCALL lj_strscan_number(GCstr *str, TValue *o) |
492 | { | 545 | { |
493 | StrScanFmt fmt = lj_strscan_scan((const uint8_t *)strdata(str), o, | 546 | StrScanFmt fmt = lj_strscan_scan((const uint8_t *)strdata(str), str->len, o, |
494 | STRSCAN_OPT_TOINT); | 547 | STRSCAN_OPT_TOINT); |
495 | lua_assert(fmt == STRSCAN_ERROR || fmt == STRSCAN_NUM || fmt == STRSCAN_INT); | 548 | lj_assertX(fmt == STRSCAN_ERROR || fmt == STRSCAN_NUM || fmt == STRSCAN_INT, |
549 | "bad scan format"); | ||
496 | if (fmt == STRSCAN_INT) setitype(o, LJ_TISNUM); | 550 | if (fmt == STRSCAN_INT) setitype(o, LJ_TISNUM); |
497 | return (fmt != STRSCAN_ERROR); | 551 | return (fmt != STRSCAN_ERROR); |
498 | } | 552 | } |