Index: runtime/lib/string_patch.dart |
diff --git a/runtime/lib/string_patch.dart b/runtime/lib/string_patch.dart |
index f22b6e1fe834d727301712c211dee90b1da25a22..76950644b18fcd51c9e1b457cd1c1f1a0b68eb97 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) { |
@@ -80,11 +88,19 @@ 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 |
+ * [charCodes]. |
+ * |
+ * Only uses the character codes betwen index [start] and index [end] of |
+ * `charCodes`. They must satisfy `0 <= start <= end <= charCodes.length`. |
+ * |
+ * The [limit] is an upper limit on the character codes in the iterable. |
+ * It's `null` if unknown. |
*/ |
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); |
@@ -93,68 +109,102 @@ 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); |
+ end = RangeError.checkValidRange(start, end, codeCount); |
+ final len = end - start; |
+ if (len == 0) return ""; |
+ if (limit == null) { |
+ limit = _scanCodeUnits(charCodes, start, end); |
} |
- if (end == null) { |
- end = codeCount; |
- } else if (end < start || end > codeCount) { |
- throw new RangeError.range(end, start, codeCount); |
+ if (limit < 0) { |
+ throw new ArgumentError(charCodes); |
} |
- 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 (limit <= _maxLatin1) { |
+ return _createOneByteString(charCodes, start, len); |
+ } |
+ if (limit <= _maxUtf16) { |
+ return _TwoByteString._allocateFromTwoByteList(charCodes, start, end); |
+ } |
+ // TODO(lrn): Consider passing limit to _createFromCodePoints, because |
+ // the function is currently fully generic and doesn't know that its |
+ // charCodes are not all Latin-1 or Utf-16. |
+ return _createFromCodePoints(charCodes, start, end); |
+ } |
+ |
+ 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); |
+ 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 { |
+ if (end < start) { |
+ throw new RangeError.range(end, start, charCodes.length); |
+ } |
+ 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); |
} |
+ 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]); |
@@ -162,6 +212,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"; |
@@ -1140,6 +1196,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); |
} |