diff --git a/src/hb-number-parser.hh b/src/hb-number-parser.hh index b666d00c5..49ade7a2e 100644 --- a/src/hb-number-parser.hh +++ b/src/hb-number-parser.hh @@ -30,8 +30,10 @@ #include "hb.hh" +#include -#line 35 "hb-number-parser.hh" + +#line 37 "hb-number-parser.hh" static const unsigned char _double_parser_trans_keys[] = { 0u, 0u, 43u, 57u, 46u, 57u, 48u, 57u, 43u, 57u, 48u, 57u, 48u, 101u, 48u, 57u, 46u, 101u, 0 @@ -91,7 +93,7 @@ static const int double_parser_error = 0; static const int double_parser_en_main = 1; -#line 55 "hb-number-parser.rl" +#line 70 "hb-number-parser.rl" constexpr double _pow2 (double x) { return x * x; } @@ -130,7 +132,9 @@ strtod_rl (const char *buf, char **end_ptr) double frac = 0; double frac_count = 0; unsigned int exp = 0; - bool neg = false, exp_neg = false; + bool neg = false, exp_neg = false, exp_overflow = false; + const unsigned long long MAX_FRACT = 0xFFFFFFFFFFFFFull; /* 1^52-1 */ + const unsigned int MAX_EXP = 0x7FFu; /* 1^11-1 */ p = buf; pe = p + strlen (p); @@ -139,12 +143,12 @@ strtod_rl (const char *buf, char **end_ptr) int cs; -#line 143 "hb-number-parser.hh" +#line 147 "hb-number-parser.hh" { cs = double_parser_start; } -#line 148 "hb-number-parser.hh" +#line 152 "hb-number-parser.hh" { int _slen; int _trans; @@ -170,26 +174,39 @@ _resume: switch ( _double_parser_trans_actions[_trans] ) { case 1: -#line 37 "hb-number-parser.rl" +#line 39 "hb-number-parser.rl" { neg = true; } break; case 4: -#line 38 "hb-number-parser.rl" +#line 40 "hb-number-parser.rl" { exp_neg = true; } break; case 2: -#line 40 "hb-number-parser.rl" - { value = value * 10. + ((*p) - '0'); } +#line 42 "hb-number-parser.rl" + { + value = value * 10. + ((*p) - '0'); +} break; case 3: -#line 41 "hb-number-parser.rl" - { frac = frac * 10. + ((*p) - '0'); ++frac_count; } +#line 45 "hb-number-parser.rl" + { + if (likely (frac <= MAX_FRACT / 10)) + { + frac = frac * 10. + ((*p) - '0'); + ++frac_count; + } +} break; case 5: -#line 42 "hb-number-parser.rl" - { exp = exp * 10 + ((*p) - '0'); } +#line 52 "hb-number-parser.rl" + { + if (likely (exp * 10 + ((*p) - '0') <= MAX_EXP)) + exp = exp * 10 + ((*p) - '0'); + else + exp_overflow = true; +} break; -#line 193 "hb-number-parser.hh" +#line 210 "hb-number-parser.hh" } _again: @@ -201,7 +218,7 @@ _again: _out: {} } -#line 104 "hb-number-parser.rl" +#line 121 "hb-number-parser.rl" *end_ptr = (char *) p; @@ -209,12 +226,17 @@ _again: if (frac_count) value += frac / _pow10 (frac_count); if (neg) value *= -1.; + if (unlikely (exp_overflow)) + { + if (value == 0) return value; + if (exp_neg) return neg ? -DBL_MIN : DBL_MIN; + else return neg ? -DBL_MAX : DBL_MAX; + } + if (exp) { - if (exp_neg) - value /= _pow10 (exp); - else - value *= _pow10 (exp); + if (exp_neg) value /= _pow10 (exp); + else value *= _pow10 (exp); } return value; diff --git a/src/hb-number-parser.rl b/src/hb-number-parser.rl index 6f0830670..db7b2ccc9 100644 --- a/src/hb-number-parser.rl +++ b/src/hb-number-parser.rl @@ -28,6 +28,8 @@ #include "hb.hh" +#include + %%{ machine double_parser; @@ -37,9 +39,22 @@ write data; action see_neg { neg = true; } action see_exp_neg { exp_neg = true; } -action add_int { value = value * 10. + (fc - '0'); } -action add_frac { frac = frac * 10. + (fc - '0'); ++frac_count; } -action add_exp { exp = exp * 10 + (fc - '0'); } +action add_int { + value = value * 10. + (fc - '0'); +} +action add_frac { + if (likely (frac <= MAX_FRACT / 10)) + { + frac = frac * 10. + (fc - '0'); + ++frac_count; + } +} +action add_exp { + if (likely (exp * 10 + (fc - '0') <= MAX_EXP)) + exp = exp * 10 + (fc - '0'); + else + exp_overflow = true; +} num = [0-9]+; @@ -90,7 +105,9 @@ strtod_rl (const char *buf, char **end_ptr) double frac = 0; double frac_count = 0; unsigned int exp = 0; - bool neg = false, exp_neg = false; + bool neg = false, exp_neg = false, exp_overflow = false; + const unsigned long long MAX_FRACT = 0xFFFFFFFFFFFFFull; /* 1^52-1 */ + const unsigned int MAX_EXP = 0x7FFu; /* 1^11-1 */ p = buf; pe = p + strlen (p); @@ -108,12 +125,17 @@ strtod_rl (const char *buf, char **end_ptr) if (frac_count) value += frac / _pow10 (frac_count); if (neg) value *= -1.; + if (unlikely (exp_overflow)) + { + if (value == 0) return value; + if (exp_neg) return neg ? -DBL_MIN : DBL_MIN; + else return neg ? -DBL_MAX : DBL_MAX; + } + if (exp) { - if (exp_neg) - value /= _pow10 (exp); - else - value *= _pow10 (exp); + if (exp_neg) value /= _pow10 (exp); + else value *= _pow10 (exp); } return value;