Chromium Code Reviews| Index: runtime/lib/string_patch.dart |
| diff --git a/runtime/lib/string_patch.dart b/runtime/lib/string_patch.dart |
| index 3a1fd711b6cd06e4a46f44ad383d6258a8d47a76..854b5095787ee335a8f29180f9f8f1afabae89a0 100644 |
| --- a/runtime/lib/string_patch.dart |
| +++ b/runtime/lib/string_patch.dart |
| @@ -2,10 +2,18 @@ |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| +const int _maxAscii = 0x7f; |
| +const int _maxLatin1 = 0xff; |
| +const int _maxUtf16 = 0xffff; |
| +const int _maxUnicode = 0x10ffff; |
| + |
| patch class String { |
| /* patch */ factory String.fromCharCodes(Iterable<int> charCodes, |
| [int start = 0, int end]) { |
| - return _StringBase.createFromCharCodes(charCodes, start, end); |
| + if (charCodes is! Iterable) throw new ArgumentError.value(charCodes, "charCodes"); |
| + if (start is! int) throw new ArgumentError.value(start, "start"); |
| + if (end != null && end is! int) throw new ArgumentError.value(end, "end"); |
| + return _StringBase.createFromCharCodes(charCodes, start, end, null); |
| } |
| /* patch */ factory String.fromCharCode(int charCode) { |
| @@ -51,11 +59,16 @@ class _StringBase { |
| int get hashCode native "String_getHashCode"; |
| /** |
| - * Create the most efficient string representation for specified |
| - * [codePoints]. |
| + * Create the most efficient string representation for specified |
| + * [codePoints]. |
|
siva
2015/01/20 23:50:26
The parameter name is not documented correctly, th
Lasse Reichstein Nielsen
2015/01/22 07:16:32
Done.
|
| + * |
| + * The [limit] is an upper limit on the character codes in the iterable. |
| + * It's `null` if unknown. |
|
siva
2015/01/20 23:50:26
Since limit is documented maybe we should also doc
Lasse Reichstein Nielsen
2015/01/22 07:16:33
Done.
|
| */ |
| static String createFromCharCodes(Iterable<int> charCodes, |
| - int start, int end) { |
| + int start, int end, |
| + int limit) { |
| + if (start == null) throw new ArgumentError.notNull("start"); |
| if (charCodes == null) throw new ArgumentError(charCodes); |
| // TODO(srdjan): Also skip copying of wide typed arrays. |
| final ccid = ClassID.getID(charCodes); |
| @@ -64,68 +77,97 @@ class _StringBase { |
| (ccid != ClassID.cidGrowableObjectArray) && |
| (ccid != ClassID.cidImmutableArray)) { |
| if (charCodes is Uint8List) { |
| - isOneByteString = true; |
| - } else { |
| - // Treat charCodes as Iterable. |
| - if (start < 0) throw new RangeError.range(start, 0, charCodes.length); |
| - if (end != null && end < start) { |
| - throw new RangeError.range(end, start, charCodes.length); |
| - } |
| - var it = charCodes.iterator; |
| - for (int i = 0; i < start; i++) { |
| - if (!it.moveNext()) { |
| - throw new RangeError.range(start, 0, i); |
| - } |
| - } |
| - int bits = 0; // Bitwise or of all char codes in list. |
| - var list = []; |
| - if (end == null) { |
| - while (it.moveNext()) { |
| - int code = it.current; |
| - bits |= code; |
| - list.add(code); |
| - } |
| - } else { |
| - for (int i = start; i < end; i++) { |
| - if (!it.moveNext()) { |
| - throw new RangeError.range(end, start, i); |
| - } |
| - int code = it.current; |
| - bits |= code; |
| - list.add(code); |
| - } |
| - } |
| - charCodes = list; |
| - isOneByteString = (bits >= 0 && bits <= 0xff); |
| - start = 0; |
| - end = list.length; |
| + end = RangeError.checkValidRange(start, end, charCodes.length); |
| + return _createOneByteString(charCodes, start, end - start); |
| + } else if (charCodes is! Uint16List) { |
| + return _createStringFromIterable(charCodes, start, end); |
| } |
| } |
| int codeCount = charCodes.length; |
| - if (start < 0 || start > codeCount) { |
| - throw new RangeError.range(start, 0, codeCount); |
| - } |
| - if (end == null) { |
| - end = codeCount; |
| - } else if (end < start || end > codeCount) { |
| - throw new RangeError.range(end, start, codeCount); |
| - } |
| + end = RangeError.checkValidRange(start, end, codeCount); |
| final len = end - start; |
| - if (!isOneByteString) { |
| - for (int i = start; i < end; i++) { |
| - int e = charCodes[i]; |
| - if (e is! _Smi) throw new ArgumentError(e); |
| - // Is e Latin1? |
| - if ((e < 0) || (e > 0xFF)) { |
| - return _createFromCodePoints(charCodes, start, end); |
| + if (len == 0) return ""; |
| + if (limit == null) { |
| + limit = _scanCodeUnits(charCodes, start, end); |
| + if (limit < 0) { |
| + throw new ArgumentError(charCodes); |
| + } |
|
siva
2015/01/20 23:50:26
the (limit < 0) should be pulled outside the if (l
Lasse Reichstein Nielsen
2015/01/22 07:16:32
While that shouldn't happen (we check that elsewhe
|
| + } |
| + if (limit <= _maxLatin1) { |
| + return _createOneByteString(charCodes, start, len); |
| + } |
|
siva
2015/01/20 23:50:26
Since you are checking for 'limit' not being < 0 a
Lasse Reichstein Nielsen
2015/01/22 07:16:32
True. I'll consider if it's worth it for another C
|
| + if (limit <= _maxUtf16) { |
| + return _TwoByteString._allocateFromTwoByteList(charCodes, start, end); |
| + } |
| + return _createFromCodePoints(charCodes, start, end); |
|
siva
2015/01/20 23:50:26
The same TODO applies to this call of createFromCo
Lasse Reichstein Nielsen
2015/01/22 07:16:33
If we get here, then limit > 0xffff, so there is a
|
| + } |
| + |
| + static int _scanCodeUnits(List<int> charCodes, int start, int end) { |
| + int bits = 0; |
| + for (int i = start; i < end; i++) { |
| + int code = charCodes[i]; |
| + if (code is! _Smi) throw new ArgumentError(charCodes); |
| + bits |= code; |
| + } |
| + return bits; |
| + } |
| + |
| + static String _createStringFromIterable(Iterable<int> charCodes, |
| + int start, int end) { |
| + // Treat charCodes as Iterable. |
| + if (charCodes is EfficientLength) { |
| + int length = charCodes.length; |
| + end = RangeError.checkValidRange(start, end, length); |
| + List charCodeList = new List.from(charCodes.take(end).skip(start), |
| + growable: false); |
| + return createFromCharCodes(charCodeList, 0, charCodeList.length, null); |
| + } |
| + // Don't know length of iterable, so iterate and see if all the values |
| + // are there. |
| + if (start < 0) throw new RangeError.range(start, 0, charCodes.length); |
| + if (end != null && end < start) { |
| + throw new RangeError.range(end, start, charCodes.length); |
| + } |
| + var it = charCodes.iterator; |
| + for (int i = 0; i < start; i++) { |
| + if (!it.moveNext()) { |
| + throw new RangeError.range(start, 0, i); |
| + } |
| + } |
| + List charCodeList; |
| + int bits = 0; // Bitwise-or of all char codes in list. |
| + if (end == null) { |
| + var list = []; |
| + while (it.moveNext()) { |
| + int code = it.current; |
| + bits |= code; |
| + list.add(code); |
| + } |
| + charCodeList = makeListFixedLength(list); |
| + } else { |
|
siva
2015/01/20 23:50:26
why not move the end < start check here to avoid h
Lasse Reichstein Nielsen
2015/01/22 07:16:32
Done.
|
| + int len = end - start; |
| + var list = new List(len); |
| + for (int i = 0; i < len; i++) { |
| + if (!it.moveNext()) { |
| + throw new RangeError.range(end, start, start + i); |
| } |
| + int code = it.current; |
| + bits |= code; |
| + list[i] = code; |
| } |
| + charCodeList = list; |
| } |
| - // Allocate a one byte string. When the list is 128 entries or longer, |
| - // it's faster to perform a runtime-call. |
| - if (len >= 128) { |
| - return _OneByteString._allocateFromOneByteList(charCodes, start, end); |
| + int length = charCodeList.length; |
| + if (bits < 0) throw new ArgumentError(charCodes); |
|
siva
2015/01/20 23:50:26
This style of not using a '{' ... '}' seems incons
Lasse Reichstein Nielsen
2015/01/22 07:16:32
I use it when it fits on a line, but not consisten
|
| + bool isOneByteString = (bits <= _maxLatin1); |
| + if (isOneByteString) { |
| + return _createOneByteString(charCodeList, 0, length); |
| } |
| + return createFromCharCodes(charCodeList, 0, length, bits); |
| + } |
| + |
| + static String _createOneByteString(List<int> charCodes, int start, int len) { |
| + // It's always faster to do this in Dart than to call into the runtime. |
| var s = _OneByteString._allocate(len); |
| for (int i = 0; i < len; i++) { |
| s._setAt(i, charCodes[start + i]); |
| @@ -133,6 +175,12 @@ class _StringBase { |
| return s; |
| } |
| + static String _createTwoByteString(List<int> charCodes, int start, int len) { |
| + // TODO(lrn): Create string without scanning charCodes again - all values |
| + // in the [start..end] range are uint16 values. |
| + return _createFromCodePoints(charCodes, start, end); |
| + } |
| + |
| static String _createFromCodePoints(List<int> codePoints, int start, int end) |
| native "StringBase_createFromCodePoints"; |
| @@ -996,6 +1044,9 @@ class _TwoByteString extends _StringBase implements String { |
| "_TwoByteString can only be allocated by the VM"); |
| } |
| + static String _allocateFromTwoByteList(List list, int start, int end) |
| + native "TwoByteString_allocateFromTwoByteList"; |
| + |
| bool _isWhitespace(int codeUnit) { |
| return _StringBase._isTwoByteWhitespace(codeUnit); |
| } |