Index: runtime/lib/integers_patch.dart |
diff --git a/runtime/lib/integers_patch.dart b/runtime/lib/integers_patch.dart |
index f6bc484764435d00c0e5ffd7d4fa7ea2cc07a5b2..d038c0e68ba69a43d04e5eab5cd977ab3337e37b 100644 |
--- a/runtime/lib/integers_patch.dart |
+++ b/runtime/lib/integers_patch.dart |
@@ -7,6 +7,10 @@ |
patch class int { |
+ /* patch */ const factory int.fromEnvironment(String name, |
+ {int defaultValue}) |
+ native "Integer_fromEnvironment"; |
+ |
static bool is64Bit() => 1 << 32 is _Smi; |
static int _tryParseSmi(String str, int first, int last) { |
@@ -37,95 +41,167 @@ patch class int { |
return sign * result; |
} |
- static int _tryParseSmiWhitespace(String str) { |
- int first = str._firstNonWhitespace(); |
- if (first < str.length) { |
- int last = str._lastNonWhitespace(); |
- int res = _tryParseSmi(str, first, last); |
- if (res != null) return res; |
+ /* patch */ static int parse(String source, |
+ { int radix, |
+ int onError(String str) }) { |
+ if (source == null) throw new ArgumentError("The source must not be null"); |
+ if (source.isEmpty) _throwFormatException(onError, source, 0, radix); |
+ if (radix == null || radix == 10) { |
+ // Try parsing immediately, without trimming whitespace. |
+ int result = _tryParseSmi(source, 0, source.length - 1); |
+ if (result != null) return result; |
+ } else if (radix < 2 || radix > 36) { |
+ throw new RangeError("Radix $radix not in range 2..36"); |
} |
- return _native_parse(str); |
- } |
- |
- static int _parse(String str) { |
- int res = _tryParseSmi(str, 0, str.length - 1); |
- if (res != null) return res; |
- return _tryParseSmiWhitespace(str); |
+ // Split here so improve odds of parse being inlined and the checks omitted. |
+ return _parse(source, radix, onError); |
} |
- static int _native_parse(String str) native "Integer_parse"; |
- |
- static int _throwFormatException(String source, int position) { |
- throw new FormatException("", source, position); |
- } |
+ static int _parse(String source, int radix, onError) { |
+ int end = source._lastNonWhitespace() + 1; |
+ if (end == 0) { |
+ return _throwFormatException(onError, source, source.length, radix); |
+ } |
+ int start = source._firstNonWhitespace(); |
- /* patch */ static int parse(String source, |
- { int radix, |
- int onError(String str) }) { |
- if (source == null) throw new ArgumentError(source); |
+ int first = source.codeUnitAt(start); |
+ int sign = 1; |
+ if (first == 0x2b /* + */ || first == 0x2d /* - */) { |
+ sign = 0x2c - first; // -1 if '-', +1 if '+'. |
+ start++; |
+ if (start == end) { |
+ return _throwFormatException(onError, source, end, radix); |
+ } |
+ } |
if (radix == null) { |
- int result; |
- if (source.isNotEmpty) result = _parse(source); |
- if (result == null) { |
- if (onError == null) { |
- throw new FormatException("", source); |
+ // check for 0x prefix. |
+ int index = start; |
+ first = source.codeUnitAt(index); |
+ if (first == 0x30 /* 0 */) { |
+ index++; |
+ if (index == end) return 0; |
+ first = source.codeUnitAt(index); |
+ if ((first | 0x20) == 0x78 /* x */) { |
+ index++; |
+ if (index == end) { |
+ return _throwFormatException(onError, source, index, null); |
+ } |
+ int result = _parseRadix(source, 16, index, end, sign); |
+ if (result == null) { |
+ return _throwFormatException(onError, source, null, null); |
+ } |
+ return result; |
} |
- return onError(source); |
} |
- return result; |
+ radix = 10; |
} |
- return _slowParse(source, radix, onError); |
+ int result = _parseRadix(source, radix, start, end, sign); |
+ if (result == null) { |
+ return _throwFormatException(onError, source, null, radix); |
+ } |
+ return result; |
} |
- /* patch */ const factory int.fromEnvironment(String name, |
- {int defaultValue}) |
- native "Integer_fromEnvironment"; |
- |
- static int _slowParse(String source, int radix, int onError(String str)) { |
- if (source is! String) throw new ArgumentError(source); |
- if (radix is! int) throw new ArgumentError("Radix is not an integer"); |
- if (radix < 2 || radix > 36) { |
- throw new RangeError("Radix $radix not in range 2..36"); |
+ static int _throwFormatException(onError, source, index, radix) { |
+ if (onError != null) return onError(source); |
+ if (radix == null) { |
+ throw new FormatException("Invalid number", source, index); |
} |
- // Remove leading and trailing white space. |
- int start = source._firstNonWhitespace(); |
- int i = start; |
- if (onError == null) onError = (source) { |
- throw new FormatException("Invalid radix-$radix number", source, i); |
- }; |
- if (start == source.length) return onError(source); |
- int end = source._lastNonWhitespace() + 1; |
+ throw new FormatException("Invalid radix-$radix number", source, index); |
+ } |
- bool negative = false; |
+ static int _parseRadix(String source, int radix, |
+ int start, int end, int sign) { |
+ int tableIndex = (radix - 2) * 4 + (int.is64Bit() ? 2 : 0); |
+ int blockSize = _PARSE_LIMITS[tableIndex]; |
+ int length = end - start; |
+ if (length <= blockSize) { |
+ _Smi smi = _parseBlock(source, radix, start, end); |
+ if (smi != null) return sign * smi; |
+ return null; |
+ } |
+ int smallBlockSize = length % blockSize; |
int result = 0; |
- |
- // The value 99 is used to represent a non-digit. It is too large to be |
- // a digit value in any of the used bases. |
- const NA = 99; |
- const List<int> digits = const <int>[ |
- 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, NA, NA, NA, NA, NA, NA, // 0x30 |
- NA, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40 |
- 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, NA, NA, NA, NA, NA, // 0x50 |
- NA, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x60 |
- 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, NA, NA, NA, NA, NA, // 0x70 |
- ]; |
- |
- int code = source.codeUnitAt(i); |
- if (code == 0x2d || code == 0x2b) { // Starts with a plus or minus-sign. |
- negative = (code == 0x2d); |
- i++; |
- if (i == end) return onError(source); |
- code = source.codeUnitAt(i); |
+ if (smallBlockSize > 0) { |
+ int blockEnd = start + smallBlockSize; |
+ _Smi smi = _parseBlock(source, radix, start, blockEnd); |
+ if (smi == null) return null; |
+ result = sign * smi; |
+ start = blockEnd; |
} |
+ _Smi multiplier = _PARSE_LIMITS[tableIndex + 1]; |
+ int blockEnd = start + blockSize; |
do { |
- if (code < 0x30 || code > 0x7f) return onError(source); |
- int digit = digits[code - 0x30]; |
- if (digit >= radix) return onError(source); |
- result = result * radix + digit; |
- i++; |
- if (i == end) break; |
- code = source.codeUnitAt(i); |
- } while (true); |
- return negative ? -result : result; |
+ _Smi smi = _parseBlock(source, radix, start, blockEnd); |
+ if (smi == null) return null; |
+ result = (result * multiplier) + (sign * smi); |
+ start = blockEnd; |
+ blockEnd = start + blockSize; |
+ } while (blockEnd <= end); |
+ return result; |
+ } |
+ |
+ // Parse block of digits with radix > 10. |
+ static _Smi _parseBlock(String source, int radix, int start, int end) { |
+ _Smi result = 0; |
+ if (radix <= 10) { |
+ for (int i = start; i < end; i++) { |
+ int digit = source.codeUnitAt(i) ^ 0x30; |
+ if (digit >= radix) return null; |
+ result = radix * result + digit; |
+ } |
+ } else { |
+ for (int i = start; i < end; i++) { |
+ int char = source.codeUnitAt(i); |
+ int digit = char ^ 0x30; |
+ if (digit > 9) { |
+ digit = (char | 0x20) - (0x61 - 10); |
+ if (digit < 10 || digit >= radix) return null; |
+ } |
+ result = radix * result + digit; |
+ } |
+ } |
+ return result; |
} |
+ |
+ // For each radix, 2-36, how many digits are guaranteed to fit in a smi, |
+ // and magnitude of such a block (radix ** digit-count). |
+ // 32-bit limit/multiplier at (radix - 2)*4, 64-bit limit at (radix-2)*4+1 |
+ static const _PARSE_LIMITS = const [ |
+ 30, 1073741824, 62, 4611686018427387904, /* radix: 2 */ |
+ 18, 387420489, 39, 4052555153018976267, |
+ 15, 1073741824, 30, 1152921504606846976, |
+ 12, 244140625, 26, 1490116119384765625, /* radix: 5 */ |
+ 11, 362797056, 23, 789730223053602816, |
+ 10, 282475249, 22, 3909821048582988049, |
+ 10, 1073741824, 20, 1152921504606846976, |
+ 9, 387420489, 19, 1350851717672992089, |
+ 9, 1000000000, 18, 1000000000000000000, /* radix: 10 */ |
+ 8, 214358881, 17, 505447028499293771, |
+ 8, 429981696, 17, 2218611106740436992, |
+ 8, 815730721, 16, 665416609183179841, |
+ 7, 105413504, 16, 2177953337809371136, |
+ 7, 170859375, 15, 437893890380859375, /* radix: 15 */ |
+ 7, 268435456, 15, 1152921504606846976, |
+ 7, 410338673, 15, 2862423051509815793, |
+ 7, 612220032, 14, 374813367582081024, |
+ 7, 893871739, 14, 799006685782884121, |
+ 6, 64000000, 14, 1638400000000000000, /* radix: 20 */ |
+ 6, 85766121, 14, 3243919932521508681, |
+ 6, 113379904, 13, 282810057883082752, |
+ 6, 148035889, 13, 504036361936467383, |
+ 6, 191102976, 13, 876488338465357824, |
+ 6, 244140625, 13, 1490116119384765625, /* radix: 25 */ |
+ 6, 308915776, 13, 2481152873203736576, |
+ 6, 387420489, 13, 4052555153018976267, |
+ 6, 481890304, 12, 232218265089212416, |
+ 6, 594823321, 12, 353814783205469041, |
+ 6, 729000000, 12, 531441000000000000, /* radix: 30 */ |
+ 6, 887503681, 12, 787662783788549761, |
+ 6, 1073741824, 12, 1152921504606846976, |
+ 5, 39135393, 12, 1667889514952984961, |
+ 5, 45435424, 12, 2386420683693101056, |
+ 5, 52521875, 12, 3379220508056640625, /* radix: 35 */ |
+ 5, 60466176, 11, 131621703842267136, |
+ ]; |
} |