| 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 | 4 |
| 5 const int _maxAscii = 0x7f; | 5 const int _maxAscii = 0x7f; |
| 6 const int _maxLatin1 = 0xff; | 6 const int _maxLatin1 = 0xff; |
| 7 const int _maxUtf16 = 0xffff; | 7 const int _maxUtf16 = 0xffff; |
| 8 const int _maxUnicode = 0x10ffff; | 8 const int _maxUnicode = 0x10ffff; |
| 9 | 9 |
| 10 @patch | 10 @patch class String { |
| 11 class String { | 11 @patch factory String.fromCharCodes(Iterable<int> charCodes, |
| 12 @patch | 12 [int start = 0, int end]) { |
| 13 factory String.fromCharCodes(Iterable<int> charCodes, | 13 if (charCodes is! Iterable) throw new ArgumentError.value(charCodes, "charCo
des"); |
| 14 [int start = 0, int end]) { | |
| 15 if (charCodes is! Iterable) | |
| 16 throw new ArgumentError.value(charCodes, "charCodes"); | |
| 17 if (start is! int) throw new ArgumentError.value(start, "start"); | 14 if (start is! int) throw new ArgumentError.value(start, "start"); |
| 18 if (end != null && end is! int) throw new ArgumentError.value(end, "end"); | 15 if (end != null && end is! int) throw new ArgumentError.value(end, "end"); |
| 19 return _StringBase.createFromCharCodes(charCodes, start, end, null); | 16 return _StringBase.createFromCharCodes(charCodes, start, end, null); |
| 20 } | 17 } |
| 21 | 18 |
| 22 @patch | 19 @patch factory String.fromCharCode(int charCode) { |
| 23 factory String.fromCharCode(int charCode) { | |
| 24 if (charCode >= 0) { | 20 if (charCode >= 0) { |
| 25 if (charCode <= 0xff) { | 21 if (charCode <= 0xff) { |
| 26 return _OneByteString._allocate(1).._setAt(0, charCode); | 22 return _OneByteString._allocate(1).._setAt(0, charCode); |
| 27 } | 23 } |
| 28 if (charCode <= 0xffff) { | 24 if (charCode <= 0xffff) { |
| 29 return _StringBase._createFromCodePoints( | 25 return _StringBase._createFromCodePoints(new _List(1)..[0] = charCode, |
| 30 new _List(1)..[0] = charCode, 0, 1); | 26 0, 1); |
| 31 } | 27 } |
| 32 if (charCode <= 0x10ffff) { | 28 if (charCode <= 0x10ffff) { |
| 33 var low = 0xDC00 | (charCode & 0x3ff); | 29 var low = 0xDC00 | (charCode & 0x3ff); |
| 34 int bits = charCode - 0x10000; | 30 int bits = charCode - 0x10000; |
| 35 var high = 0xD800 | (bits >> 10); | 31 var high = 0xD800 | (bits >> 10); |
| 36 return _StringBase._createFromCodePoints( | 32 return _StringBase._createFromCodePoints(new _List(2)..[0] = high |
| 37 new _List(2) | 33 ..[1] = low, |
| 38 ..[0] = high | 34 0, 2); |
| 39 ..[1] = low, | |
| 40 0, | |
| 41 2); | |
| 42 } | 35 } |
| 43 } | 36 } |
| 44 throw new RangeError.range(charCode, 0, 0x10ffff); | 37 throw new RangeError.range(charCode, 0, 0x10ffff); |
| 45 } | 38 } |
| 46 | 39 |
| 47 @patch | 40 @patch const factory String.fromEnvironment(String name, |
| 48 const factory String.fromEnvironment(String name, {String defaultValue}) | 41 {String defaultValue}) |
| 49 native "String_fromEnvironment"; | 42 native "String_fromEnvironment"; |
| 50 } | 43 } |
| 51 | 44 |
| 45 |
| 52 /** | 46 /** |
| 53 * [_StringBase] contains common methods used by concrete String | 47 * [_StringBase] contains common methods used by concrete String |
| 54 * implementations, e.g., _OneByteString. | 48 * implementations, e.g., _OneByteString. |
| 55 */ | 49 */ |
| 56 abstract class _StringBase { | 50 abstract class _StringBase { |
| 57 // Constants used by replaceAll encoding of string slices between matches. | 51 // Constants used by replaceAll encoding of string slices between matches. |
| 58 // A string slice (start+length) is encoded in a single Smi to save memory | 52 // A string slice (start+length) is encoded in a single Smi to save memory |
| 59 // overhead in the common case. | 53 // overhead in the common case. |
| 60 // We use fewer bits for length (11 bits) than for the start index (19+ bits). | 54 // We use fewer bits for length (11 bits) than for the start index (19+ bits). |
| 61 // For long strings, it's possible to have many large indices, | 55 // For long strings, it's possible to have many large indices, |
| (...skipping 16 matching lines...) Expand all Loading... |
| 78 // We pick 30 as a safe lower bound on available bits in a negative smi. | 72 // We pick 30 as a safe lower bound on available bits in a negative smi. |
| 79 // TODO(lrn): Consider allowing more bits for start on 64-bit systems. | 73 // TODO(lrn): Consider allowing more bits for start on 64-bit systems. |
| 80 static const int _maxUnsignedSmiBits = 30; | 74 static const int _maxUnsignedSmiBits = 30; |
| 81 | 75 |
| 82 // For longer strings, calling into C++ to create the result of a | 76 // For longer strings, calling into C++ to create the result of a |
| 83 // [replaceAll] is faster than [_joinReplaceAllOneByteResult]. | 77 // [replaceAll] is faster than [_joinReplaceAllOneByteResult]. |
| 84 // TODO(lrn): See if this limit can be tweaked. | 78 // TODO(lrn): See if this limit can be tweaked. |
| 85 static const int _maxJoinReplaceOneByteStringLength = 500; | 79 static const int _maxJoinReplaceOneByteStringLength = 500; |
| 86 | 80 |
| 87 factory _StringBase._uninstantiable() { | 81 factory _StringBase._uninstantiable() { |
| 88 throw new UnsupportedError("_StringBase can't be instaniated"); | 82 throw new UnsupportedError( |
| 83 "_StringBase can't be instaniated"); |
| 89 } | 84 } |
| 90 | 85 |
| 91 int get hashCode native "String_getHashCode"; | 86 int get hashCode native "String_getHashCode"; |
| 92 | 87 |
| 93 bool get _isOneByte { | 88 bool get _isOneByte { |
| 94 // Alternatively return false and override it on one-byte string classes. | 89 // Alternatively return false and override it on one-byte string classes. |
| 95 int id = ClassID.getID(this); | 90 int id = ClassID.getID(this); |
| 96 return id == ClassID.cidOneByteString || | 91 return id == ClassID.cidOneByteString || |
| 97 id == ClassID.cidExternalOneByteString; | 92 id == ClassID.cidExternalOneByteString; |
| 98 } | 93 } |
| 99 | 94 |
| 100 /** | 95 /** |
| 101 * Create the most efficient string representation for specified | 96 * Create the most efficient string representation for specified |
| 102 * [charCodes]. | 97 * [charCodes]. |
| 103 * | 98 * |
| 104 * Only uses the character codes betwen index [start] and index [end] of | 99 * Only uses the character codes betwen index [start] and index [end] of |
| 105 * `charCodes`. They must satisfy `0 <= start <= end <= charCodes.length`. | 100 * `charCodes`. They must satisfy `0 <= start <= end <= charCodes.length`. |
| 106 * | 101 * |
| 107 * The [limit] is an upper limit on the character codes in the iterable. | 102 * The [limit] is an upper limit on the character codes in the iterable. |
| 108 * It's `null` if unknown. | 103 * It's `null` if unknown. |
| 109 */ | 104 */ |
| 110 static String createFromCharCodes( | 105 static String createFromCharCodes(Iterable<int> charCodes, |
| 111 Iterable<int> charCodes, int start, int end, int limit) { | 106 int start, int end, |
| 107 int limit) { |
| 112 if (start == null) throw new ArgumentError.notNull("start"); | 108 if (start == null) throw new ArgumentError.notNull("start"); |
| 113 if (charCodes == null) throw new ArgumentError(charCodes); | 109 if (charCodes == null) throw new ArgumentError(charCodes); |
| 114 // TODO(srdjan): Also skip copying of wide typed arrays. | 110 // TODO(srdjan): Also skip copying of wide typed arrays. |
| 115 final ccid = ClassID.getID(charCodes); | 111 final ccid = ClassID.getID(charCodes); |
| 116 bool isOneByteString = false; | 112 bool isOneByteString = false; |
| 117 if ((ccid != ClassID.cidArray) && | 113 if ((ccid != ClassID.cidArray) && |
| 118 (ccid != ClassID.cidGrowableObjectArray) && | 114 (ccid != ClassID.cidGrowableObjectArray) && |
| 119 (ccid != ClassID.cidImmutableArray)) { | 115 (ccid != ClassID.cidImmutableArray)) { |
| 120 if (charCodes is Uint8List) { | 116 if (charCodes is Uint8List) { |
| 121 end = RangeError.checkValidRange(start, end, charCodes.length); | 117 end = RangeError.checkValidRange(start, end, charCodes.length); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 149 static int _scanCodeUnits(List<int> charCodes, int start, int end) { | 145 static int _scanCodeUnits(List<int> charCodes, int start, int end) { |
| 150 int bits = 0; | 146 int bits = 0; |
| 151 for (int i = start; i < end; i++) { | 147 for (int i = start; i < end; i++) { |
| 152 int code = charCodes[i]; | 148 int code = charCodes[i]; |
| 153 if (code is! _Smi) throw new ArgumentError(charCodes); | 149 if (code is! _Smi) throw new ArgumentError(charCodes); |
| 154 bits |= code; | 150 bits |= code; |
| 155 } | 151 } |
| 156 return bits; | 152 return bits; |
| 157 } | 153 } |
| 158 | 154 |
| 159 static String _createStringFromIterable( | 155 static String _createStringFromIterable(Iterable<int> charCodes, |
| 160 Iterable<int> charCodes, int start, int end) { | 156 int start, int end) { |
| 161 // Treat charCodes as Iterable. | 157 // Treat charCodes as Iterable. |
| 162 if (charCodes is EfficientLengthIterable) { | 158 if (charCodes is EfficientLengthIterable) { |
| 163 int length = charCodes.length; | 159 int length = charCodes.length; |
| 164 end = RangeError.checkValidRange(start, end, length); | 160 end = RangeError.checkValidRange(start, end, length); |
| 165 List charCodeList = | 161 List charCodeList = new List.from(charCodes.take(end).skip(start), |
| 166 new List.from(charCodes.take(end).skip(start), growable: false); | 162 growable: false); |
| 167 return createFromCharCodes(charCodeList, 0, charCodeList.length, null); | 163 return createFromCharCodes(charCodeList, 0, charCodeList.length, null); |
| 168 } | 164 } |
| 169 // Don't know length of iterable, so iterate and see if all the values | 165 // Don't know length of iterable, so iterate and see if all the values |
| 170 // are there. | 166 // are there. |
| 171 if (start < 0) throw new RangeError.range(start, 0, charCodes.length); | 167 if (start < 0) throw new RangeError.range(start, 0, charCodes.length); |
| 172 var it = charCodes.iterator; | 168 var it = charCodes.iterator; |
| 173 for (int i = 0; i < start; i++) { | 169 for (int i = 0; i < start; i++) { |
| 174 if (!it.moveNext()) { | 170 if (!it.moveNext()) { |
| 175 throw new RangeError.range(start, 0, i); | 171 throw new RangeError.range(start, 0, i); |
| 176 } | 172 } |
| 177 } | 173 } |
| 178 List charCodeList; | 174 List charCodeList; |
| 179 int bits = 0; // Bitwise-or of all char codes in list. | 175 int bits = 0; // Bitwise-or of all char codes in list. |
| 180 if (end == null) { | 176 if (end == null) { |
| 181 var list = []; | 177 var list = []; |
| 182 while (it.moveNext()) { | 178 while (it.moveNext()) { |
| 183 int code = it.current; | 179 int code = it.current; |
| 184 bits |= code; | 180 bits |= code; |
| 185 list.add(code); | 181 list.add(code); |
| 186 } | 182 } |
| 187 charCodeList = makeListFixedLength(list); | 183 charCodeList = makeListFixedLength(list); |
| 188 } else { | 184 } else { |
| 189 if (end < start) { | 185 if (end < start) { |
| (...skipping 29 matching lines...) Expand all Loading... |
| 219 s._setAt(i, charCodes[start + i]); | 215 s._setAt(i, charCodes[start + i]); |
| 220 } | 216 } |
| 221 return s; | 217 return s; |
| 222 } | 218 } |
| 223 | 219 |
| 224 static String _createFromCodePoints(List<int> codePoints, int start, int end) | 220 static String _createFromCodePoints(List<int> codePoints, int start, int end) |
| 225 native "StringBase_createFromCodePoints"; | 221 native "StringBase_createFromCodePoints"; |
| 226 | 222 |
| 227 String operator [](int index) native "String_charAt"; | 223 String operator [](int index) native "String_charAt"; |
| 228 | 224 |
| 229 int codeUnitAt(int index); // Implemented in the subclasses. | 225 int codeUnitAt(int index); // Implemented in the subclasses. |
| 230 | 226 |
| 231 int get length native "String_getLength"; | 227 int get length native "String_getLength"; |
| 232 | 228 |
| 233 bool get isEmpty { | 229 bool get isEmpty { |
| 234 return this.length == 0; | 230 return this.length == 0; |
| 235 } | 231 } |
| 236 | 232 |
| 237 bool get isNotEmpty => !isEmpty; | 233 bool get isNotEmpty => !isEmpty; |
| 238 | 234 |
| 239 String operator +(String other) native "String_concat"; | 235 String operator +(String other) native "String_concat"; |
| 240 | 236 |
| 241 String toString() { | 237 String toString() { |
| 242 return this; | 238 return this; |
| 243 } | 239 } |
| 244 | 240 |
| 245 bool operator ==(Object other) { | 241 bool operator ==(Object other) { |
| 246 if (identical(this, other)) { | 242 if (identical(this, other)) { |
| 247 return true; | 243 return true; |
| 248 } | 244 } |
| 249 if ((other is! String) || (this.length != other.length)) { | 245 if ((other is! String) || |
| 246 (this.length != other.length)) { |
| 250 return false; | 247 return false; |
| 251 } | 248 } |
| 252 final len = this.length; | 249 final len = this.length; |
| 253 for (int i = 0; i < len; i++) { | 250 for (int i = 0; i < len; i++) { |
| 254 if (this.codeUnitAt(i) != other.codeUnitAt(i)) { | 251 if (this.codeUnitAt(i) != other.codeUnitAt(i)) { |
| 255 return false; | 252 return false; |
| 256 } | 253 } |
| 257 } | 254 } |
| 258 return true; | 255 return true; |
| 259 } | 256 } |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 385 } | 382 } |
| 386 return _substringUncheckedNative(startIndex, endIndex); | 383 return _substringUncheckedNative(startIndex, endIndex); |
| 387 } | 384 } |
| 388 | 385 |
| 389 String _substringUncheckedNative(int startIndex, int endIndex) | 386 String _substringUncheckedNative(int startIndex, int endIndex) |
| 390 native "StringBase_substringUnchecked"; | 387 native "StringBase_substringUnchecked"; |
| 391 | 388 |
| 392 // Checks for one-byte whitespaces only. | 389 // Checks for one-byte whitespaces only. |
| 393 static bool _isOneByteWhitespace(int codeUnit) { | 390 static bool _isOneByteWhitespace(int codeUnit) { |
| 394 if (codeUnit <= 32) { | 391 if (codeUnit <= 32) { |
| 395 return ((codeUnit == 32) || // Space. | 392 return ((codeUnit == 32) || // Space. |
| 396 ((codeUnit <= 13) && (codeUnit >= 9))); // CR, LF, TAB, etc. | 393 ((codeUnit <= 13) && (codeUnit >= 9))); // CR, LF, TAB, etc. |
| 397 } | 394 } |
| 398 return (codeUnit == 0x85) || (codeUnit == 0xA0); // NEL, NBSP. | 395 return (codeUnit == 0x85) || (codeUnit == 0xA0); // NEL, NBSP. |
| 399 } | 396 } |
| 400 | 397 |
| 401 // Characters with Whitespace property (Unicode 6.2). | 398 // Characters with Whitespace property (Unicode 6.2). |
| 402 // 0009..000D ; White_Space # Cc <control-0009>..<control-000D> | 399 // 0009..000D ; White_Space # Cc <control-0009>..<control-000D> |
| 403 // 0020 ; White_Space # Zs SPACE | 400 // 0020 ; White_Space # Zs SPACE |
| 404 // 0085 ; White_Space # Cc <control-0085> | 401 // 0085 ; White_Space # Cc <control-0085> |
| 405 // 00A0 ; White_Space # Zs NO-BREAK SPACE | 402 // 00A0 ; White_Space # Zs NO-BREAK SPACE |
| 406 // 1680 ; White_Space # Zs OGHAM SPACE MARK | 403 // 1680 ; White_Space # Zs OGHAM SPACE MARK |
| 407 // 180E ; White_Space # Zs MONGOLIAN VOWEL SEPARATOR | 404 // 180E ; White_Space # Zs MONGOLIAN VOWEL SEPARATOR |
| 408 // 2000..200A ; White_Space # Zs EN QUAD..HAIR SPACE | 405 // 2000..200A ; White_Space # Zs EN QUAD..HAIR SPACE |
| 409 // 2028 ; White_Space # Zl LINE SEPARATOR | 406 // 2028 ; White_Space # Zl LINE SEPARATOR |
| 410 // 2029 ; White_Space # Zp PARAGRAPH SEPARATOR | 407 // 2029 ; White_Space # Zp PARAGRAPH SEPARATOR |
| 411 // 202F ; White_Space # Zs NARROW NO-BREAK SPACE | 408 // 202F ; White_Space # Zs NARROW NO-BREAK SPACE |
| 412 // 205F ; White_Space # Zs MEDIUM MATHEMATICAL SPACE | 409 // 205F ; White_Space # Zs MEDIUM MATHEMATICAL SPACE |
| 413 // 3000 ; White_Space # Zs IDEOGRAPHIC SPACE | 410 // 3000 ; White_Space # Zs IDEOGRAPHIC SPACE |
| 414 // | 411 // |
| 415 // BOM: 0xFEFF | 412 // BOM: 0xFEFF |
| 416 static bool _isTwoByteWhitespace(int codeUnit) { | 413 static bool _isTwoByteWhitespace(int codeUnit) { |
| 417 if (codeUnit <= 32) { | 414 if (codeUnit <= 32) { |
| 418 return (codeUnit == 32) || ((codeUnit <= 13) && (codeUnit >= 9)); | 415 return (codeUnit == 32) || |
| 416 ((codeUnit <= 13) && (codeUnit >= 9)); |
| 419 } | 417 } |
| 420 if (codeUnit < 0x85) return false; | 418 if (codeUnit < 0x85) return false; |
| 421 if ((codeUnit == 0x85) || (codeUnit == 0xA0)) return true; | 419 if ((codeUnit == 0x85) || (codeUnit == 0xA0)) return true; |
| 422 return (codeUnit <= 0x200A) | 420 return (codeUnit <= 0x200A) |
| 423 ? ((codeUnit == 0x1680) || (codeUnit == 0x180E) || (0x2000 <= codeUnit)) | 421 ? ((codeUnit == 0x1680) || |
| 424 : ((codeUnit == 0x2028) || | 422 (codeUnit == 0x180E) || |
| 425 (codeUnit == 0x2029) || | 423 (0x2000 <= codeUnit)) |
| 426 (codeUnit == 0x202F) || | 424 : ((codeUnit == 0x2028) || |
| 427 (codeUnit == 0x205F) || | 425 (codeUnit == 0x2029) || |
| 428 (codeUnit == 0x3000) || | 426 (codeUnit == 0x202F) || |
| 429 (codeUnit == 0xFEFF)); | 427 (codeUnit == 0x205F) || |
| 428 (codeUnit == 0x3000) || |
| 429 (codeUnit == 0xFEFF)); |
| 430 } | 430 } |
| 431 | 431 |
| 432 int _firstNonWhitespace() { | 432 int _firstNonWhitespace() { |
| 433 final len = this.length; | 433 final len = this.length; |
| 434 int first = 0; | 434 int first = 0; |
| 435 for (; first < len; first++) { | 435 for (; first < len; first++) { |
| 436 if (!_isWhitespace(this.codeUnitAt(first))) { | 436 if (!_isWhitespace(this.codeUnitAt(first))) { |
| 437 break; | 437 break; |
| 438 } | 438 } |
| 439 } | 439 } |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 498 // String contains only whitespaces. | 498 // String contains only whitespaces. |
| 499 return ""; | 499 return ""; |
| 500 } | 500 } |
| 501 if (last == (len - 1)) { | 501 if (last == (len - 1)) { |
| 502 // Returns this string since it does not have trailing whitespaces. | 502 // Returns this string since it does not have trailing whitespaces. |
| 503 return this; | 503 return this; |
| 504 } | 504 } |
| 505 return _substringUnchecked(0, last + 1); | 505 return _substringUnchecked(0, last + 1); |
| 506 } | 506 } |
| 507 | 507 |
| 508 String operator *(int times) { | 508 String operator*(int times) { |
| 509 if (times <= 0) return ""; | 509 if (times <= 0) return ""; |
| 510 if (times == 1) return this; | 510 if (times == 1) return this; |
| 511 StringBuffer buffer = new StringBuffer(this); | 511 StringBuffer buffer = new StringBuffer(this); |
| 512 for (int i = 1; i < times; i++) { | 512 for (int i = 1; i < times; i++) { |
| 513 buffer.write(this); | 513 buffer.write(this); |
| 514 } | 514 } |
| 515 return buffer.toString(); | 515 return buffer.toString(); |
| 516 } | 516 } |
| 517 | 517 |
| 518 String padLeft(int width, [String padding = ' ']) { | 518 String padLeft(int width, [String padding = ' ']) { |
| (...skipping 20 matching lines...) Expand all Loading... |
| 539 bool contains(Pattern pattern, [int startIndex = 0]) { | 539 bool contains(Pattern pattern, [int startIndex = 0]) { |
| 540 if (pattern is String) { | 540 if (pattern is String) { |
| 541 if (startIndex < 0 || startIndex > this.length) { | 541 if (startIndex < 0 || startIndex > this.length) { |
| 542 throw new RangeError.range(startIndex, 0, this.length); | 542 throw new RangeError.range(startIndex, 0, this.length); |
| 543 } | 543 } |
| 544 return indexOf(pattern, startIndex) >= 0; | 544 return indexOf(pattern, startIndex) >= 0; |
| 545 } | 545 } |
| 546 return pattern.allMatches(this.substring(startIndex)).isNotEmpty; | 546 return pattern.allMatches(this.substring(startIndex)).isNotEmpty; |
| 547 } | 547 } |
| 548 | 548 |
| 549 String replaceFirst(Pattern pattern, String replacement, | 549 String replaceFirst(Pattern pattern, |
| 550 [int startIndex = 0]) { | 550 String replacement, |
| 551 [int startIndex = 0]) { |
| 551 if (pattern is! Pattern) { | 552 if (pattern is! Pattern) { |
| 552 throw new ArgumentError("${pattern} is not a Pattern"); | 553 throw new ArgumentError("${pattern} is not a Pattern"); |
| 553 } | 554 } |
| 554 if (replacement is! String) { | 555 if (replacement is! String) { |
| 555 throw new ArgumentError("${replacement} is not a String"); | 556 throw new ArgumentError("${replacement} is not a String"); |
| 556 } | 557 } |
| 557 if (startIndex is! int) { | 558 if (startIndex is! int) { |
| 558 throw new ArgumentError("${startIndex} is not an int"); | 559 throw new ArgumentError("${startIndex} is not an int"); |
| 559 } | 560 } |
| 560 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex"); | 561 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex"); |
| 561 Iterator iterator = startIndex == 0 | 562 Iterator iterator = |
| 562 ? pattern.allMatches(this).iterator | 563 startIndex == 0 ? pattern.allMatches(this).iterator |
| 563 : pattern.allMatches(this, startIndex).iterator; | 564 : pattern.allMatches(this, startIndex).iterator; |
| 564 if (!iterator.moveNext()) return this; | 565 if (!iterator.moveNext()) return this; |
| 565 Match match = iterator.current; | 566 Match match = iterator.current; |
| 566 return replaceRange(match.start, match.end, replacement); | 567 return replaceRange(match.start, match.end, replacement); |
| 567 } | 568 } |
| 568 | 569 |
| 569 String replaceRange(int start, int end, String replacement) { | 570 String replaceRange(int start, int end, String replacement) { |
| 570 int length = this.length; | 571 int length = this.length; |
| 571 end = RangeError.checkValidRange(start, end, length); | 572 end = RangeError.checkValidRange(start, end, length); |
| 572 bool replacementIsOneByte = replacement._isOneByte; | 573 bool replacementIsOneByte = replacement._isOneByte; |
| 573 if (start == 0 && end == length) return replacement; | 574 if (start == 0 && end == length) return replacement; |
| 574 int replacementLength = replacement.length; | 575 int replacementLength = replacement.length; |
| 575 int totalLength = start + (length - end) + replacementLength; | 576 int totalLength = start + (length - end) + replacementLength; |
| 576 if (replacementIsOneByte && this._isOneByte) { | 577 if (replacementIsOneByte && this._isOneByte) { |
| 577 var result = _OneByteString._allocate(totalLength); | 578 var result = _OneByteString._allocate(totalLength); |
| 578 int index = 0; | 579 int index = 0; |
| 579 index = result._setRange(index, this, 0, start); | 580 index = result._setRange(index, this, 0, start); |
| 580 index = result._setRange(start, replacement, 0, replacementLength); | 581 index = result._setRange(start, replacement, 0, replacementLength); |
| 581 result._setRange(index, this, end, length); | 582 result._setRange(index, this, end, length); |
| 582 return result; | 583 return result; |
| 583 } | 584 } |
| 584 List slices = []; | 585 List slices = []; |
| 585 _addReplaceSlice(slices, 0, start); | 586 _addReplaceSlice(slices, 0, start); |
| 586 if (replacement.length > 0) slices.add(replacement); | 587 if (replacement.length > 0) slices.add(replacement); |
| 587 _addReplaceSlice(slices, end, length); | 588 _addReplaceSlice(slices, end, length); |
| 588 return _joinReplaceAllResult( | 589 return _joinReplaceAllResult(this, slices, totalLength, |
| 589 this, slices, totalLength, replacementIsOneByte); | 590 replacementIsOneByte); |
| 590 } | 591 } |
| 591 | 592 |
| 592 static int _addReplaceSlice(List matches, int start, int end) { | 593 static int _addReplaceSlice(List matches, int start, int end) { |
| 593 int length = end - start; | 594 int length = end - start; |
| 594 if (length > 0) { | 595 if (length > 0) { |
| 595 if (length <= _maxLengthValue && start <= _maxStartValue) { | 596 if (length <= _maxLengthValue && start <= _maxStartValue) { |
| 596 matches.add(-((start << _lengthBits) | length)); | 597 matches.add(-((start << _lengthBits) | length)); |
| 597 } else { | 598 } else { |
| 598 matches.add(start); | 599 matches.add(start); |
| 599 matches.add(end); | 600 matches.add(end); |
| (...skipping 24 matching lines...) Expand all Loading... |
| 624 } | 625 } |
| 625 } | 626 } |
| 626 length += _addReplaceSlice(matches, startIndex, this.length); | 627 length += _addReplaceSlice(matches, startIndex, this.length); |
| 627 bool replacementIsOneByte = replacement._isOneByte; | 628 bool replacementIsOneByte = replacement._isOneByte; |
| 628 if (replacementIsOneByte && | 629 if (replacementIsOneByte && |
| 629 length < _maxJoinReplaceOneByteStringLength && | 630 length < _maxJoinReplaceOneByteStringLength && |
| 630 this._isOneByte) { | 631 this._isOneByte) { |
| 631 // TODO(lrn): Is there a cut-off point, or is runtime always faster? | 632 // TODO(lrn): Is there a cut-off point, or is runtime always faster? |
| 632 return _joinReplaceAllOneByteResult(this, matches, length); | 633 return _joinReplaceAllOneByteResult(this, matches, length); |
| 633 } | 634 } |
| 634 return _joinReplaceAllResult(this, matches, length, replacementIsOneByte); | 635 return _joinReplaceAllResult(this, matches, length, |
| 636 replacementIsOneByte); |
| 635 } | 637 } |
| 636 | 638 |
| 637 /** | 639 /** |
| 638 * As [_joinReplaceAllResult], but knowing that the result | 640 * As [_joinReplaceAllResult], but knowing that the result |
| 639 * is always a [_OneByteString]. | 641 * is always a [_OneByteString]. |
| 640 */ | 642 */ |
| 641 static String _joinReplaceAllOneByteResult( | 643 static String _joinReplaceAllOneByteResult(String base, |
| 642 String base, List matches, int length) { | 644 List matches, |
| 645 int length) { |
| 643 _OneByteString result = _OneByteString._allocate(length); | 646 _OneByteString result = _OneByteString._allocate(length); |
| 644 int writeIndex = 0; | 647 int writeIndex = 0; |
| 645 for (int i = 0; i < matches.length; i++) { | 648 for (int i = 0; i < matches.length; i++) { |
| 646 var entry = matches[i]; | 649 var entry = matches[i]; |
| 647 if (entry is _Smi) { | 650 if (entry is _Smi) { |
| 648 int sliceStart = entry; | 651 int sliceStart = entry; |
| 649 int sliceEnd; | 652 int sliceEnd; |
| 650 if (sliceStart < 0) { | 653 if (sliceStart < 0) { |
| 651 int bits = -sliceStart; | 654 int bits = -sliceStart; |
| 652 int sliceLength = bits & _lengthMask; | 655 int sliceLength = bits & _lengthMask; |
| (...skipping 26 matching lines...) Expand all Loading... |
| 679 * Combine the results of a [replaceAll] match into a new string. | 682 * Combine the results of a [replaceAll] match into a new string. |
| 680 * | 683 * |
| 681 * The [matches] lists contains Smi index pairs representing slices of | 684 * The [matches] lists contains Smi index pairs representing slices of |
| 682 * [base] and [String]s to be put in between the slices. | 685 * [base] and [String]s to be put in between the slices. |
| 683 * | 686 * |
| 684 * The total [length] of the resulting string is known, as is | 687 * The total [length] of the resulting string is known, as is |
| 685 * whether the replacement strings are one-byte strings. | 688 * whether the replacement strings are one-byte strings. |
| 686 * If they are, then we have to check the base string slices to know | 689 * If they are, then we have to check the base string slices to know |
| 687 * whether the result must be a one-byte string. | 690 * whether the result must be a one-byte string. |
| 688 */ | 691 */ |
| 689 static String | 692 static String _joinReplaceAllResult(String base, List matches, int length, |
| 690 _joinReplaceAllResult(String base, List matches, int length, | 693 bool replacementStringsAreOneByte) |
| 691 bool replacementStringsAreOneByte) | |
| 692 native "StringBase_joinReplaceAllResult"; | 694 native "StringBase_joinReplaceAllResult"; |
| 693 | 695 |
| 694 String replaceAllMapped(Pattern pattern, String replace(Match match)) { | 696 String replaceAllMapped(Pattern pattern, String replace(Match match)) { |
| 695 if (pattern == null) throw new ArgumentError.notNull("pattern"); | 697 if (pattern == null) throw new ArgumentError.notNull("pattern"); |
| 696 if (replace == null) throw new ArgumentError.notNull("replace"); | 698 if (replace == null) throw new ArgumentError.notNull("replace"); |
| 697 List matches = []; | 699 List matches = []; |
| 698 int length = 0; | 700 int length = 0; |
| 699 int startIndex = 0; | 701 int startIndex = 0; |
| 700 bool replacementStringsAreOneByte = true; | 702 bool replacementStringsAreOneByte = true; |
| 701 for (Match match in pattern.allMatches(this)) { | 703 for (Match match in pattern.allMatches(this)) { |
| 702 length += _addReplaceSlice(matches, startIndex, match.start); | 704 length += _addReplaceSlice(matches, startIndex, match.start); |
| 703 var replacement = "${replace(match)}"; | 705 var replacement = "${replace(match)}"; |
| 704 matches.add(replacement); | 706 matches.add(replacement); |
| 705 length += replacement.length; | 707 length += replacement.length; |
| 706 replacementStringsAreOneByte = | 708 replacementStringsAreOneByte = |
| 707 replacementStringsAreOneByte && replacement._isOneByte; | 709 replacementStringsAreOneByte && replacement._isOneByte; |
| 708 startIndex = match.end; | 710 startIndex = match.end; |
| 709 } | 711 } |
| 710 if (matches.isEmpty) return this; | 712 if (matches.isEmpty) return this; |
| 711 length += _addReplaceSlice(matches, startIndex, this.length); | 713 length += _addReplaceSlice(matches, startIndex, this.length); |
| 712 if (replacementStringsAreOneByte && | 714 if (replacementStringsAreOneByte && |
| 713 length < _maxJoinReplaceOneByteStringLength && | 715 length < _maxJoinReplaceOneByteStringLength && |
| 714 this._isOneByte) { | 716 this._isOneByte) { |
| 715 return _joinReplaceAllOneByteResult(this, matches, length); | 717 return _joinReplaceAllOneByteResult(this, matches, length); |
| 716 } | 718 } |
| 717 return _joinReplaceAllResult( | 719 return _joinReplaceAllResult(this, matches, length, |
| 718 this, matches, length, replacementStringsAreOneByte); | 720 replacementStringsAreOneByte); |
| 719 } | 721 } |
| 720 | 722 |
| 721 String replaceFirstMapped(Pattern pattern, String replace(Match match), | 723 String replaceFirstMapped(Pattern pattern, String replace(Match match), |
| 722 [int startIndex = 0]) { | 724 [int startIndex = 0]) { |
| 723 if (pattern == null) throw new ArgumentError.notNull("pattern"); | 725 if (pattern == null) throw new ArgumentError.notNull("pattern"); |
| 724 if (replace == null) throw new ArgumentError.notNull("replace"); | 726 if (replace == null) throw new ArgumentError.notNull("replace"); |
| 725 if (startIndex == null) throw new ArgumentError.notNull("startIndex"); | 727 if (startIndex == null) throw new ArgumentError.notNull("startIndex"); |
| 726 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex"); | 728 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex"); |
| 727 | 729 |
| 728 var matches = pattern.allMatches(this, startIndex).iterator; | 730 var matches = pattern.allMatches(this, startIndex).iterator; |
| 729 if (!matches.moveNext()) return this; | 731 if (!matches.moveNext()) return this; |
| 730 var match = matches.current; | 732 var match = matches.current; |
| 731 var replacement = "${replace(match)}"; | 733 var replacement = "${replace(match)}"; |
| 732 return replaceRange(match.start, match.end, replacement); | 734 return replaceRange(match.start, match.end, replacement); |
| 733 } | 735 } |
| 734 | 736 |
| 735 static String _matchString(Match match) => match[0]; | 737 static String _matchString(Match match) => match[0]; |
| 736 static String _stringIdentity(String string) => string; | 738 static String _stringIdentity(String string) => string; |
| 737 | 739 |
| 738 String _splitMapJoinEmptyString( | 740 String _splitMapJoinEmptyString(String onMatch(Match match), |
| 739 String onMatch(Match match), String onNonMatch(String nonMatch)) { | 741 String onNonMatch(String nonMatch)) { |
| 740 // Pattern is the empty string. | 742 // Pattern is the empty string. |
| 741 StringBuffer buffer = new StringBuffer(); | 743 StringBuffer buffer = new StringBuffer(); |
| 742 int length = this.length; | 744 int length = this.length; |
| 743 int i = 0; | 745 int i = 0; |
| 744 buffer.write(onNonMatch("")); | 746 buffer.write(onNonMatch("")); |
| 745 while (i < length) { | 747 while (i < length) { |
| 746 buffer.write(onMatch(new _StringMatch(i, this, ""))); | 748 buffer.write(onMatch(new _StringMatch(i, this, ""))); |
| 747 // Special case to avoid splitting a surrogate pair. | 749 // Special case to avoid splitting a surrogate pair. |
| 748 int code = this.codeUnitAt(i); | 750 int code = this.codeUnitAt(i); |
| 749 if ((code & ~0x3FF) == 0xD800 && length > i + 1) { | 751 if ((code & ~0x3FF) == 0xD800 && length > i + 1) { |
| 750 // Leading surrogate; | 752 // Leading surrogate; |
| 751 code = this.codeUnitAt(i + 1); | 753 code = this.codeUnitAt(i + 1); |
| 752 if ((code & ~0x3FF) == 0xDC00) { | 754 if ((code & ~0x3FF) == 0xDC00) { |
| 753 // Matching trailing surrogate. | 755 // Matching trailing surrogate. |
| 754 buffer.write(onNonMatch(this.substring(i, i + 2))); | 756 buffer.write(onNonMatch(this.substring(i, i + 2))); |
| 755 i += 2; | 757 i += 2; |
| 756 continue; | 758 continue; |
| 757 } | 759 } |
| 758 } | 760 } |
| 759 buffer.write(onNonMatch(this[i])); | 761 buffer.write(onNonMatch(this[i])); |
| 760 i++; | 762 i++; |
| 761 } | 763 } |
| 762 buffer.write(onMatch(new _StringMatch(i, this, ""))); | 764 buffer.write(onMatch(new _StringMatch(i, this, ""))); |
| 763 buffer.write(onNonMatch("")); | 765 buffer.write(onNonMatch("")); |
| 764 return buffer.toString(); | 766 return buffer.toString(); |
| 765 } | 767 } |
| 766 | 768 |
| 767 String splitMapJoin(Pattern pattern, | 769 String splitMapJoin(Pattern pattern, |
| 768 {String onMatch(Match match), String onNonMatch(String nonMatch)}) { | 770 {String onMatch(Match match), |
| 771 String onNonMatch(String nonMatch)}) { |
| 769 if (pattern is! Pattern) { | 772 if (pattern is! Pattern) { |
| 770 throw new ArgumentError("${pattern} is not a Pattern"); | 773 throw new ArgumentError("${pattern} is not a Pattern"); |
| 771 } | 774 } |
| 772 if (onMatch == null) onMatch = _matchString; | 775 if (onMatch == null) onMatch = _matchString; |
| 773 if (onNonMatch == null) onNonMatch = _stringIdentity; | 776 if (onNonMatch == null) onNonMatch = _stringIdentity; |
| 774 if (pattern is String) { | 777 if (pattern is String) { |
| 775 String stringPattern = pattern; | 778 String stringPattern = pattern; |
| 776 if (stringPattern.isEmpty) { | 779 if (stringPattern.isEmpty) { |
| 777 return _splitMapJoinEmptyString(onMatch, onNonMatch); | 780 return _splitMapJoinEmptyString(onMatch, onNonMatch); |
| 778 } | 781 } |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 877 result.add(this.substring(previousIndex, length)); | 880 result.add(this.substring(previousIndex, length)); |
| 878 break; | 881 break; |
| 879 } | 882 } |
| 880 Match match = iterator.current; | 883 Match match = iterator.current; |
| 881 if (match.start == length) { | 884 if (match.start == length) { |
| 882 result.add(this.substring(previousIndex, length)); | 885 result.add(this.substring(previousIndex, length)); |
| 883 break; | 886 break; |
| 884 } | 887 } |
| 885 int endIndex = match.end; | 888 int endIndex = match.end; |
| 886 if (startIndex == endIndex && endIndex == previousIndex) { | 889 if (startIndex == endIndex && endIndex == previousIndex) { |
| 887 ++startIndex; // empty match, advance and restart | 890 ++startIndex; // empty match, advance and restart |
| 888 continue; | 891 continue; |
| 889 } | 892 } |
| 890 result.add(this.substring(previousIndex, match.start)); | 893 result.add(this.substring(previousIndex, match.start)); |
| 891 startIndex = previousIndex = endIndex; | 894 startIndex = previousIndex = endIndex; |
| 892 } | 895 } |
| 893 return result; | 896 return result; |
| 894 } | 897 } |
| 895 | 898 |
| 896 List<int> get codeUnits => new CodeUnits(this); | 899 List<int> get codeUnits => new CodeUnits(this); |
| 897 | 900 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 909 } | 912 } |
| 910 return _concatRangeNative(strings, start, end); | 913 return _concatRangeNative(strings, start, end); |
| 911 } | 914 } |
| 912 | 915 |
| 913 // Call this method if not all list elements are known to be OneByteString(s). | 916 // Call this method if not all list elements are known to be OneByteString(s). |
| 914 // 'strings' must be an _List or _GrowableList. | 917 // 'strings' must be an _List or _GrowableList. |
| 915 static String _concatRangeNative(List<String> strings, int start, int end) | 918 static String _concatRangeNative(List<String> strings, int start, int end) |
| 916 native "String_concatRange"; | 919 native "String_concatRange"; |
| 917 } | 920 } |
| 918 | 921 |
| 922 |
| 919 class _OneByteString extends _StringBase implements String { | 923 class _OneByteString extends _StringBase implements String { |
| 924 |
| 920 factory _OneByteString._uninstantiable() { | 925 factory _OneByteString._uninstantiable() { |
| 921 throw new UnsupportedError( | 926 throw new UnsupportedError( |
| 922 "_OneByteString can only be allocated by the VM"); | 927 "_OneByteString can only be allocated by the VM"); |
| 923 } | 928 } |
| 924 | 929 |
| 925 int get hashCode native "String_getHashCode"; | 930 int get hashCode native "String_getHashCode"; |
| 926 | 931 |
| 927 int codeUnitAt(int index) native "String_codeUnitAt"; | 932 int codeUnitAt(int index) native "String_codeUnitAt"; |
| 928 | 933 |
| 929 bool _isWhitespace(int codeUnit) { | 934 bool _isWhitespace(int codeUnit) { |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1006 if (this.codeUnitAt(i) == patternCu0) { | 1011 if (this.codeUnitAt(i) == patternCu0) { |
| 1007 return true; | 1012 return true; |
| 1008 } | 1013 } |
| 1009 } | 1014 } |
| 1010 return false; | 1015 return false; |
| 1011 } | 1016 } |
| 1012 } | 1017 } |
| 1013 return super.contains(pattern, start); | 1018 return super.contains(pattern, start); |
| 1014 } | 1019 } |
| 1015 | 1020 |
| 1016 String operator *(int times) { | 1021 String operator*(int times) { |
| 1017 if (times <= 0) return ""; | 1022 if (times <= 0) return ""; |
| 1018 if (times == 1) return this; | 1023 if (times == 1) return this; |
| 1019 int length = this.length; | 1024 int length = this.length; |
| 1020 if (this.isEmpty) return this; // Don't clone empty string. | 1025 if (this.isEmpty) return this; // Don't clone empty string. |
| 1021 _OneByteString result = _OneByteString._allocate(length * times); | 1026 _OneByteString result = _OneByteString._allocate(length * times); |
| 1022 int index = 0; | 1027 int index = 0; |
| 1023 for (int i = 0; i < times; i++) { | 1028 for (int i = 0; i < times; i ++) { |
| 1024 for (int j = 0; j < length; j++) { | 1029 for (int j = 0; j < length; j++) { |
| 1025 result._setAt(index++, this.codeUnitAt(j)); | 1030 result._setAt(index++, this.codeUnitAt(j)); |
| 1026 } | 1031 } |
| 1027 } | 1032 } |
| 1028 return result; | 1033 return result; |
| 1029 } | 1034 } |
| 1030 | 1035 |
| 1031 String padLeft(int width, [String padding = ' ']) { | 1036 String padLeft(int width, [String padding = ' ']) { |
| 1032 int padCid = ClassID.getID(padding); | 1037 int padCid = ClassID.getID(padding); |
| 1033 if ((padCid != ClassID.cidOneByteString) && | 1038 if ((padCid != ClassID.cidOneByteString) && |
| (...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1181 } | 1186 } |
| 1182 return result; | 1187 return result; |
| 1183 } | 1188 } |
| 1184 return this; | 1189 return this; |
| 1185 } | 1190 } |
| 1186 | 1191 |
| 1187 // Allocates a string of given length, expecting its content to be | 1192 // Allocates a string of given length, expecting its content to be |
| 1188 // set using _setAt. | 1193 // set using _setAt. |
| 1189 static _OneByteString _allocate(int length) native "OneByteString_allocate"; | 1194 static _OneByteString _allocate(int length) native "OneByteString_allocate"; |
| 1190 | 1195 |
| 1191 static _OneByteString _allocateFromOneByteList(List<int> list, int start, | 1196 |
| 1192 int end) native "OneByteString_allocateFromOneByteList"; | 1197 static _OneByteString _allocateFromOneByteList(List<int> list, |
| 1198 int start, int end) |
| 1199 native "OneByteString_allocateFromOneByteList"; |
| 1193 | 1200 |
| 1194 // This is internal helper method. Code point value must be a valid | 1201 // This is internal helper method. Code point value must be a valid |
| 1195 // Latin1 value (0..0xFF), index must be valid. | 1202 // Latin1 value (0..0xFF), index must be valid. |
| 1196 void _setAt(int index, int codePoint) native "OneByteString_setAt"; | 1203 void _setAt(int index, int codePoint) native "OneByteString_setAt"; |
| 1197 | 1204 |
| 1198 // Should be optimizable to a memory move. | 1205 // Should be optimizable to a memory move. |
| 1199 // Accepts both _OneByteString and _ExternalOneByteString as argument. | 1206 // Accepts both _OneByteString and _ExternalOneByteString as argument. |
| 1200 // Returns index after last character written. | 1207 // Returns index after last character written. |
| 1201 int _setRange(int index, String oneByteString, int start, int end) { | 1208 int _setRange(int index, String oneByteString, int start, int end) { |
| 1202 assert(oneByteString._isOneByte); | 1209 assert(oneByteString._isOneByte); |
| 1203 assert(0 <= start); | 1210 assert(0 <= start); |
| 1204 assert(start <= end); | 1211 assert(start <= end); |
| 1205 assert(end <= oneByteString.length); | 1212 assert(end <= oneByteString.length); |
| 1206 assert(0 <= index); | 1213 assert(0 <= index); |
| 1207 assert(index + (end - start) <= length); | 1214 assert(index + (end - start) <= length); |
| 1208 for (int i = start; i < end; i++) { | 1215 for (int i = start; i < end; i++) { |
| 1209 _setAt(index, oneByteString.codeUnitAt(i)); | 1216 _setAt(index, oneByteString.codeUnitAt(i)); |
| 1210 index += 1; | 1217 index += 1; |
| 1211 } | 1218 } |
| 1212 return index; | 1219 return index; |
| 1213 } | 1220 } |
| 1214 } | 1221 } |
| 1215 | 1222 |
| 1223 |
| 1216 class _TwoByteString extends _StringBase implements String { | 1224 class _TwoByteString extends _StringBase implements String { |
| 1217 factory _TwoByteString._uninstantiable() { | 1225 factory _TwoByteString._uninstantiable() { |
| 1218 throw new UnsupportedError( | 1226 throw new UnsupportedError( |
| 1219 "_TwoByteString can only be allocated by the VM"); | 1227 "_TwoByteString can only be allocated by the VM"); |
| 1220 } | 1228 } |
| 1221 | 1229 |
| 1222 static String _allocateFromTwoByteList(List list, int start, int end) | 1230 static String _allocateFromTwoByteList(List list, int start, int end) |
| 1223 native "TwoByteString_allocateFromTwoByteList"; | 1231 native "TwoByteString_allocateFromTwoByteList"; |
| 1224 | 1232 |
| 1225 bool _isWhitespace(int codeUnit) { | 1233 bool _isWhitespace(int codeUnit) { |
| 1226 return _StringBase._isTwoByteWhitespace(codeUnit); | 1234 return _StringBase._isTwoByteWhitespace(codeUnit); |
| 1227 } | 1235 } |
| 1228 | 1236 |
| 1229 int codeUnitAt(int index) native "String_codeUnitAt"; | 1237 int codeUnitAt(int index) native "String_codeUnitAt"; |
| 1230 | 1238 |
| 1231 bool operator ==(Object other) { | 1239 bool operator ==(Object other) { |
| 1232 return super == other; | 1240 return super == other; |
| 1233 } | 1241 } |
| 1234 } | 1242 } |
| 1235 | 1243 |
| 1244 |
| 1236 class _ExternalOneByteString extends _StringBase implements String { | 1245 class _ExternalOneByteString extends _StringBase implements String { |
| 1237 factory _ExternalOneByteString._uninstantiable() { | 1246 factory _ExternalOneByteString._uninstantiable() { |
| 1238 throw new UnsupportedError( | 1247 throw new UnsupportedError( |
| 1239 "_ExternalOneByteString can only be allocated by the VM"); | 1248 "_ExternalOneByteString can only be allocated by the VM"); |
| 1240 } | 1249 } |
| 1241 | 1250 |
| 1242 bool _isWhitespace(int codeUnit) { | 1251 bool _isWhitespace(int codeUnit) { |
| 1243 return _StringBase._isOneByteWhitespace(codeUnit); | 1252 return _StringBase._isOneByteWhitespace(codeUnit); |
| 1244 } | 1253 } |
| 1245 | 1254 |
| 1246 int codeUnitAt(int index) native "String_codeUnitAt"; | 1255 int codeUnitAt(int index) native "String_codeUnitAt"; |
| 1247 | 1256 |
| 1248 bool operator ==(Object other) { | 1257 bool operator ==(Object other) { |
| 1249 return super == other; | 1258 return super == other; |
| 1250 } | 1259 } |
| 1251 | 1260 |
| 1252 static int _getCid() native "ExternalOneByteString_getCid"; | 1261 static int _getCid() native "ExternalOneByteString_getCid"; |
| 1253 } | 1262 } |
| 1254 | 1263 |
| 1264 |
| 1255 class _ExternalTwoByteString extends _StringBase implements String { | 1265 class _ExternalTwoByteString extends _StringBase implements String { |
| 1256 factory _ExternalTwoByteString._uninstantiable() { | 1266 factory _ExternalTwoByteString._uninstantiable() { |
| 1257 throw new UnsupportedError( | 1267 throw new UnsupportedError( |
| 1258 "_ExternalTwoByteString can only be allocated by the VM"); | 1268 "_ExternalTwoByteString can only be allocated by the VM"); |
| 1259 } | 1269 } |
| 1260 | 1270 |
| 1261 bool _isWhitespace(int codeUnit) { | 1271 bool _isWhitespace(int codeUnit) { |
| 1262 return _StringBase._isTwoByteWhitespace(codeUnit); | 1272 return _StringBase._isTwoByteWhitespace(codeUnit); |
| 1263 } | 1273 } |
| 1264 | 1274 |
| 1265 int codeUnitAt(int index) native "String_codeUnitAt"; | 1275 int codeUnitAt(int index) native "String_codeUnitAt"; |
| 1266 | 1276 |
| 1267 bool operator ==(Object other) { | 1277 bool operator ==(Object other) { |
| 1268 return super == other; | 1278 return super == other; |
| 1269 } | 1279 } |
| 1270 } | 1280 } |
| 1271 | 1281 |
| 1282 |
| 1272 class _StringMatch implements Match { | 1283 class _StringMatch implements Match { |
| 1273 const _StringMatch(this.start, this.input, this.pattern); | 1284 const _StringMatch(this.start, this.input, this.pattern); |
| 1274 | 1285 |
| 1275 int get end => start + pattern.length; | 1286 int get end => start + pattern.length; |
| 1276 String operator [](int g) => group(g); | 1287 String operator[](int g) => group(g); |
| 1277 int get groupCount => 0; | 1288 int get groupCount => 0; |
| 1278 | 1289 |
| 1279 String group(int group) { | 1290 String group(int group) { |
| 1280 if (group != 0) { | 1291 if (group != 0) { |
| 1281 throw new RangeError.value(group); | 1292 throw new RangeError.value(group); |
| 1282 } | 1293 } |
| 1283 return pattern; | 1294 return pattern; |
| 1284 } | 1295 } |
| 1285 | 1296 |
| 1286 List<String> groups(List<int> groups) { | 1297 List<String> groups(List<int> groups) { |
| 1287 List<String> result = new List<String>(); | 1298 List<String> result = new List<String>(); |
| 1288 for (int g in groups) { | 1299 for (int g in groups) { |
| 1289 result.add(group(g)); | 1300 result.add(group(g)); |
| 1290 } | 1301 } |
| 1291 return result; | 1302 return result; |
| 1292 } | 1303 } |
| 1293 | 1304 |
| 1294 final int start; | 1305 final int start; |
| 1295 final String input; | 1306 final String input; |
| 1296 final String pattern; | 1307 final String pattern; |
| 1297 } | 1308 } |
| 1298 | 1309 |
| 1310 |
| 1299 class _StringAllMatchesIterable extends Iterable<Match> { | 1311 class _StringAllMatchesIterable extends Iterable<Match> { |
| 1300 final String _input; | 1312 final String _input; |
| 1301 final String _pattern; | 1313 final String _pattern; |
| 1302 final int _index; | 1314 final int _index; |
| 1303 | 1315 |
| 1304 _StringAllMatchesIterable(this._input, this._pattern, this._index); | 1316 _StringAllMatchesIterable(this._input, this._pattern, this._index); |
| 1305 | 1317 |
| 1306 Iterator<Match> get iterator => | 1318 Iterator<Match> get iterator => |
| 1307 new _StringAllMatchesIterator(_input, _pattern, _index); | 1319 new _StringAllMatchesIterator(_input, _pattern, _index); |
| 1308 | 1320 |
| (...skipping 28 matching lines...) Expand all Loading... |
| 1337 int end = index + _pattern.length; | 1349 int end = index + _pattern.length; |
| 1338 _current = new _StringMatch(index, _input, _pattern); | 1350 _current = new _StringMatch(index, _input, _pattern); |
| 1339 // Empty match, don't start at same location again. | 1351 // Empty match, don't start at same location again. |
| 1340 if (end == _index) end++; | 1352 if (end == _index) end++; |
| 1341 _index = end; | 1353 _index = end; |
| 1342 return true; | 1354 return true; |
| 1343 } | 1355 } |
| 1344 | 1356 |
| 1345 Match get current => _current; | 1357 Match get current => _current; |
| 1346 } | 1358 } |
| OLD | NEW |