Index: src/conversions.cc |
=================================================================== |
--- src/conversions.cc (revision 4210) |
+++ src/conversions.cc (working copy) |
@@ -26,6 +26,7 @@ |
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
#include <stdarg.h> |
+#include <limits.h> |
#include "v8.h" |
@@ -92,74 +93,66 @@ |
} |
-static inline void ReleaseCString(const char* original, const char* str) { |
-} |
+namespace { |
+// C++-style iterator adaptor for StringInputBuffer |
+// (unlike C++ iterators the end-marker has different type). |
+class StringInputBufferIterator { |
+ public: |
+ class EndMarker {}; |
-static inline void ReleaseCString(String* original, const char* str) { |
- DeleteArray(const_cast<char *>(str)); |
-} |
+ explicit StringInputBufferIterator(StringInputBuffer* buffer); |
+ int operator*() const; |
+ void operator++(); |
+ bool operator==(EndMarker const&) const { return end_; } |
+ bool operator!=(EndMarker const& m) const { return !end_; } |
-static inline bool IsSpace(const char* str, int index) { |
- ASSERT(index >= 0 && index < StrLength(str)); |
- return Scanner::kIsWhiteSpace.get(str[index]); |
-} |
+ private: |
+ StringInputBuffer* const buffer_; |
+ int current_; |
+ bool end_; |
+}; |
-static inline bool IsSpace(String* str, int index) { |
- return Scanner::kIsWhiteSpace.get(str->Get(index)); |
+StringInputBufferIterator::StringInputBufferIterator( |
+ StringInputBuffer* buffer) : buffer_(buffer) { |
+ ++(*this); |
} |
+int StringInputBufferIterator::operator*() const { |
+ return current_; |
+} |
-static inline bool SubStringEquals(const char* str, |
- int index, |
- const char* other) { |
- return strncmp(str + index, other, strlen(other)) != 0; |
+ |
+void StringInputBufferIterator::operator++() { |
+ end_ = !buffer_->has_more(); |
+ if (!end_) { |
+ current_ = buffer_->GetNext(); |
+ } |
} |
+} |
-static inline bool SubStringEquals(String* str, int index, const char* other) { |
- HandleScope scope; |
- int str_length = str->length(); |
- int other_length = StrLength(other); |
- int end = index + other_length < str_length ? |
- index + other_length : |
- str_length; |
- Handle<String> substring = |
- Factory::NewSubString(Handle<String>(str), index, end); |
- return substring->IsEqualTo(Vector<const char>(other, other_length)); |
+static inline void ReleaseCString(const char* original, const char* str) { |
} |
-// Check if a string should be parsed as an octal number. The string |
-// can be either a char* or a String*. |
-template<class S> |
-static bool ShouldParseOctal(S* s, int i) { |
- int index = i; |
- int len = GetLength(s); |
- if (index < len && GetChar(s, index) != '0') return false; |
+static inline void ReleaseCString(String* original, const char* str) { |
+ DeleteArray(const_cast<char *>(str)); |
+} |
- // If the first real character (following '0') is not an octal |
- // digit, bail out early. This also takes care of numbers of the |
- // forms 0.xxx and 0exxx by not allowing the first 0 to be |
- // interpreted as an octal. |
- index++; |
- if (index < len) { |
- int d = GetChar(s, index) - '0'; |
- if (d < 0 || d > 7) return false; |
- } else { |
- return false; |
- } |
- // Traverse all digits (including the first). If there is an octal |
- // prefix which is not a part of a longer decimal prefix, we return |
- // true. Otherwise, false is returned. |
- while (index < len) { |
- int d = GetChar(s, index++) - '0'; |
- if (d == 8 || d == 9) return false; |
- if (d < 0 || d > 7) return true; |
+template <class Iterator, class EndMark> |
+static bool SubStringEquals(Iterator* current, |
+ EndMark end, |
+ const char* substring) { |
+ ASSERT(**current == *substring); |
+ for (substring++; *substring != '\0'; substring++) { |
+ ++*current; |
+ if (*current == end || **current != *substring) return false; |
} |
+ ++*current; |
return true; |
} |
@@ -262,95 +255,365 @@ |
static const double JUNK_STRING_VALUE = OS::nan_value(); |
-// Convert a string to a double value. The string can be either a |
-// char* or a String*. |
-template<class S> |
-static double InternalStringToDouble(S* str, |
+// Returns true if a nonspace found and false if the end has reached. |
+template <class Iterator, class EndMark> |
+static inline bool AdvanceToNonspace(Iterator* current, EndMark end) { |
+ while (*current != end) { |
+ if (!Scanner::kIsWhiteSpace.get(**current)) return true; |
+ ++*current; |
+ } |
+ return false; |
+} |
+ |
+ |
+template <class Iterator, class EndMark> |
+static double InternalHexidecimalStringToDouble(Iterator current, |
+ EndMark end, |
+ char* buffer, |
+ bool allow_trailing_junk) { |
+ ASSERT(current != end); |
+ // We reuse the buffer of InternalStringToDouble. Since hexidecimal |
+ // numbers may have much less digits than decimal the buffer won't overflow. |
+ int significant_digits = 0; |
+ int insignificant_digits = 0; |
+ bool leading_zero = false; |
+ // Hexidecomal may have (52) / 4 + 1 significant digit. Mean of 2 |
Erik Corry
2010/03/25 12:26:16
Hexidecomal -> Hexadecimal
digit -> digits
I can't
Florian Loitsch
2010/03/25 13:51:35
Maybe the following explanation is easier to under
|
+ // hexidecimal may have n + 1. |
Erik Corry
2010/03/25 12:26:16
hexidecimal -> hexadecimal
|
+ const int max_significant_digits = (52) / 4 + 2; |
Erik Corry
2010/03/25 12:26:16
I don't see the point in putting 52 in brackets he
|
+ int buffer_pos = 0; |
+ bool nonzero_digit_dropped = false; |
+ |
+ // Skip leading 0s. |
+ while (*current == '0') { |
+ leading_zero = true; |
+ ++current; |
+ if (current == end) return 0; |
+ } |
+ |
+ int begin_pos = buffer_pos; |
+ while ((*current >= '0' && *current <= '9') |
+ || (*current >= 'a' && *current <= 'f') |
+ || (*current >= 'A' && *current <= 'F')) { |
+ if (significant_digits <= max_significant_digits) { |
+ ASSERT(buffer_pos < buffer_size); |
+ buffer[buffer_pos++] = *current; |
+ significant_digits++; |
+ } else { |
+ insignificant_digits++; |
+ nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; |
+ } |
+ ++current; |
+ if (current == end) break; |
+ } |
+ |
+ if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { |
+ return JUNK_STRING_VALUE; |
+ } |
+ |
+ if (significant_digits == 0) { |
+ return leading_zero ? 0 : JUNK_STRING_VALUE; |
+ } |
+ |
+ if (nonzero_digit_dropped) { |
+ ASSERT(insignificant_digits > 0); |
+ insignificant_digits--; |
+ buffer[buffer_pos++] = '1'; |
+ } |
+ |
+ buffer[buffer_pos] = '\0'; |
+ |
+ double result; |
+ StringToInt(buffer, begin_pos, 16, &result); |
+ if (insignificant_digits > 0) { |
+ // Multiplying by power of 2 doesn't cause a loss of precision. |
Erik Corry
2010/03/25 12:26:16
power -> a power
|
+ result *= pow(16.0, insignificant_digits); |
+ } |
+ return result; |
+} |
+ |
+ |
+// Converts a string to a double value. Assumes the Iterator supports |
+// the following operations: |
+// 1. current == end (other ops are not allowed), current != end. |
+// 2. *current - gets the current character in the sequence. |
+// 3. ++current (advances the position). |
+template <class Iterator, class EndMark> |
+static double InternalStringToDouble(Iterator current, |
+ EndMark end, |
int flags, |
double empty_string_val) { |
- double result = 0.0; |
- int index = 0; |
+ // To make sure that iterator unreferencing is valid the following |
Erik Corry
2010/03/25 12:26:16
unreferencing -> dereferencing
|
+ // convention is used: |
+ // 1. Each '++current' statement is followed by check for equality to 'end'. |
+ // 2. If AdvanceToNonspace returned false then current == end. |
+ // 3. If 'current' becomes be equal to 'end' the function returns or goes to |
+ // 'parsing_done'. |
+ // 4. 'current' is not unreferenced after the 'parsing_done' label. |
Erik Corry
2010/03/25 12:26:16
and here
|
+ // 5. Code before 'parsing_done' may rely on 'current != end'. |
+ if (!AdvanceToNonspace(¤t, end)) return empty_string_val; |
- int len = GetLength(str); |
+ const bool allow_trailing_junk = (flags & ALLOW_TRAILING_JUNK) != 0; |
- // Skip leading spaces. |
- while ((index < len) && IsSpace(str, index)) index++; |
+ // Insignificant digits will be removed. |
+ const int max_significant_digits = 772; |
Erik Corry
2010/03/25 12:26:16
Name as a constant. Is it possible to provide a o
|
+ // The longest form of simplified number is: "-<significant digits>'.1eXXX\0". |
+ const int buffer_size = max_significant_digits + 10; |
Erik Corry
2010/03/25 12:26:16
Name as a constant.
|
+ char buffer[buffer_size]; // NOLINT: size is known at compile time. |
+ int buffer_pos = 0; |
- // Is the string empty? |
- if (index >= len) return empty_string_val; |
+ // Exponent will be adjusted if insignificant digits of the integer part |
+ // or insignificant leading zeros of the fractional part are dropped. |
+ int exponent = 0; |
+ int significant_digits = 0; |
+ int insignificant_digits = 0; |
+ bool nonzero_digit_dropped = false; |
- // Get the first character. |
- uint16_t first = GetChar(str, index); |
+ double signed_zero = 0.0; |
- // Numbers can only start with '-', '+', '.', 'I' (Infinity), or a digit. |
- if (first != '-' && first != '+' && first != '.' && first != 'I' && |
- (first > '9' || first < '0')) { |
- return JUNK_STRING_VALUE; |
+ if (*current == '+') { |
+ // Ignore leading sign; Skip following spaces. |
Erik Corry
2010/03/25 12:26:16
Skip -> skip
|
+ ++current; |
+ if (!AdvanceToNonspace(¤t, end)) return JUNK_STRING_VALUE; |
+ } else if (*current == '-') { |
+ buffer[buffer_pos++] = '-'; |
+ ++current; |
+ if (!AdvanceToNonspace(¤t, end)) return JUNK_STRING_VALUE; |
+ signed_zero = -0.0; |
} |
- // Compute sign of result based on first character. |
- int sign = 1; |
- if (first == '-') { |
- sign = -1; |
- index++; |
- // String only containing a '-' are junk chars. |
- if (index == len) return JUNK_STRING_VALUE; |
+ static const char infinity_symbol[] = "Infinity"; |
Erik Corry
2010/03/25 12:26:16
Name as a constant.
|
+ if (*current == infinity_symbol[0]) { |
+ if (!SubStringEquals(¤t, end, infinity_symbol)) { |
+ return JUNK_STRING_VALUE; |
+ } |
+ |
+ if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { |
+ return JUNK_STRING_VALUE; |
+ } |
+ |
+ return (buffer_pos > 0 && buffer[0] == '-') ? -V8_INFINITY : V8_INFINITY; |
} |
- // do we have a hex number? |
- // (since the string is 0-terminated, it's ok to look one char beyond the end) |
- if ((flags & ALLOW_HEX) != 0 && |
- (index + 1) < len && |
- GetChar(str, index) == '0' && |
- (GetChar(str, index + 1) == 'x' || GetChar(str, index + 1) == 'X')) { |
- index += 2; |
- index = StringToInt(str, index, 16, &result); |
- } else if ((flags & ALLOW_OCTALS) != 0 && ShouldParseOctal(str, index)) { |
- // NOTE: We optimistically try to parse the number as an octal (if |
- // we're allowed to), even though this is not as dictated by |
- // ECMA-262. The reason for doing this is compatibility with IE and |
- // Firefox. |
- index = StringToInt(str, index, 8, &result); |
- } else { |
- const char* cstr = GetCString(str, index); |
- const char* end; |
- // Optimistically parse the number and then, if that fails, |
- // check if it might have been {+,-,}Infinity. |
- result = gay_strtod(cstr, &end); |
- ReleaseCString(str, cstr); |
- if (result != 0.0 || end != cstr) { |
- // It appears that strtod worked |
- index += static_cast<int>(end - cstr); |
+ bool leading_zero = false; |
+ if (*current == '0') { |
+ ++current; |
+ if (current == end) return signed_zero; |
+ |
+ leading_zero = true; |
+ |
+ // It could be hexadecimal value. |
+ if ((flags & ALLOW_HEX) && (*current == 'x' || *current == 'X')) { |
+ ++current; |
+ if (current == end) return JUNK_STRING_VALUE; // "0x". |
+ |
+ double result = InternalHexidecimalStringToDouble(current, |
Erik Corry
2010/03/25 12:26:16
Hexi -> Hexa
|
+ end, |
+ buffer + buffer_pos, |
+ allow_trailing_junk); |
+ return (buffer_pos > 0 && buffer[0] == '-') ? -result : result; |
Erik Corry
2010/03/25 12:26:16
Should this be buffer[buffer_pos - 1]?
Florian Loitsch
2010/03/25 13:51:35
lgtm.
|
+ } |
+ |
+ // Ignore leading zeros in the integer part. |
+ while (*current == '0') { |
+ ++current; |
+ if (current == end) return signed_zero; |
+ } |
+ } |
+ |
+ bool octal = leading_zero && (flags & ALLOW_OCTALS) != 0; |
+ |
+ // Copy significant digits of the integer part (if any) to the buffer. |
+ while (*current >= '0' && *current <= '9') { |
+ if (significant_digits < max_significant_digits) { |
+ ASSERT(buffer_pos < buffer_size); |
+ buffer[buffer_pos++] = *current; |
+ significant_digits++; |
+ // Will later check if it's an octal in the buffer. |
} else { |
- // Check for {+,-,}Infinity |
- bool is_negative = (GetChar(str, index) == '-'); |
- if (GetChar(str, index) == '+' || GetChar(str, index) == '-') |
- index++; |
- if (!SubStringEquals(str, index, "Infinity")) |
+ insignificant_digits++; // Move the digit into the exponential part. |
+ nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; |
+ } |
+ octal = octal && *current < '8'; |
+ ++current; |
+ if (current == end) goto parsing_done; |
+ } |
+ |
+ if (*current == '.') { |
+ ASSERT(buffer_pos < buffer_size); |
+ buffer[buffer_pos++] = '.'; |
+ ++current; |
+ if (current == end) { |
+ if (significant_digits == 0 && !leading_zero) { |
return JUNK_STRING_VALUE; |
- result = is_negative ? -V8_INFINITY : V8_INFINITY; |
- index += 8; |
+ } else { |
+ goto parsing_done; |
+ } |
} |
+ |
+ if (significant_digits == 0) { |
+ octal = false; |
+ // Integer part consists of 0 or is absent. Significant digits start after |
+ // leading zeros (if any). |
+ while (*current == '0') { |
+ ++current; |
+ if (current == end) return signed_zero; |
+ exponent--; // Move this 0 into the exponent. |
+ } |
+ } |
+ |
+ // There is the fractional part. |
+ while (*current >= '0' && *current <= '9') { |
+ if (significant_digits < max_significant_digits) { |
+ ASSERT(buffer_pos < buffer_size); |
+ buffer[buffer_pos++] = *current; |
+ significant_digits++; |
+ } else { |
+ // Ignore insignificant digits in the fractional part. |
+ nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; |
+ } |
+ ++current; |
+ if (current == end) goto parsing_done; |
+ } |
} |
- if ((flags & ALLOW_TRAILING_JUNK) == 0) { |
- // skip trailing spaces |
- while ((index < len) && IsSpace(str, index)) index++; |
- // string ending with junk? |
- if (index < len) return JUNK_STRING_VALUE; |
+ if (!leading_zero && exponent == 0 && significant_digits == 0) { |
+ // If leading_zeros is true then the string contains zeros. |
+ // If exponent < 0 then string was [+-]\.0*... |
+ // If significant_digits != 0 the string is not equal to 0. |
+ // Otherwise there is no digits in the string. |
+ return JUNK_STRING_VALUE; |
} |
- return sign * result; |
+ // Parse exponential part. |
+ if (*current == 'e' || *current == 'E') { |
+ if (octal) return JUNK_STRING_VALUE; |
+ ++current; |
+ if (current == end) { |
+ if (allow_trailing_junk) { |
+ goto parsing_done; |
+ } else { |
+ return JUNK_STRING_VALUE; |
+ } |
+ } |
+ char sign = '+'; |
+ if (*current == '+' || *current == '-') { |
+ sign = *current; |
+ ++current; |
+ if (current == end) { |
+ if (allow_trailing_junk) { |
+ goto parsing_done; |
+ } else { |
+ return JUNK_STRING_VALUE; |
+ } |
+ } |
+ } |
+ |
+ if (current == end || *current < '0' || *current > '9') { |
+ if (allow_trailing_junk) { |
+ goto parsing_done; |
+ } else { |
+ return JUNK_STRING_VALUE; |
+ } |
+ } |
+ |
+ const int max_exponent = INT_MAX / 2; |
+ ASSERT(-max_exponent / 2 <= exponent && exponent <= max_exponent / 2); |
+ int num = 0; |
+ do { |
+ // Check overflow. |
+ int digit = *current - '0'; |
+ if (num >= max_exponent / 10 |
+ && !(num == max_exponent / 10 && digit <= max_exponent % 10)) { |
+ num = max_exponent; |
+ } else { |
+ num = num * 10 + digit; |
+ } |
+ ++current; |
+ } while (current != end && *current >= '0' && *current <= '9'); |
+ |
+ exponent += (sign == '-' ? -num : num); |
+ } |
+ |
+ if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { |
+ return JUNK_STRING_VALUE; |
+ } |
+ |
+ parsing_done: |
+ exponent += insignificant_digits; |
+ |
+ if (octal) { |
+ buffer[buffer_pos] = '\0'; |
+ // ALLOW_OCTALS has set and there is no '8' and '9' in insignificant |
Erik Corry
2010/03/25 12:26:16
'8' and '9' -> '8' or '9'
Florian Loitsch
2010/03/25 13:51:35
ALLOW_OCTALS is set ...
|
+ // digits. Check significant digits now. |
+ char sign = '+'; |
+ const char* s = buffer; |
+ if (*s == '-' || *s == '+') sign = *s++; |
+ |
+ double result; |
+ s += StringToInt(s, 0, 8, &result); |
+ if (!allow_trailing_junk && *s != '\0') return JUNK_STRING_VALUE; |
+ |
+ if (sign == '-') result = -result; |
+ if (insignificant_digits > 0) { |
+ result *= pow(8.0, insignificant_digits); |
+ } |
+ return result; |
+ } |
+ |
+ if (nonzero_digit_dropped) { |
+ if (insignificant_digits) buffer[buffer_pos++] = '.'; |
+ buffer[buffer_pos++] = '1'; |
+ } |
+ |
+ if (exponent != 0) { |
+ ASSERT(buffer_pos < buffer_size); |
+ buffer[buffer_pos++] = 'e'; |
+ if (exponent < 0) { |
+ ASSERT(buffer_pos < buffer_size); |
+ buffer[buffer_pos++] = '-'; |
+ exponent = -exponent; |
+ } |
+ if (exponent > 999) exponent = 999; // Result will be Infinity or 0 or -0. |
+ |
+ const int exp_digits = 3; |
+ for (int i = 0; i < exp_digits; i++) { |
+ buffer[buffer_pos + exp_digits - 1 - i] = '0' + exponent % 10; |
+ exponent /= 10; |
+ } |
+ ASSERT(exponent == 0); |
+ buffer_pos += exp_digits; |
+ } |
+ |
+ ASSERT(buffer_pos < buffer_size); |
+ buffer[buffer_pos] = '\0'; |
+ |
+ return gay_strtod(buffer, NULL); |
} |
- |
double StringToDouble(String* str, int flags, double empty_string_val) { |
- return InternalStringToDouble(str, flags, empty_string_val); |
+ StringShape shape(str); |
+ if (shape.IsSequentialAscii()) { |
+ const char* begin = SeqAsciiString::cast(str)->GetChars(); |
+ const char* end = begin + str->length(); |
+ return InternalStringToDouble(begin, end, flags, empty_string_val); |
+ } else if (shape.IsSequentialTwoByte()) { |
+ const uc16* begin = SeqTwoByteString::cast(str)->GetChars(); |
+ const uc16* end = begin + str->length(); |
+ return InternalStringToDouble(begin, end, flags, empty_string_val); |
+ } else { |
+ StringInputBuffer buffer(str); |
+ return InternalStringToDouble(StringInputBufferIterator(&buffer), |
+ StringInputBufferIterator::EndMarker(), |
+ flags, |
+ empty_string_val); |
+ } |
} |
double StringToDouble(const char* str, int flags, double empty_string_val) { |
- return InternalStringToDouble(str, flags, empty_string_val); |
+ const char* end = str + StrLength(str); |
+ |
+ return InternalStringToDouble(str, end, flags, empty_string_val); |
} |