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