diff options
Diffstat (limited to 'src/literal.cc')
-rw-r--r-- | src/literal.cc | 879 |
1 files changed, 325 insertions, 554 deletions
diff --git a/src/literal.cc b/src/literal.cc index 1e1d1d99..e57f513e 100644 --- a/src/literal.cc +++ b/src/literal.cc @@ -22,56 +22,101 @@ #include <cstdlib> #include <cstring> -#define HEX_DIGIT_BITS 4 - -/* The PLUS_ONE values are used because normal IEEE floats have an implicit - * leading one, so they have an additional bit of precision. */ - -#define F32_SIGN_SHIFT 31 -#define F32_SIG_BITS 23 -#define F32_SIG_MASK 0x7fffff -#define F32_SIG_PLUS_ONE_BITS 24 -#define F32_SIG_PLUS_ONE_MASK 0xffffff -#define F32_EXP_MASK 0xff -#define F32_MIN_EXP -127 -#define F32_MAX_EXP 128 -#define F32_EXP_BIAS 127 -#define F32_QUIET_NAN_TAG 0x400000 - -#define F64_SIGN_SHIFT 63 -#define F64_SIG_BITS 52 -#define F64_SIG_MASK 0xfffffffffffffULL -#define F64_SIG_PLUS_ONE_BITS 53 -#define F64_SIG_PLUS_ONE_MASK 0x1fffffffffffffULL -#define F64_EXP_MASK 0x7ff -#define F64_MIN_EXP -1023 -#define F64_MAX_EXP 1024 -#define F64_EXP_BIAS 1023 -#define F64_QUIET_NAN_TAG 0x8000000000000ULL - namespace wabt { -static const char s_hex_digits[] = "0123456789abcdef"; +namespace { -Result parse_hexdigit(char c, uint32_t* out) { - if (static_cast<unsigned int>(c - '0') <= 9) { - *out = c - '0'; - return Result::Ok; - } else if (static_cast<unsigned int>(c - 'a') <= 6) { - *out = 10 + (c - 'a'); - return Result::Ok; - } else if (static_cast<unsigned int>(c - 'A') <= 6) { - *out = 10 + (c - 'A'); - return Result::Ok; - } - return Result::Error; +int Clz(uint32_t value) { + return wabt_clz_u32(value); +} + +int Clz(uint64_t value) { + return wabt_clz_u64(value); } -/* return 1 if the non-NULL-terminated string starting with |start| and ending - with |end| starts with the NULL-terminated string |prefix|. */ -static bool string_starts_with(const char* start, +template <typename T> +struct FloatTraitsBase {}; + +// The "PlusOne" values are used because normal IEEE floats have an implicit +// leading one, so they have an additional bit of precision. + +template <> +struct FloatTraitsBase<float> { + typedef uint32_t Uint; + static constexpr int kBits = sizeof(Uint) * 8; + static constexpr int kSigBits = 23; + static constexpr float kHugeVal = HUGE_VALF; + static constexpr int kMaxHexBufferSize = WABT_MAX_FLOAT_HEX; +}; + +template <> +struct FloatTraitsBase<double> { + typedef uint64_t Uint; + static constexpr int kBits = sizeof(Uint) * 8; + static constexpr int kSigBits = 52; + static constexpr float kHugeVal = HUGE_VAL; + static constexpr int kMaxHexBufferSize = WABT_MAX_DOUBLE_HEX; +}; + +template <typename T> +struct FloatTraits : FloatTraitsBase<T> { + typedef typename FloatTraitsBase<T>::Uint Uint; + using FloatTraitsBase<T>::kBits; + using FloatTraitsBase<T>::kSigBits; + + static constexpr int kExpBits = kBits - kSigBits - 1; + static constexpr int kSignShift = kBits - 1; + static constexpr Uint kSigMask = (Uint(1) << kSigBits) - 1; + static constexpr int kSigPlusOneBits = kSigBits + 1; + static constexpr Uint kSigPlusOneMask = (Uint(1) << kSigPlusOneBits) - 1; + static constexpr int kExpMask = (1 << kExpBits) - 1; + static constexpr int kMaxExp = 1 << (kExpBits - 1); + static constexpr int kMinExp = -kMaxExp + 1; + static constexpr int kExpBias = -kMinExp; + static constexpr Uint kQuietNanTag = Uint(1) << (kSigBits - 1); +}; + +template <typename T> +class FloatParser { + public: + typedef FloatTraits<T> Traits; + typedef typename Traits::Uint Uint; + typedef T Float; + + static Result Parse(LiteralType, + const char* s, + const char* end, + Uint* out_bits); + + private: + static bool StringStartsWith(const char* start, const char* end, - const char* prefix) { + const char* prefix); + static Float Strto(const char* s, char** endptr); + static Uint Make(bool sign, int exp, Uint sig); + static Uint ShiftAndRoundToNearest(Uint significand, int shift); + + static Result ParseNan(const char* s, const char* end, Uint* out_bits); + static void ParseHex(const char* s, const char* end, Uint* out_bits); + static void ParseInfinity(const char* s, const char* end, Uint* out_bits); +}; + +template <typename T> +class FloatWriter { + public: + typedef FloatTraits<T> Traits; + typedef typename Traits::Uint Uint; + + static void WriteHex(char* out, size_t size, Uint bits); +}; + +// Return 1 if the non-NULL-terminated string starting with |start| and ending +// with |end| starts with the NULL-terminated string |prefix|. +template <typename T> +// static +bool FloatParser<T>::StringStartsWith(const char* start, + const char* end, + const char* prefix) { while (start < end && *prefix) { if (*start != *prefix) return false; @@ -81,116 +126,48 @@ static bool string_starts_with(const char* start, return *prefix == 0; } -Result parse_uint64(const char* s, const char* end, uint64_t* out) { - if (s == end) - return Result::Error; - uint64_t value = 0; - if (*s == '0' && s + 1 < end && s[1] == 'x') { - s += 2; - if (s == end) - return Result::Error; - for (; s < end; ++s) { - uint32_t digit; - if (WABT_FAILED(parse_hexdigit(*s, &digit))) - return Result::Error; - uint64_t old_value = value; - value = value * 16 + digit; - /* check for overflow */ - if (old_value > value) - return Result::Error; - } - } else { - for (; s < end; ++s) { - uint32_t digit = (*s - '0'); - if (digit > 9) - return Result::Error; - uint64_t old_value = value; - value = value * 10 + digit; - /* check for overflow */ - if (old_value > value) - return Result::Error; - } - } - if (s != end) - return Result::Error; - *out = value; - return Result::Ok; +// static +template <> +float FloatParser<float>::Strto(const char* s, char** endptr) { + return strtof(s, endptr); } -Result parse_int64(const char* s, - const char* end, - uint64_t* out, - ParseIntType parse_type) { - bool has_sign = false; - if (*s == '-' || *s == '+') { - if (parse_type == ParseIntType::UnsignedOnly) - return Result::Error; - if (*s == '-') - has_sign = true; - s++; - } - uint64_t value = 0; - Result result = parse_uint64(s, end, &value); - if (has_sign) { - /* abs(INT64_MIN) == INT64_MAX + 1 */ - if (value > static_cast<uint64_t>(INT64_MAX) + 1) - return Result::Error; - value = UINT64_MAX - value + 1; - } - *out = value; - return result; +// static +template <> +double FloatParser<double>::Strto(const char* s, char** endptr) { + return strtod(s, endptr); } -Result parse_int32(const char* s, - const char* end, - uint32_t* out, - ParseIntType parse_type) { - uint64_t value; - bool has_sign = false; - if (*s == '-' || *s == '+') { - if (parse_type == ParseIntType::UnsignedOnly) - return Result::Error; - if (*s == '-') - has_sign = true; - s++; - } - if (WABT_FAILED(parse_uint64(s, end, &value))) - return Result::Error; - - if (has_sign) { - /* abs(INT32_MIN) == INT32_MAX + 1 */ - if (value > static_cast<uint64_t>(INT32_MAX) + 1) - return Result::Error; - value = UINT32_MAX - value + 1; - } else { - if (value > static_cast<uint64_t>(UINT32_MAX)) - return Result::Error; - } - *out = static_cast<uint32_t>(value); - return Result::Ok; -} -/* floats */ -static uint32_t make_float(bool sign, int exp, uint32_t sig) { - assert(exp >= F32_MIN_EXP && exp <= F32_MAX_EXP); - assert(sig <= F32_SIG_MASK); - return (static_cast<uint32_t>(sign) << F32_SIGN_SHIFT) | - (static_cast<uint32_t>(exp + F32_EXP_BIAS) << F32_SIG_BITS) | sig; +// static +template <typename T> +typename FloatParser<T>::Uint FloatParser<T>::Make(bool sign, + int exp, + Uint sig) { + assert(exp >= Traits::kMinExp && exp <= Traits::kMaxExp); + assert(sig <= Traits::kSigMask); + return (Uint(sign) << Traits::kSignShift) | + (Uint(exp + Traits::kExpBias) << Traits::kSigBits) | sig; } -static uint32_t shift_float_and_round_to_nearest(uint32_t significand, - int shift) { +// static +template <typename T> +typename FloatParser<T>::Uint FloatParser<T>::ShiftAndRoundToNearest( + Uint significand, + int shift) { assert(shift > 0); - /* round ties to even */ - if (significand & (1U << shift)) - significand += 1U << (shift - 1); + // Round ties to even. + if (significand & (Uint(1) << shift)) + significand += Uint(1) << (shift - 1); significand >>= shift; return significand; } -static Result parse_float_nan(const char* s, - const char* end, - uint32_t* out_bits) { +// static +template <typename T> +Result FloatParser<T>::ParseNan(const char* s, + const char* end, + Uint* out_bits) { bool is_neg = false; if (*s == '-') { is_neg = true; @@ -198,13 +175,13 @@ static Result parse_float_nan(const char* s, } else if (*s == '+') { s++; } - assert(string_starts_with(s, end, "nan")); + assert(StringStartsWith(s, end, "nan")); s += 3; - uint32_t tag; + Uint tag; if (s != end) { tag = 0; - assert(string_starts_with(s, end, ":0x")); + assert(StringStartsWith(s, end, ":0x")); s += 3; for (; s < end; ++s) { @@ -212,25 +189,27 @@ static Result parse_float_nan(const char* s, if (WABT_FAILED(parse_hexdigit(*s, &digit))) return Result::Error; tag = tag * 16 + digit; - /* check for overflow */ - if (tag > F32_SIG_MASK) + // Check for overflow. + if (tag > Traits::kSigMask) return Result::Error; } - /* NaN cannot have a zero tag, that is reserved for infinity */ + // NaN cannot have a zero tag, that is reserved for infinity. if (tag == 0) return Result::Error; } else { - tag = F32_QUIET_NAN_TAG; + tag = Traits::kQuietNanTag; } - *out_bits = make_float(is_neg, F32_MAX_EXP, tag); + *out_bits = Make(is_neg, Traits::kMaxExp, tag); return Result::Ok; } -static void parse_float_hex(const char* s, - const char* end, - uint32_t* out_bits) { +// static +template <typename T> +void FloatParser<T>::ParseHex(const char* s, const char* end, Uint* out_bits) { + static constexpr int kHexDigitBits = 4; + bool is_neg = false; if (*s == '-') { is_neg = true; @@ -238,23 +217,22 @@ static void parse_float_hex(const char* s, } else if (*s == '+') { s++; } - assert(string_starts_with(s, end, "0x")); + assert(StringStartsWith(s, end, "0x")); s += 2; - /* loop over the significand; everything up to the 'p'. - this code is a bit nasty because we want to support extra zeroes anywhere - without having to use many significand bits. - e.g. - 0x00000001.0p0 => significand = 1, significand_exponent = 0 - 0x10000000.0p0 => significand = 1, significand_exponent = 28 - 0x0.000001p0 => significand = 1, significand_exponent = -24 - */ + // Loop over the significand; everything up to the 'p'. + // This code is a bit nasty because we want to support extra zeroes anywhere + // without having to use many significand bits. + // e.g. + // 0x00000001.0p0 => significand = 1, significand_exponent = 0 + // 0x10000000.0p0 => significand = 1, significand_exponent = 28 + // 0x0.000001p0 => significand = 1, significand_exponent = -24 bool seen_dot = false; - uint32_t significand = 0; - /* how much to shift |significand| if a non-zero value is appended */ + Uint significand = 0; + // How much to shift |significand| if a non-zero value is appended. int significand_shift = 0; - int significand_bits = 0; /* bits of |significand| */ - int significand_exponent = 0; /* exponent adjustment due to dot placement */ + int significand_bits = 0; // Bits of |significand|. + int significand_exponent = 0; // Exponent adjustment due to dot placement. for (; s < end; ++s) { uint32_t digit; if (*s == '.') { @@ -266,17 +244,17 @@ static void parse_float_hex(const char* s, } else if (WABT_FAILED(parse_hexdigit(*s, &digit))) { break; } - significand_shift += HEX_DIGIT_BITS; - if (digit != 0 && (significand == 0 || - significand_bits + significand_shift <= - F32_SIG_BITS + 1 + HEX_DIGIT_BITS)) { + significand_shift += kHexDigitBits; + if (digit != 0 && + (significand == 0 || significand_bits + significand_shift <= + Traits::kSigBits + 1 + kHexDigitBits)) { if (significand != 0) significand <<= significand_shift; if (seen_dot) significand_exponent -= significand_shift; significand += digit; significand_shift = 0; - significand_bits += HEX_DIGIT_BITS; + significand_bits += kHexDigitBits; } } @@ -284,8 +262,8 @@ static void parse_float_hex(const char* s, significand_exponent += significand_shift; if (significand == 0) { - /* 0 or -0 */ - *out_bits = make_float(is_neg, F32_MIN_EXP, 0); + // 0 or -0. + *out_bits = Make(is_neg, Traits::kMinExp, 0); return; } @@ -294,9 +272,9 @@ static void parse_float_hex(const char* s, if (s < end) { assert(*s == 'p'); s++; - /* exponent is always positive, but significand_exponent is signed. - significand_exponent_add is negated if exponent will be negative, so it can - be easily summed to see if the exponent is too large (see below) */ + // Exponent is always positive, but significand_exponent is signed. + // significand_exponent_add is negated if exponent will be negative, so it + // can be easily summed to see if the exponent is too large (see below). int significand_exponent_add = 0; if (*s == '-') { exponent_is_neg = true; @@ -311,7 +289,7 @@ static void parse_float_hex(const char* s, uint32_t digit = (*s - '0'); assert(digit <= 9); exponent = exponent * 10 + digit; - if (exponent + significand_exponent_add >= F32_MAX_EXP) + if (exponent + significand_exponent_add >= Traits::kMaxExp) break; } } @@ -319,56 +297,58 @@ static void parse_float_hex(const char* s, if (exponent_is_neg) exponent = -exponent; - significand_bits = sizeof(uint32_t) * 8 - wabt_clz_u32(significand); - /* -1 for the implicit 1 bit of the significand */ + significand_bits = Traits::kBits - Clz(significand); + // -1 for the implicit 1 bit of the significand. exponent += significand_exponent + significand_bits - 1; - if (exponent >= F32_MAX_EXP) { - /* inf or -inf */ - *out_bits = make_float(is_neg, F32_MAX_EXP, 0); - } else if (exponent <= F32_MIN_EXP) { - /* maybe subnormal */ - if (significand_bits > F32_SIG_BITS) { - significand = shift_float_and_round_to_nearest( - significand, significand_bits - F32_SIG_BITS); - } else if (significand_bits < F32_SIG_BITS) { - significand <<= (F32_SIG_BITS - significand_bits); + if (exponent >= Traits::kMaxExp) { + // inf or -inf. + *out_bits = Make(is_neg, Traits::kMaxExp, 0); + } else if (exponent <= Traits::kMinExp) { + // Maybe subnormal. + if (significand_bits > Traits::kSigBits) { + significand = ShiftAndRoundToNearest(significand, + significand_bits - Traits::kSigBits); + } else if (significand_bits < Traits::kSigBits) { + significand <<= (Traits::kSigBits - significand_bits); } - int shift = F32_MIN_EXP - exponent; - if (shift < F32_SIG_BITS) { + int shift = Traits::kMinExp - exponent; + if (shift < Traits::kSigBits) { if (shift) { significand = - shift_float_and_round_to_nearest(significand, shift) & F32_SIG_MASK; + ShiftAndRoundToNearest(significand, shift) & Traits::kSigMask; } - exponent = F32_MIN_EXP; + exponent = Traits::kMinExp; if (significand != 0) { - *out_bits = make_float(is_neg, exponent, significand); + *out_bits = Make(is_neg, exponent, significand); return; } } - /* not subnormal, too small; return 0 or -0 */ - *out_bits = make_float(is_neg, F32_MIN_EXP, 0); + // Not subnormal, too small; return 0 or -0. + *out_bits = Make(is_neg, Traits::kMinExp, 0); } else { - /* normal value */ - if (significand_bits > F32_SIG_PLUS_ONE_BITS) { - significand = shift_float_and_round_to_nearest( - significand, significand_bits - F32_SIG_PLUS_ONE_BITS); - if (significand > F32_SIG_PLUS_ONE_MASK) + // Normal value. + if (significand_bits > Traits::kSigPlusOneBits) { + significand = ShiftAndRoundToNearest( + significand, significand_bits - Traits::kSigPlusOneBits); + if (significand > Traits::kSigPlusOneMask) exponent++; - } else if (significand_bits < F32_SIG_PLUS_ONE_BITS) { - significand <<= (F32_SIG_PLUS_ONE_BITS - significand_bits); + } else if (significand_bits < Traits::kSigPlusOneBits) { + significand <<= (Traits::kSigPlusOneBits - significand_bits); } - *out_bits = make_float(is_neg, exponent, significand & F32_SIG_MASK); + *out_bits = Make(is_neg, exponent, significand & Traits::kSigMask); } } -static void parse_float_infinity(const char* s, - const char* end, - uint32_t* out_bits) { +// static +template <typename T> +void FloatParser<T>::ParseInfinity(const char* s, + const char* end, + Uint* out_bits) { bool is_neg = false; if (*s == '-') { is_neg = true; @@ -376,17 +356,18 @@ static void parse_float_infinity(const char* s, } else if (*s == '+') { s++; } - assert(string_starts_with(s, end, "inf")); - *out_bits = make_float(is_neg, F32_MAX_EXP, 0); + assert(StringStartsWith(s, end, "inf")); + *out_bits = Make(is_neg, Traits::kMaxExp, 0); } -Result parse_float(LiteralType literal_type, - const char* s, - const char* end, - uint32_t* out_bits) { +// static +template <typename T> +Result FloatParser<T>::Parse(LiteralType literal_type, + const char* s, + const char* end, + Uint* out_bits) { #if COMPILER_IS_MSVC - if (literal_type == LiteralType::Int && string_starts_with(s, end, "0x")) - { + if (literal_type == LiteralType::Int && StringStartsWith(s, end, "0x")) { // Some MSVC crt implementation of strtof doesn't support hex strings literal_type = LiteralType::Hexfloat; } @@ -396,11 +377,11 @@ Result parse_float(LiteralType literal_type, case LiteralType::Float: { errno = 0; char* endptr; - float value; - value = strtof(s, &endptr); - if (endptr != end || - ((value == 0 || value == HUGE_VALF || value == -HUGE_VALF) && - errno != 0)) + Float value; + value = Strto(s, &endptr); + if (endptr != end || ((value == 0 || value == Traits::kHugeVal || + value == -Traits::kHugeVal) && + errno != 0)) return Result::Error; memcpy(out_bits, &value, sizeof(value)); @@ -408,15 +389,15 @@ Result parse_float(LiteralType literal_type, } case LiteralType::Hexfloat: - parse_float_hex(s, end, out_bits); + ParseHex(s, end, out_bits); return Result::Ok; case LiteralType::Infinity: - parse_float_infinity(s, end, out_bits); + ParseInfinity(s, end, out_bits); return Result::Ok; case LiteralType::Nan: - return parse_float_nan(s, end, out_bits); + return ParseNan(s, end, out_bits); default: assert(0); @@ -424,38 +405,41 @@ Result parse_float(LiteralType literal_type, } } -void write_float_hex(char* out, size_t size, uint32_t bits) { - /* 1234567890123456 */ - /* -0x#.######p-### */ - /* -nan:0x###### */ - /* -inf */ - char buffer[WABT_MAX_FLOAT_HEX]; +// static +template <typename T> +void FloatWriter<T>::WriteHex(char* out, size_t size, Uint bits) { + static constexpr int kNumNybbles = Traits::kBits / 4; + static constexpr int kTopNybbleShift = Traits::kBits - 4; + static constexpr Uint kTopNybble = Uint(0xf) << kTopNybbleShift; + static const char s_hex_digits[] = "0123456789abcdef"; + + char buffer[Traits::kMaxHexBufferSize]; char* p = buffer; - bool is_neg = (bits >> F32_SIGN_SHIFT); - int exp = ((bits >> F32_SIG_BITS) & F32_EXP_MASK) - F32_EXP_BIAS; - uint32_t sig = bits & F32_SIG_MASK; + bool is_neg = (bits >> Traits::kSignShift); + int exp = ((bits >> Traits::kSigBits) & Traits::kExpMask) - Traits::kExpBias; + Uint sig = bits & Traits::kSigMask; if (is_neg) *p++ = '-'; - if (exp == F32_MAX_EXP) { - /* infinity or nan */ + if (exp == Traits::kMaxExp) { + // Infinity or nan. if (sig == 0) { strcpy(p, "inf"); p += 3; } else { strcpy(p, "nan"); p += 3; - if (sig != F32_QUIET_NAN_TAG) { + if (sig != Traits::kQuietNanTag) { strcpy(p, ":0x"); p += 3; - /* skip leading zeroes */ - int num_nybbles = sizeof(uint32_t) * 8 / 4; - while ((sig & 0xf0000000) == 0) { + // Skip leading zeroes. + int num_nybbles = kNumNybbles; + while ((sig & kTopNybble) == 0) { sig <<= 4; num_nybbles--; } while (num_nybbles) { - uint32_t nybble = (sig >> (sizeof(uint32_t) * 8 - 4)) & 0xf; + Uint nybble = (sig >> kTopNybbleShift) & 0xf; *p++ = s_hex_digits[nybble]; sig <<= 4; --num_nybbles; @@ -463,19 +447,19 @@ void write_float_hex(char* out, size_t size, uint32_t bits) { } } } else { - bool is_zero = sig == 0 && exp == F32_MIN_EXP; + bool is_zero = sig == 0 && exp == Traits::kMinExp; strcpy(p, "0x"); p += 2; *p++ = is_zero ? '0' : '1'; - /* shift sig up so the top 4-bits are at the top of the uint32 */ - sig <<= sizeof(uint32_t) * 8 - F32_SIG_BITS; + // Shift sig up so the top 4-bits are at the top of the Uint. + sig <<= Traits::kBits - Traits::kSigBits; if (sig) { - if (exp == F32_MIN_EXP) { - /* subnormal; shift the significand up, and shift out the implicit 1 */ - uint32_t leading_zeroes = wabt_clz_u32(sig); - if (leading_zeroes < 31) + if (exp == Traits::kMinExp) { + // Subnormal; shift the significand up, and shift out the implicit 1. + Uint leading_zeroes = Clz(sig); + if (leading_zeroes < Traits::kSignShift) sig <<= leading_zeroes + 1; else sig = 0; @@ -484,7 +468,7 @@ void write_float_hex(char* out, size_t size, uint32_t bits) { *p++ = '.'; while (sig) { - uint32_t nybble = (sig >> (sizeof(uint32_t) * 8 - 4)) & 0xf; + int nybble = (sig >> kTopNybbleShift) & 0xf; *p++ = s_hex_digits[nybble]; sig <<= 4; } @@ -500,8 +484,10 @@ void write_float_hex(char* out, size_t size, uint32_t bits) { } else { *p++ = '+'; } - if (exp >= 100) + if (exp >= 1000) *p++ = '1'; + if (exp >= 100) + *p++ = '0' + (exp / 100) % 10; if (exp >= 10) *p++ = '0' + (exp / 10) % 10; *p++ = '0' + exp % 10; @@ -515,347 +501,132 @@ void write_float_hex(char* out, size_t size, uint32_t bits) { out[len] = '\0'; } -/* doubles */ -static uint64_t make_double(bool sign, int exp, uint64_t sig) { - assert(exp >= F64_MIN_EXP && exp <= F64_MAX_EXP); - assert(sig <= F64_SIG_MASK); - return (static_cast<uint64_t>(sign) << F64_SIGN_SHIFT) | - (static_cast<uint64_t>(exp + F64_EXP_BIAS) << F64_SIG_BITS) | sig; -} - -static uint64_t shift_double_and_round_to_nearest(uint64_t significand, - int shift) { - assert(shift > 0); - /* round ties to even */ - if (significand & (static_cast<uint64_t>(1) << shift)) - significand += static_cast<uint64_t>(1) << (shift - 1); - significand >>= shift; - return significand; -} +} // namespace -static Result parse_double_nan(const char* s, - const char* end, - uint64_t* out_bits) { - bool is_neg = false; - if (*s == '-') { - is_neg = true; - s++; - } else if (*s == '+') { - s++; +Result parse_hexdigit(char c, uint32_t* out) { + if (static_cast<unsigned int>(c - '0') <= 9) { + *out = c - '0'; + return Result::Ok; + } else if (static_cast<unsigned int>(c - 'a') <= 6) { + *out = 10 + (c - 'a'); + return Result::Ok; + } else if (static_cast<unsigned int>(c - 'A') <= 6) { + *out = 10 + (c - 'A'); + return Result::Ok; } - assert(string_starts_with(s, end, "nan")); - s += 3; + return Result::Error; +} - uint64_t tag; - if (s != end) { - tag = 0; - if (!string_starts_with(s, end, ":0x")) +Result parse_uint64(const char* s, const char* end, uint64_t* out) { + if (s == end) + return Result::Error; + uint64_t value = 0; + if (*s == '0' && s + 1 < end && s[1] == 'x') { + s += 2; + if (s == end) return Result::Error; - s += 3; - for (; s < end; ++s) { uint32_t digit; if (WABT_FAILED(parse_hexdigit(*s, &digit))) return Result::Error; - tag = tag * 16 + digit; - /* check for overflow */ - if (tag > F64_SIG_MASK) + uint64_t old_value = value; + value = value * 16 + digit; + // Check for overflow. + if (old_value > value) return Result::Error; } - - /* NaN cannot have a zero tag, that is reserved for infinity */ - if (tag == 0) - return Result::Error; } else { - tag = F64_QUIET_NAN_TAG; + for (; s < end; ++s) { + uint32_t digit = (*s - '0'); + if (digit > 9) + return Result::Error; + uint64_t old_value = value; + value = value * 10 + digit; + // Check for overflow. + if (old_value > value) + return Result::Error; + } } - - *out_bits = make_double(is_neg, F64_MAX_EXP, tag); + if (s != end) + return Result::Error; + *out = value; return Result::Ok; } -static void parse_double_hex(const char* s, - const char* end, - uint64_t* out_bits) { - bool is_neg = false; - if (*s == '-') { - is_neg = true; - s++; - } else if (*s == '+') { +Result parse_int64(const char* s, + const char* end, + uint64_t* out, + ParseIntType parse_type) { + bool has_sign = false; + if (*s == '-' || *s == '+') { + if (parse_type == ParseIntType::UnsignedOnly) + return Result::Error; + if (*s == '-') + has_sign = true; s++; } - assert(string_starts_with(s, end, "0x")); - s += 2; - - /* see the similar comment in parse_float_hex */ - bool seen_dot = false; - uint64_t significand = 0; - /* how much to shift |significand| if a non-zero value is appended */ - int significand_shift = 0; - int significand_bits = 0; /* bits of |significand| */ - int significand_exponent = 0; /* exponent adjustment due to dot placement */ - for (; s < end; ++s) { - uint32_t digit; - if (*s == '.') { - if (significand != 0) - significand_exponent += significand_shift; - significand_shift = 0; - seen_dot = true; - continue; - } else if (WABT_FAILED(parse_hexdigit(*s, &digit))) { - break; - } - significand_shift += HEX_DIGIT_BITS; - if (digit != 0 && (significand == 0 || - significand_bits + significand_shift <= - F64_SIG_BITS + 1 + HEX_DIGIT_BITS)) { - if (significand != 0) - significand <<= significand_shift; - if (seen_dot) - significand_exponent -= significand_shift; - significand += digit; - significand_shift = 0; - significand_bits += HEX_DIGIT_BITS; - } - } - - if (!seen_dot) - significand_exponent += significand_shift; - - if (significand == 0) { - /* 0 or -0 */ - *out_bits = make_double(is_neg, F64_MIN_EXP, 0); - return; + uint64_t value = 0; + Result result = parse_uint64(s, end, &value); + if (has_sign) { + // abs(INT64_MIN) == INT64_MAX + 1. + if (value > static_cast<uint64_t>(INT64_MAX) + 1) + return Result::Error; + value = UINT64_MAX - value + 1; } + *out = value; + return result; +} - int exponent = 0; - bool exponent_is_neg = false; - if (s < end) { - assert(*s == 'p'); +Result parse_int32(const char* s, + const char* end, + uint32_t* out, + ParseIntType parse_type) { + uint64_t value; + bool has_sign = false; + if (*s == '-' || *s == '+') { + if (parse_type == ParseIntType::UnsignedOnly) + return Result::Error; + if (*s == '-') + has_sign = true; s++; - - /* exponent is always positive, but significand_exponent is signed. - significand_exponent_add is negated if exponent will be negative, so it can - be easily summed to see if the exponent is too large (see below) */ - int significand_exponent_add = 0; - if (*s == '-') { - exponent_is_neg = true; - significand_exponent_add = -significand_exponent; - s++; - } else if (*s == '+') { - s++; - significand_exponent_add = significand_exponent; - } - - for (; s < end; ++s) { - uint32_t digit = (*s - '0'); - assert(digit <= 9); - exponent = exponent * 10 + digit; - if (exponent + significand_exponent_add >= F64_MAX_EXP) - break; - } } + if (WABT_FAILED(parse_uint64(s, end, &value))) + return Result::Error; - if (exponent_is_neg) - exponent = -exponent; - - significand_bits = sizeof(uint64_t) * 8 - wabt_clz_u64(significand); - /* -1 for the implicit 1 bit of the significand */ - exponent += significand_exponent + significand_bits - 1; - - if (exponent >= F64_MAX_EXP) { - /* inf or -inf */ - *out_bits = make_double(is_neg, F64_MAX_EXP, 0); - } else if (exponent <= F64_MIN_EXP) { - /* maybe subnormal */ - if (significand_bits > F64_SIG_BITS) { - significand = shift_double_and_round_to_nearest( - significand, significand_bits - F64_SIG_BITS); - } else if (significand_bits < F64_SIG_BITS) { - significand <<= (F64_SIG_BITS - significand_bits); - } - - int shift = F64_MIN_EXP - exponent; - if (shift < F64_SIG_BITS) { - if (shift) { - significand = shift_double_and_round_to_nearest(significand, shift) & - F64_SIG_MASK; - } - exponent = F64_MIN_EXP; - - if (significand != 0) { - *out_bits = make_double(is_neg, exponent, significand); - return; - } - } - - /* not subnormal, too small; return 0 or -0 */ - *out_bits = make_double(is_neg, F64_MIN_EXP, 0); + if (has_sign) { + // abs(INT32_MIN) == INT32_MAX + 1. + if (value > static_cast<uint64_t>(INT32_MAX) + 1) + return Result::Error; + value = UINT32_MAX - value + 1; } else { - /* normal value */ - if (significand_bits > F64_SIG_PLUS_ONE_BITS) { - significand = shift_double_and_round_to_nearest( - significand, significand_bits - F64_SIG_PLUS_ONE_BITS); - if (significand > F64_SIG_PLUS_ONE_MASK) - exponent++; - } else if (significand_bits < F64_SIG_PLUS_ONE_BITS) { - significand <<= (F64_SIG_PLUS_ONE_BITS - significand_bits); - } - - *out_bits = make_double(is_neg, exponent, significand & F64_SIG_MASK); + if (value > static_cast<uint64_t>(UINT32_MAX)) + return Result::Error; } + *out = static_cast<uint32_t>(value); + return Result::Ok; } -static void parse_double_infinity(const char* s, - const char* end, - uint64_t* out_bits) { - bool is_neg = false; - if (*s == '-') { - is_neg = true; - s++; - } else if (*s == '+') { - s++; - } - assert(string_starts_with(s, end, "inf")); - *out_bits = make_double(is_neg, F64_MAX_EXP, 0); + +Result parse_float(LiteralType literal_type, + const char* s, + const char* end, + uint32_t* out_bits) { + return FloatParser<float>::Parse(literal_type, s, end, out_bits); } Result parse_double(LiteralType literal_type, const char* s, const char* end, uint64_t* out_bits) { - -#if COMPILER_IS_MSVC - if (literal_type == LiteralType::Int && string_starts_with(s, end, "0x")) - { - // Some MSVC crt implementation of strtod doesn't support hex strings - literal_type = LiteralType::Hexfloat; - } -#endif - switch (literal_type) { - case LiteralType::Int: - case LiteralType::Float: { - errno = 0; - char* endptr; - double value; - value = strtod(s, &endptr); - if (endptr != end || - ((value == 0 || value == HUGE_VAL || value == -HUGE_VAL) && - errno != 0)) - return Result::Error; - - memcpy(out_bits, &value, sizeof(value)); - return Result::Ok; - } - - case LiteralType::Hexfloat: - parse_double_hex(s, end, out_bits); - return Result::Ok; - - case LiteralType::Infinity: - parse_double_infinity(s, end, out_bits); - return Result::Ok; - - case LiteralType::Nan: - return parse_double_nan(s, end, out_bits); - - default: - assert(0); - return Result::Error; - } + return FloatParser<double>::Parse(literal_type, s, end, out_bits); } -void write_double_hex(char* out, size_t size, uint64_t bits) { - /* 123456789012345678901234 */ - /* -0x#.#############p-#### */ - /* -nan:0x############# */ - /* -inf */ - char buffer[WABT_MAX_DOUBLE_HEX]; - char* p = buffer; - bool is_neg = (bits >> F64_SIGN_SHIFT); - int exp = ((bits >> F64_SIG_BITS) & F64_EXP_MASK) - F64_EXP_BIAS; - uint64_t sig = bits & F64_SIG_MASK; - - if (is_neg) - *p++ = '-'; - if (exp == F64_MAX_EXP) { - /* infinity or nan */ - if (sig == 0) { - strcpy(p, "inf"); - p += 3; - } else { - strcpy(p, "nan"); - p += 3; - if (sig != F64_QUIET_NAN_TAG) { - strcpy(p, ":0x"); - p += 3; - /* skip leading zeroes */ - int num_nybbles = sizeof(uint64_t) * 8 / 4; - while ((sig & 0xf000000000000000ULL) == 0) { - sig <<= 4; - num_nybbles--; - } - while (num_nybbles) { - uint32_t nybble = (sig >> (sizeof(uint64_t) * 8 - 4)) & 0xf; - *p++ = s_hex_digits[nybble]; - sig <<= 4; - --num_nybbles; - } - } - } - } else { - bool is_zero = sig == 0 && exp == F64_MIN_EXP; - strcpy(p, "0x"); - p += 2; - *p++ = is_zero ? '0' : '1'; - - /* shift sig up so the top 4-bits are at the top of the uint32 */ - sig <<= sizeof(uint64_t) * 8 - F64_SIG_BITS; - - if (sig) { - if (exp == F64_MIN_EXP) { - /* subnormal; shift the significand up, and shift out the implicit 1 */ - uint32_t leading_zeroes = wabt_clz_u64(sig); - if (leading_zeroes < 63) - sig <<= leading_zeroes + 1; - else - sig = 0; - exp -= leading_zeroes; - } - - *p++ = '.'; - while (sig) { - uint32_t nybble = (sig >> (sizeof(uint64_t) * 8 - 4)) & 0xf; - *p++ = s_hex_digits[nybble]; - sig <<= 4; - } - } - *p++ = 'p'; - if (is_zero) { - strcpy(p, "+0"); - p += 2; - } else { - if (exp < 0) { - *p++ = '-'; - exp = -exp; - } else { - *p++ = '+'; - } - if (exp >= 1000) - *p++ = '1'; - if (exp >= 100) - *p++ = '0' + (exp / 100) % 10; - if (exp >= 10) - *p++ = '0' + (exp / 10) % 10; - *p++ = '0' + exp % 10; - } - } +void write_float_hex(char* buffer, size_t size, uint32_t bits) { + return FloatWriter<float>::WriteHex(buffer, size, bits); +} - size_t len = p - buffer; - if (len >= size) - len = size - 1; - memcpy(out, buffer, len); - out[len] = '\0'; +void write_double_hex(char* buffer, size_t size, uint64_t bits) { + return FloatWriter<double>::WriteHex(buffer, size, bits); } } // namespace wabt |