OLD | NEW |
---|---|
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 // Dart core library. | 4 // Dart core library. |
5 | 5 |
6 // VM implementation of int. | 6 // VM implementation of int. |
7 | 7 |
8 patch class int { | 8 patch class int { |
9 | 9 |
10 /* patch */ const factory int.fromEnvironment(String name, | |
11 {int defaultValue}) | |
12 native "Integer_fromEnvironment"; | |
13 | |
10 static bool is64Bit() => 1 << 32 is _Smi; | 14 static bool is64Bit() => 1 << 32 is _Smi; |
11 | 15 |
12 static int _tryParseSmi(String str, int first, int last) { | 16 static int _tryParseSmi(String str, int first, int last) { |
13 assert(first <= last); | 17 assert(first <= last); |
14 var ix = first; | 18 var ix = first; |
15 var sign = 1; | 19 var sign = 1; |
16 var c = str.codeUnitAt(ix); | 20 var c = str.codeUnitAt(ix); |
17 // Check for leading '+' or '-'. | 21 // Check for leading '+' or '-'. |
18 if ((c == 0x2b) || (c == 0x2d)) { | 22 if ((c == 0x2b) || (c == 0x2d)) { |
19 ix++; | 23 ix++; |
(...skipping 10 matching lines...) Expand all Loading... | |
30 for (int i = ix; i <= last; i++) { | 34 for (int i = ix; i <= last; i++) { |
31 var c = 0x30 ^ str.codeUnitAt(i); | 35 var c = 0x30 ^ str.codeUnitAt(i); |
32 if (9 < c) { | 36 if (9 < c) { |
33 return null; | 37 return null; |
34 } | 38 } |
35 result = 10 * result + c; | 39 result = 10 * result + c; |
36 } | 40 } |
37 return sign * result; | 41 return sign * result; |
38 } | 42 } |
39 | 43 |
40 static int _tryParseSmiWhitespace(String str) { | |
41 int first = str._firstNonWhitespace(); | |
42 if (first < str.length) { | |
43 int last = str._lastNonWhitespace(); | |
44 int res = _tryParseSmi(str, first, last); | |
45 if (res != null) return res; | |
46 } | |
47 return _native_parse(str); | |
48 } | |
49 | |
50 static int _parse(String str) { | |
51 int res = _tryParseSmi(str, 0, str.length - 1); | |
52 if (res != null) return res; | |
53 return _tryParseSmiWhitespace(str); | |
54 } | |
55 | |
56 static int _native_parse(String str) native "Integer_parse"; | |
57 | |
58 static int _throwFormatException(String source, int position) { | |
59 throw new FormatException("", source, position); | |
60 } | |
61 | |
62 /* patch */ static int parse(String source, | 44 /* patch */ static int parse(String source, |
63 { int radix, | 45 { int radix, |
64 int onError(String str) }) { | 46 int onError(String str) }) { |
65 if (source == null) throw new ArgumentError(source); | 47 if (source == null) throw new ArgumentError("The source must not be null"); |
66 if (radix == null) { | 48 if (source.isEmpty) return _throwFormatException(onError, source, 0, radix); |
67 int result; | 49 if (radix == null || radix == 10) { |
68 if (source.isNotEmpty) result = _parse(source); | 50 // Try parsing immediately, without trimming whitespace. |
69 if (result == null) { | 51 int result = _tryParseSmi(source, 0, source.length - 1); |
70 if (onError == null) { | 52 if (result != null) return result; |
71 throw new FormatException("", source); | 53 } else if (radix < 2 || radix > 36) { |
72 } | 54 throw new RangeError("Radix $radix not in range 2..36"); |
73 return onError(source); | |
74 } | |
75 return result; | |
76 } | 55 } |
77 return _slowParse(source, radix, onError); | 56 // Split here so improve odds of parse being inlined and the checks omitted. |
57 return _parse(source, radix, onError); | |
78 } | 58 } |
79 | 59 |
80 /* patch */ const factory int.fromEnvironment(String name, | 60 static int _parse(String source, int radix, onError) { |
81 {int defaultValue}) | 61 int end = source._lastNonWhitespace() + 1; |
82 native "Integer_fromEnvironment"; | 62 if (end == 0) { |
63 return _throwFormatException(onError, source, source.length, radix); | |
64 } | |
65 int start = source._firstNonWhitespace(); | |
83 | 66 |
84 static int _slowParse(String source, int radix, int onError(String str)) { | 67 int first = source.codeUnitAt(start); |
85 if (source is! String) throw new ArgumentError(source); | 68 int sign = 1; |
86 if (radix is! int) throw new ArgumentError("Radix is not an integer"); | 69 if (first == 0x2b /* + */ || first == 0x2d /* - */) { |
87 if (radix < 2 || radix > 36) { | 70 sign = 0x2c - first; // -1 if '-', +1 if '+'. |
88 throw new RangeError("Radix $radix not in range 2..36"); | 71 start++; |
72 if (start == end) { | |
73 return _throwFormatException(onError, source, end, radix); | |
74 } | |
89 } | 75 } |
90 // Remove leading and trailing white space. | 76 if (radix == null) { |
91 int start = source._firstNonWhitespace(); | 77 // check for 0x prefix. |
92 int i = start; | 78 int index = start; |
93 if (onError == null) onError = (source) { | 79 first = source.codeUnitAt(index); |
94 throw new FormatException("Invalid radix-$radix number", source, i); | 80 if (first == 0x30 /* 0 */) { |
95 }; | 81 index++; |
96 if (start == source.length) return onError(source); | 82 if (index == end) return 0; |
97 int end = source._lastNonWhitespace() + 1; | 83 first = source.codeUnitAt(index); |
84 if ((first | 0x20) == 0x78 /* x */) { | |
85 index++; | |
86 if (index == end) { | |
87 return _throwFormatException(onError, source, index, null); | |
88 } | |
89 int result = _parseRadix(source, 16, index, end, sign); | |
90 if (result == null) { | |
91 return _throwFormatException(onError, source, null, null); | |
92 } | |
93 return result; | |
94 } | |
95 } | |
96 radix = 10; | |
97 } | |
98 int result = _parseRadix(source, radix, start, end, sign); | |
99 if (result == null) { | |
100 return _throwFormatException(onError, source, null, radix); | |
101 } | |
102 return result; | |
103 } | |
98 | 104 |
99 bool negative = false; | 105 static int _throwFormatException(onError, source, index, radix) { |
106 if (onError != null) return onError(source); | |
107 if (radix == null) { | |
108 throw new FormatException("Invalid number", source, index); | |
109 } | |
110 throw new FormatException("Invalid radix-$radix number", source, index); | |
111 } | |
112 | |
113 static int _parseRadix(String source, int radix, | |
114 int start, int end, int sign) { | |
115 int tableIndex = (radix - 2) * 4 + (int.is64Bit() ? 2 : 0); | |
116 int blockSize = _PARSE_LIMITS[tableIndex]; | |
117 int length = end - start; | |
118 if (length <= blockSize) { | |
119 _Smi smi = _parseBlock(source, radix, start, end); | |
120 if (smi != null) return sign * smi; | |
121 return null; | |
122 } | |
123 int smallBlockSize = length % blockSize; | |
100 int result = 0; | 124 int result = 0; |
125 if (smallBlockSize > 0) { | |
126 int blockEnd = start + smallBlockSize; | |
127 _Smi smi = _parseBlock(source, radix, start, blockEnd); | |
128 if (smi == null) return null; | |
129 result = sign * smi; | |
130 start = blockEnd; | |
131 } | |
132 _Smi multiplier = _PARSE_LIMITS[tableIndex + 1]; | |
133 int blockEnd = start + blockSize; | |
134 do { | |
135 _Smi smi = _parseBlock(source, radix, start, blockEnd); | |
136 if (smi == null) return null; | |
137 result = (result * multiplier) + (sign * smi); | |
Florian Schneider
2014/09/25 11:38:39
Maybe factor out the multiplication with sign out
Lasse Reichstein Nielsen
2014/09/25 14:37:16
This makes all the multiplications Smi operations,
Florian Schneider
2014/09/26 11:57:05
I see - that makes sense.
| |
138 start = blockEnd; | |
139 blockEnd = start + blockSize; | |
140 } while (blockEnd <= end); | |
141 return result; | |
142 } | |
101 | 143 |
102 // The value 99 is used to represent a non-digit. It is too large to be | 144 // Parse block of digits with radix > 10. |
103 // a digit value in any of the used bases. | 145 static _Smi _parseBlock(String source, int radix, int start, int end) { |
104 const NA = 99; | 146 _Smi result = 0; |
105 const List<int> digits = const <int>[ | 147 if (radix <= 10) { |
106 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, NA, NA, NA, NA, NA, NA, // 0x30 | 148 for (int i = start; i < end; i++) { |
107 NA, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40 | 149 int digit = source.codeUnitAt(i) ^ 0x30; |
108 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, NA, NA, NA, NA, NA, // 0x50 | 150 if (digit >= radix) return null; |
109 NA, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x60 | 151 result = radix * result + digit; |
110 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, NA, NA, NA, NA, NA, // 0x70 | 152 } |
111 ]; | 153 } else { |
154 for (int i = start; i < end; i++) { | |
155 int char = source.codeUnitAt(i); | |
156 int digit = char ^ 0x30; | |
Florian Schneider
2014/09/25 11:38:39
Is this computation of the digit actually faster t
Lasse Reichstein Nielsen
2014/09/25 14:37:16
Testing just for hex, it seems to be pretty much t
Florian Schneider
2014/09/26 11:57:05
Acknowledged.
| |
157 if (digit > 9) { | |
158 digit = (char | 0x20) - (0x61 - 10); | |
159 if (digit < 10 || digit >= radix) return null; | |
160 } | |
161 result = radix * result + digit; | |
162 } | |
163 } | |
164 return result; | |
165 } | |
112 | 166 |
113 int code = source.codeUnitAt(i); | 167 // For each radix, 2-36, how many digits are guaranteed to fit in a smi, |
114 if (code == 0x2d || code == 0x2b) { // Starts with a plus or minus-sign. | 168 // and magnitude of such a block (radix ** digit-count). |
115 negative = (code == 0x2d); | 169 // 32-bit limit/multiplier at (radix - 2)*4, 64-bit limit at (radix-2)*4+1 |
116 i++; | 170 static const _PARSE_LIMITS = const [ |
117 if (i == end) return onError(source); | 171 30, 1073741824, 62, 4611686018427387904, /* radix: 2 */ |
118 code = source.codeUnitAt(i); | 172 18, 387420489, 39, 4052555153018976267, |
119 } | 173 15, 1073741824, 30, 1152921504606846976, |
120 do { | 174 12, 244140625, 26, 1490116119384765625, /* radix: 5 */ |
121 if (code < 0x30 || code > 0x7f) return onError(source); | 175 11, 362797056, 23, 789730223053602816, |
122 int digit = digits[code - 0x30]; | 176 10, 282475249, 22, 3909821048582988049, |
123 if (digit >= radix) return onError(source); | 177 10, 1073741824, 20, 1152921504606846976, |
124 result = result * radix + digit; | 178 9, 387420489, 19, 1350851717672992089, |
125 i++; | 179 9, 1000000000, 18, 1000000000000000000, /* radix: 10 */ |
126 if (i == end) break; | 180 8, 214358881, 17, 505447028499293771, |
127 code = source.codeUnitAt(i); | 181 8, 429981696, 17, 2218611106740436992, |
128 } while (true); | 182 8, 815730721, 16, 665416609183179841, |
129 return negative ? -result : result; | 183 7, 105413504, 16, 2177953337809371136, |
130 } | 184 7, 170859375, 15, 437893890380859375, /* radix: 15 */ |
185 7, 268435456, 15, 1152921504606846976, | |
186 7, 410338673, 15, 2862423051509815793, | |
187 7, 612220032, 14, 374813367582081024, | |
188 7, 893871739, 14, 799006685782884121, | |
189 6, 64000000, 14, 1638400000000000000, /* radix: 20 */ | |
190 6, 85766121, 14, 3243919932521508681, | |
191 6, 113379904, 13, 282810057883082752, | |
192 6, 148035889, 13, 504036361936467383, | |
193 6, 191102976, 13, 876488338465357824, | |
194 6, 244140625, 13, 1490116119384765625, /* radix: 25 */ | |
195 6, 308915776, 13, 2481152873203736576, | |
196 6, 387420489, 13, 4052555153018976267, | |
197 6, 481890304, 12, 232218265089212416, | |
198 6, 594823321, 12, 353814783205469041, | |
199 6, 729000000, 12, 531441000000000000, /* radix: 30 */ | |
200 6, 887503681, 12, 787662783788549761, | |
201 6, 1073741824, 12, 1152921504606846976, | |
202 5, 39135393, 12, 1667889514952984961, | |
203 5, 45435424, 12, 2386420683693101056, | |
204 5, 52521875, 12, 3379220508056640625, /* radix: 35 */ | |
205 5, 60466176, 11, 131621703842267136, | |
206 ]; | |
131 } | 207 } |
OLD | NEW |