diff --git a/src/literal.cc b/src/literal.cc index 223f1b5c0..3560a4518 100644 --- a/src/literal.cc +++ b/src/literal.cc @@ -534,10 +534,10 @@ Result ParseHexdigit(char c, uint32_t* out) { if (static_cast(c - '0') <= 9) { *out = c - '0'; return Result::Ok; - } else if (static_cast(c - 'a') <= 6) { + } else if (static_cast(c - 'a') < 6) { *out = 10 + (c - 'a'); return Result::Ok; - } else if (static_cast(c - 'A') <= 6) { + } else if (static_cast(c - 'A') < 6) { *out = 10 + (c - 'A'); return Result::Ok; } @@ -554,20 +554,23 @@ Result ParseUint64(const char* s, const char* end, uint64_t* out) { if (s == end) { return Result::Error; } + constexpr uint64_t kMaxDiv16 = UINT64_MAX / 16; + constexpr uint64_t kMaxMod16 = UINT64_MAX % 16; for (; s < end; ++s) { uint32_t digit; if (*s == '_') { continue; } CHECK_RESULT(ParseHexdigit(*s, &digit)); - uint64_t old_value = value; - value = value * 16 + digit; // Check for overflow. - if (old_value > value) { + if (value > kMaxDiv16 || (value == kMaxDiv16 && digit > kMaxMod16)) { return Result::Error; } + value = value * 16 + digit; } } else { + constexpr uint64_t kMaxDiv10 = UINT64_MAX / 10; + constexpr uint64_t kMaxMod10 = UINT64_MAX % 10; for (; s < end; ++s) { if (*s == '_') { continue; @@ -576,12 +579,11 @@ Result ParseUint64(const char* s, const char* end, uint64_t* out) { if (digit > 9) { return Result::Error; } - uint64_t old_value = value; - value = value * 10 + digit; // Check for overflow. - if (old_value > value) { + if (value > kMaxDiv10 || (value == kMaxDiv10 && digit > kMaxMod10)) { return Result::Error; } + value = value * 10 + digit; } } if (s != end) { diff --git a/src/test-literal.cc b/src/test-literal.cc index 7fb7f032e..269470bd3 100644 --- a/src/test-literal.cc +++ b/src/test-literal.cc @@ -26,34 +26,261 @@ using namespace wabt; namespace { +enum ParseIntTypeCombo { + UnsignedOnly, + SignedAndUnsigned, + Both, +}; + +template +void AssertIntEquals(T expected, + const char* s, + F&& parse_int, + ParseIntTypeCombo parse_type = Both) { + const char* const end = s + strlen(s); + T actual; + if (parse_type == UnsignedOnly || parse_type == Both) { + ASSERT_EQ(Result::Ok, + parse_int(s, end, &actual, ParseIntType::UnsignedOnly)) + << s; + ASSERT_EQ(expected, actual); + } else { + ASSERT_EQ(Result::Error, + parse_int(s, end, &actual, ParseIntType::UnsignedOnly)) + << s; + } + + if (parse_type == SignedAndUnsigned || parse_type == Both) { + ASSERT_EQ(Result::Ok, + parse_int(s, end, &actual, ParseIntType::SignedAndUnsigned)) + << s; + ASSERT_EQ(expected, actual); + } else { + ASSERT_EQ(Result::Error, + parse_int(s, end, &actual, ParseIntType::SignedAndUnsigned)) + << s; + } +} + +void AssertInt32Equals(uint32_t expected, + const char* s, + ParseIntTypeCombo parse_type = Both) { + AssertIntEquals(expected, s, ParseInt32, parse_type); +} + +void AssertInt64Equals(uint64_t expected, + const char* s, + ParseIntTypeCombo parse_type = Both) { + AssertIntEquals(expected, s, ParseInt64, parse_type); +} + +void AssertInt32Fails(const char* s) { + const char* const end = s + strlen(s); + uint32_t actual; + ASSERT_EQ(Result::Error, + ParseInt32(s, end, &actual, ParseIntType::SignedAndUnsigned)) + << s; + ASSERT_EQ(Result::Error, + ParseInt32(s, end, &actual, ParseIntType::UnsignedOnly)) + << s; +} + +void AssertInt64Fails(const char* s) { + const char* const end = s + strlen(s); + uint64_t actual; + ASSERT_EQ(Result::Error, + ParseInt64(s, end, &actual, ParseIntType::SignedAndUnsigned)) + << s; + ASSERT_EQ(Result::Error, + ParseInt64(s, end, &actual, ParseIntType::UnsignedOnly)) + << s; +} + +void AssertUint64Equals(uint64_t expected, const char* s) { + uint64_t actual; + ASSERT_EQ(Result::Ok, ParseUint64(s, s + strlen(s), &actual)) << s; + ASSERT_EQ(expected, actual); +} + +void AssertUint64Fails(const char* s) { + uint64_t actual_bits; + ASSERT_EQ(Result::Error, ParseUint64(s, s + strlen(s), &actual_bits)) << s; +} + void AssertHexFloatEquals(uint32_t expected_bits, const char* s) { uint32_t actual_bits; ASSERT_EQ(Result::Ok, - ParseFloat(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits)); + ParseFloat(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits)) + << s; ASSERT_EQ(expected_bits, actual_bits); } void AssertHexFloatFails(const char* s) { uint32_t actual_bits; ASSERT_EQ(Result::Error, - ParseFloat(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits)); + ParseFloat(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits)) + << s; } void AssertHexDoubleEquals(uint64_t expected_bits, const char* s) { uint64_t actual_bits; ASSERT_EQ(Result::Ok, - ParseDouble(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits)); + ParseDouble(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits)) + << s; ASSERT_EQ(expected_bits, actual_bits); } void AssertHexDoubleFails(const char* s) { uint64_t actual_bits; ASSERT_EQ(Result::Error, - ParseDouble(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits)); + ParseDouble(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits)) + << s; } } // end anonymous namespace +TEST(ParseInt32, Both) { + AssertInt32Equals(0, "0"); + AssertInt32Equals(1000, "1000"); + AssertInt32Equals(123456789, "123456789"); + AssertInt32Equals(2147483647, "2147483647"); + AssertInt32Equals(4294967295u, "4294967295"); + AssertInt32Equals(0xcafef00du, "0xcafef00d"); + AssertInt32Equals(0x7fffffff, "0x7fffffff"); + AssertInt32Equals(0x80000000u, "0x80000000"); + AssertInt32Equals(0xffffffffu, "0xffffffff"); +} + +TEST(ParseInt32, SignedAndUnsigned) { + AssertInt32Equals(2147483648, "-2147483648", SignedAndUnsigned); + AssertInt32Equals(-0x80000000u, "-0x80000000", SignedAndUnsigned); + AssertInt32Equals(4294967295u, "-1", SignedAndUnsigned); + AssertInt32Equals(-1, "-0x1", SignedAndUnsigned); + AssertInt32Equals(1, "+1", SignedAndUnsigned); + AssertInt32Equals(-0xabcd, "-0xABCD", SignedAndUnsigned); + AssertInt32Equals(0xabcd, "+0xabcd", SignedAndUnsigned); +} + +TEST(ParseInt32, Invalid) { + AssertInt32Fails(""); + AssertInt32Fails("-100hello"); + AssertInt32Fails("0XABCDEF"); + AssertInt32Fails("0xgabba"); + AssertInt32Fails("two"); +} + +TEST(ParseInt32, Underscores) { + AssertInt32Equals(123456789, "12_345_6789", Both); + AssertInt32Equals(123456789, "+12_345_6789", SignedAndUnsigned); + AssertInt32Equals(-123456789, "-12345_6789", SignedAndUnsigned); + AssertInt32Equals(19, "1______9", Both); + AssertInt32Equals(0xabcd, "0xa_b_c_d", Both); + AssertInt32Equals(0xabcd, "+0xa_b_c_d", SignedAndUnsigned); + AssertInt32Equals(-0xabcd, "-0xa_b_c_d", SignedAndUnsigned); +} + +TEST(ParseInt32, Overflow) { + AssertInt32Fails("4294967296"); + AssertInt32Fails("-2147483649"); + AssertInt32Fails("0x100000000"); + AssertInt32Fails("-0x80000001"); + AssertInt32Fails("1231231231231231231231"); +} + +TEST(ParseInt64, Both) { + AssertInt64Equals(0, "0"); + AssertInt64Equals(1000, "1000"); + AssertInt64Equals(123456789, "123456789"); + AssertInt64Equals(9223372036854775807ull, "9223372036854775807"); + AssertInt64Equals(18446744073709551615ull, "18446744073709551615"); + AssertInt64Equals(0x7fffffffffffffffull, "0x7fffffffffffffff"); + AssertInt64Equals(0x8000000000000000ull, "0x8000000000000000"); + AssertInt64Equals(0xffffffffffffffffull, "0xffffffffffffffff"); +} + +TEST(ParseInt64, SignedAndUnsigned) { + AssertInt64Equals(9223372036854775808ull, "-9223372036854775808", + SignedAndUnsigned); + AssertInt64Equals(18446744073709551615ull, "-1", SignedAndUnsigned); + AssertInt64Equals(-1, "-0x1", SignedAndUnsigned); + AssertInt64Equals(1, "+1", SignedAndUnsigned); + AssertInt64Equals(-0x0bcdefabcdefabcdull, "-0x0BCDEFABCDEFABCD", + SignedAndUnsigned); + AssertInt64Equals(0xabcdefabcdefabcdull, "+0xabcdefabcdefabcd", + SignedAndUnsigned); +} + +TEST(ParseInt64, Invalid) { + AssertInt64Fails(""); + AssertInt64Fails("-100hello"); + AssertInt64Fails("0XABCDEF"); + AssertInt64Fails("0xgabba"); + AssertInt64Fails("two"); +} + +TEST(ParseInt64, Underscores) { + AssertInt64Equals(123456789, "12_345_6789", Both); + AssertInt64Equals(123456789, "+12_345_6789", SignedAndUnsigned); + AssertInt64Equals(-123456789, "-12345_6789", SignedAndUnsigned); + AssertInt64Equals(19, "1______9", Both); + AssertInt64Equals(0xabcd, "0xa_b_c_d", Both); + AssertInt64Equals(0xabcd, "+0xa_b_c_d", SignedAndUnsigned); + AssertInt64Equals(-0xabcd, "-0xa_b_c_d", SignedAndUnsigned); +} + +TEST(ParseInt64, Overflow) { + AssertInt64Fails("18446744073709551616"); + AssertInt64Fails("-9223372036854775809"); + AssertInt32Fails("0x10000000000000000"); + AssertInt32Fails("-0x80000000000000001"); + AssertInt64Fails("1231231231231231231231"); +} + +TEST(ParseUint64, Basic) { + AssertUint64Equals(0, "0"); + AssertUint64Equals(1000, "1000"); + AssertUint64Equals(123456789, "123456789"); + AssertUint64Equals(1844674407370955161ull, "1844674407370955161"); + AssertUint64Equals(18446744073709551615ull, "18446744073709551615"); + + AssertUint64Equals(0, "0x0"); + AssertUint64Equals(0x1000, "0x1000"); + AssertUint64Equals(0x123456789, "0x123456789"); + AssertUint64Equals(0xabcdef, "0xabcdef"); + AssertUint64Equals(0xffffffffffffffull, "0xffffffffffffff"); + AssertUint64Equals(0xfffffffffffffffull, "0xfffffffffffffff"); + + AssertUint64Equals(0xabcdefabcdefabcdull, "0xabcdefabcdefabcd"); +} + +TEST(ParseUint64, NoOctal) { + AssertUint64Equals(100, "0100"); + AssertUint64Equals(888, "0000888"); +} + +TEST(ParseUint64, Invalid) { + AssertUint64Fails(""); + AssertUint64Fails("-100"); + AssertUint64Fails("0XABCDEF"); + AssertUint64Fails("0xgabba"); + AssertUint64Fails("two"); +} + +TEST(ParseUint64, Underscores) { + AssertUint64Equals(123456789, "12_345_6789"); + AssertUint64Equals(19, "1______9"); + AssertUint64Equals(0xabcd, "0xa_b_c_d"); +} + +TEST(ParseUint64, Overflow) { + AssertUint64Fails("0x10000000000000000"); + AssertUint64Fails("18446744073709551616"); + AssertUint64Fails("62857453058642199420"); + AssertUint64Fails("82000999361882825820"); + AssertUint64Fails("126539114687237086210"); + AssertUint64Fails("10000000000000000000000000000000000000000"); +} + TEST(ParseFloat, NonCanonical) { AssertHexFloatEquals(0x3f800000, "0x00000000000000000000001.0p0"); AssertHexFloatEquals(0x3f800000, "0x1.00000000000000000000000p0");