summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/literal.cc879
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