diff --git a/double-conversion/string-to-double.cc b/double-conversion/string-to-double.cc index d92fd961..c3309333 100644 --- a/double-conversion/string-to-double.cc +++ b/double-conversion/string-to-double.cc @@ -51,13 +51,25 @@ namespace double_conversion { namespace { -inline char ToLower(char ch) { +// Widens an input character to its unsigned code-unit value. Symbol matching +// compares characters in this form, so that a 16-bit input character is never +// truncated into the range of the symbol byte it is compared against. +inline uint32_t CodeUnit(char ch) { + return static_cast(ch); +} + +inline uint32_t CodeUnit(uc16 ch) { + return ch; +} + +inline uint32_t ToLower(uint32_t ch) { + if (ch > 0x7F) return ch; static const std::ctype& cType = std::use_facet >(std::locale::classic()); - return cType.tolower(ch); + return static_cast(cType.tolower(static_cast(ch))); } -inline char Pass(char ch) { +inline uint32_t Pass(uint32_t ch) { return ch; } @@ -66,10 +78,11 @@ static inline bool ConsumeSubStringImpl(Iterator* current, Iterator end, const char* substring, Converter converter) { - DOUBLE_CONVERSION_ASSERT(converter(**current) == *substring); + DOUBLE_CONVERSION_ASSERT(converter(CodeUnit(**current)) == CodeUnit(*substring)); for (substring++; *substring != '\0'; substring++) { ++*current; - if (*current == end || converter(**current) != *substring) { + if (*current == end || + converter(CodeUnit(**current)) != CodeUnit(*substring)) { return false; } } @@ -92,10 +105,13 @@ static bool ConsumeSubString(Iterator* current, } // Consumes first character of the str is equal to ch -inline bool ConsumeFirstCharacter(char ch, +template +inline bool ConsumeFirstCharacter(Char ch, const char* str, bool case_insensitivity) { - return case_insensitivity ? ToLower(ch) == str[0] : ch == str[0]; + const uint32_t c = CodeUnit(ch); + const uint32_t first = CodeUnit(str[0]); + return case_insensitivity ? ToLower(c) == first : c == first; } } // namespace diff --git a/test/cctest/test-conversions.cc b/test/cctest/test-conversions.cc index 99462492..12ce8bfb 100644 --- a/test/cctest/test-conversions.cc +++ b/test/cctest/test-conversions.cc @@ -6002,6 +6002,60 @@ TEST(StringToDoubleCaseInsensitiveSpecialValues) { } +TEST(StringToDoubleNonAsciiSpecialValues) { + int processed = 0; + + // Use 1.0 as junk_string_value. + StringToDoubleConverter converter(StringToDoubleConverter::NO_FLAGS, + 0.0, 1.0, "Infinity", "NaN"); + + // The ASCII spellings are the only ones that match. + const uc16 nan16[] = { 'N', 'a', 'N' }; + CHECK_EQ(Double::NaN(), converter.StringToDouble(nan16, 3, &processed)); + CHECK_EQ(3, processed); + + const uc16 infinity16[] = { 'I', 'n', 'f', 'i', 'n', 'i', 't', 'y' }; + CHECK_EQ(Double::Infinity(), + converter.StringToDouble(infinity16, 8, &processed)); + CHECK_EQ(8, processed); + + // Characters whose low byte equals a symbol character must not match it. + // U+014E and U+FF4E end in 'N', U+0161 ends in 'a', U+FF49 ends in 'I' and + // U+016E ends in 'n'. + const uc16 nan_lead[] = { 0x014E, 'a', 'N' }; + CHECK_EQ(1.0, converter.StringToDouble(nan_lead, 3, &processed)); + CHECK_EQ(0, processed); + + const uc16 nan_tail[] = { 'N', 0x0161, 'N' }; + CHECK_EQ(1.0, converter.StringToDouble(nan_tail, 3, &processed)); + CHECK_EQ(0, processed); + + const uc16 infinity_lead[] = { 0xFF49, 'n', 'f', 'i', 'n', 'i', 't', 'y' }; + CHECK_EQ(1.0, converter.StringToDouble(infinity_lead, 8, &processed)); + CHECK_EQ(0, processed); + + const uc16 infinity_tail[] = { 'I', 0x016E, 'f', 'i', 'n', 'i', 't', 'y' }; + CHECK_EQ(1.0, converter.StringToDouble(infinity_tail, 8, &processed)); + CHECK_EQ(0, processed); + + // The same holds when the symbols are matched case-insensitively. + const int ci_flags = StringToDoubleConverter::ALLOW_CASE_INSENSITIVITY; + StringToDoubleConverter ci_converter(ci_flags, 0.0, 1.0, "infinity", "nan"); + + const uc16 nan_ci[] = { 'n', 'a', 'N' }; + CHECK_EQ(Double::NaN(), ci_converter.StringToDouble(nan_ci, 3, &processed)); + CHECK_EQ(3, processed); + + const uc16 nan_ci_lead[] = { 0x014E, 'a', 'n' }; + CHECK_EQ(1.0, ci_converter.StringToDouble(nan_ci_lead, 3, &processed)); + CHECK_EQ(0, processed); + + const uc16 nan_ci_tail[] = { 'n', 'a', 0x014E }; + CHECK_EQ(1.0, ci_converter.StringToDouble(nan_ci_tail, 3, &processed)); + CHECK_EQ(0, processed); +} + + TEST(StringToTemplate) { // Test StringToDoubleConverter::StringTo.