Chromium Code Reviews| Index: runtime/lib/string_patch.dart |
| diff --git a/runtime/lib/string_patch.dart b/runtime/lib/string_patch.dart |
| index 07f3818027962bca0c14cbef3775c71a07cb7a1c..614fcf6f9dc250bfe7f4b4bc6f5d714a70802357 100644 |
| --- a/runtime/lib/string_patch.dart |
| +++ b/runtime/lib/string_patch.dart |
| @@ -15,3 +15,488 @@ patch class StringImplementation { |
| return StringBase.concatAll(strings); |
| } |
| } |
| + |
| +/** |
| + * [StringBase] contains common methods used by concrete String implementations, |
| + * e.g., OneByteString. |
| + */ |
| +class StringBase { |
|
Mads Ager (google)
2012/08/27 12:55:03
If this is in a patch file, doesn't it have to be
|
| + |
| + factory StringBase._uninstantiable() { |
| + throw const UnsupportedOperationException( |
| + "StringBase can't be instaniated"); |
| + } |
| + |
| + int hashCode() native "String_hashCode"; |
| + |
| + /** |
| + * Create the most efficient string representation for specified |
| + * [codePoints]. |
| + */ |
| + static String createFromCharCodes(List<int> charCodes) { |
| + ObjectArray objectArray; |
| + if (charCodes is ObjectArray) { |
| + objectArray = charCodes; |
| + } else { |
| + int len = charCodes.length; |
| + objectArray = new ObjectArray(len); |
| + for (int i = 0; i < len; i++) { |
| + objectArray[i] = charCodes[i]; |
| + } |
| + } |
| + return _createFromCodePoints(objectArray); |
| + } |
| + |
| + static String _createFromCodePoints(ObjectArray<int> codePoints) |
| + native "StringBase_createFromCodePoints"; |
| + |
| + String operator [](int index) native "String_charAt"; |
| + |
| + int charCodeAt(int index) native "String_charCodeAt"; |
| + |
| + int get length() native "String_getLength"; |
| + |
| + bool isEmpty() { |
| + return this.length === 0; |
| + } |
| + |
| + String concat(String other) native "String_concat"; |
| + |
| + String toString() { |
| + return this; |
| + } |
| + |
| + bool operator ==(Object other) { |
| + if (this === other) { |
| + return true; |
| + } |
| + if ((other is !String) || |
| + (this.length != other.length)) { |
| + // TODO(5413632): Compare hash codes when both are present. |
| + return false; |
| + } |
| + return this.compareTo(other) === 0; |
| + } |
| + |
| + int compareTo(String other) { |
| + int thisLength = this.length; |
| + int otherLength = other.length; |
| + int len = (thisLength < otherLength) ? thisLength : otherLength; |
| + for (int i = 0; i < len; i++) { |
| + int thisCodePoint = this.charCodeAt(i); |
| + int otherCodePoint = other.charCodeAt(i); |
| + if (thisCodePoint < otherCodePoint) { |
| + return -1; |
| + } |
| + if (thisCodePoint > otherCodePoint) { |
| + return 1; |
| + } |
| + } |
| + if (thisLength < otherLength) return -1; |
| + if (thisLength > otherLength) return 1; |
| + return 0; |
| + } |
| + |
| + bool substringMatches(int start, String other) { |
| + if (other.isEmpty()) return true; |
| + if ((start < 0) || (start >= this.length)) { |
| + return false; |
| + } |
| + final int len = other.length; |
| + if ((start + len) > this.length) { |
| + return false; |
| + } |
| + for (int i = 0; i < len; i++) { |
| + if (this.charCodeAt(i + start) != other.charCodeAt(i)) { |
| + return false; |
| + } |
| + } |
| + return true; |
| + } |
| + |
| + bool endsWith(String other) { |
| + return this.substringMatches(this.length - other.length, other); |
| + } |
| + |
| + bool startsWith(String other) { |
| + return this.substringMatches(0, other); |
| + } |
| + |
| + int indexOf(String other, [int start = 0]) { |
| + if (other.isEmpty()) { |
| + return start < this.length ? start : this.length; |
| + } |
| + if ((start < 0) || (start >= this.length)) { |
| + return -1; |
| + } |
| + int len = this.length - other.length + 1; |
| + for (int index = start; index < len; index++) { |
| + if (this.substringMatches(index, other)) { |
| + return index; |
| + } |
| + } |
| + return -1; |
| + } |
| + |
| + int lastIndexOf(String other, [int start = null]) { |
| + if (start == null) start = length - 1; |
| + if (other.isEmpty()) { |
| + return min(this.length, start); |
| + } |
| + if (start >= this.length) { |
| + start = this.length - 1; |
| + } |
| + for (int index = start; index >= 0; index--) { |
| + if (this.substringMatches(index, other)) { |
| + return index; |
| + } |
| + } |
| + return -1; |
| + } |
| + |
| + String substring(int startIndex, [int endIndex]) { |
| + if (endIndex === null) endIndex = this.length; |
| + |
| + if ((startIndex < 0) || (startIndex > this.length)) { |
| + throw new IndexOutOfRangeException(startIndex); |
| + } |
| + if ((endIndex < 0) || (endIndex > this.length)) { |
| + throw new IndexOutOfRangeException(endIndex); |
| + } |
| + if (startIndex > endIndex) { |
| + throw new IndexOutOfRangeException(startIndex); |
| + } |
| + return _substringUnchecked(startIndex, endIndex); |
| + } |
| + |
| + String _substringUnchecked(int startIndex, int endIndex) |
| + native "StringBase_substringUnchecked"; |
| + |
| + String trim() { |
| + final int len = this.length; |
| + int first = 0; |
| + for (; first < len; first++) { |
| + if (!_isWhitespace(this.charCodeAt(first))) { |
| + break; |
| + } |
| + } |
| + if (len == first) { |
| + // String contains only whitespaces. |
| + return ""; |
| + } |
| + int last = len - 1; |
| + for (; last >= first; last--) { |
| + if (!_isWhitespace(this.charCodeAt(last))) { |
| + break; |
| + } |
| + } |
| + if ((first == 0) && (last == (len - 1))) { |
| + // Returns this string if it does not have leading or trailing |
| + // whitespaces. |
| + return this; |
| + } else { |
| + return _substringUnchecked(first, last + 1); |
| + } |
| + } |
| + |
| + bool contains(Pattern pattern, [int startIndex = 0]) { |
| + if (pattern is String) { |
| + return indexOf(pattern, startIndex) >= 0; |
| + } |
| + return pattern.allMatches(this.substring(startIndex)).iterator().hasNext(); |
| + } |
| + |
| + String replaceFirst(Pattern pattern, String replacement) { |
| + if (pattern is! Pattern) { |
| + throw new IllegalArgumentException("${pattern} is not a Pattern"); |
| + } |
| + if (replacement is! String) { |
| + throw new IllegalArgumentException("${replacement} is not a String"); |
| + } |
| + StringBuffer buffer = new StringBuffer(); |
| + int startIndex = 0; |
| + Iterator iterator = pattern.allMatches(this).iterator(); |
| + if (iterator.hasNext()) { |
| + Match match = iterator.next(); |
| + buffer.add(this.substring(startIndex, match.start())).add(replacement); |
| + startIndex = match.end(); |
| + } |
| + return buffer.add(this.substring(startIndex)).toString(); |
| + } |
| + |
| + String replaceAll(Pattern pattern, String replacement) { |
| + if (pattern is! Pattern) { |
| + throw new IllegalArgumentException("${pattern} is not a Pattern"); |
| + } |
| + if (replacement is! String) { |
| + throw new IllegalArgumentException("${replacement} is not a String"); |
| + } |
| + StringBuffer buffer = new StringBuffer(); |
| + int startIndex = 0; |
| + for (Match match in pattern.allMatches(this)) { |
| + buffer.add(this.substring(startIndex, match.start())).add(replacement); |
| + startIndex = match.end(); |
| + } |
| + return buffer.add(this.substring(startIndex)).toString(); |
| + } |
| + |
| + /** |
| + * Convert all objects in [values] to strings and concat them |
| + * into a result string. |
| + */ |
| + static String _interpolate(List values) { |
| + int numValues = values.length; |
| + var stringList = new ObjectArray(numValues); |
| + for (int i = 0; i < numValues; i++) { |
| + stringList[i] = values[i].toString(); |
| + } |
| + return _concatAll(stringList); |
| + } |
| + |
| + Iterable<Match> allMatches(String str) { |
| + List<Match> result = new List<Match>(); |
| + int length = str.length; |
| + int patternLength = this.length; |
| + int startIndex = 0; |
| + while (true) { |
| + int position = str.indexOf(this, startIndex); |
| + if (position == -1) { |
| + break; |
| + } |
| + result.add(new _StringMatch(position, str, this)); |
| + int endIndex = position + patternLength; |
| + if (endIndex == length) { |
| + break; |
| + } else if (position == endIndex) { |
| + ++startIndex; // empty match, advance and restart |
| + } else { |
| + startIndex = endIndex; |
| + } |
| + } |
| + return result; |
| + } |
| + |
| + List<String> split(Pattern pattern) { |
| + int length = this.length; |
| + Iterator iterator = pattern.allMatches(this).iterator(); |
| + if (length == 0 && iterator.hasNext()) { |
| + // A matched empty string input returns the empty list. |
| + return <String>[]; |
| + } |
| + List<String> result = new List<String>(); |
| + int startIndex = 0; |
| + int previousIndex = 0; |
| + while (true) { |
| + if (startIndex == length || !iterator.hasNext()) { |
| + result.add(this.substring(previousIndex, length)); |
| + break; |
| + } |
| + Match match = iterator.next(); |
| + if (match.start() == length) { |
| + result.add(this.substring(previousIndex, length)); |
| + break; |
| + } |
| + int endIndex = match.end(); |
| + if (startIndex == endIndex && endIndex == previousIndex) { |
| + ++startIndex; // empty match, advance and restart |
| + continue; |
| + } |
| + result.add(this.substring(previousIndex, match.start())); |
| + startIndex = previousIndex = endIndex; |
| + } |
| + return result; |
| + } |
| + |
| + List<String> splitChars() { |
| + int len = this.length; |
| + final result = new List<String>(len); |
| + for (int i = 0; i < len; i++) { |
| + result[i] = this[i]; |
| + } |
| + return result; |
| + } |
| + |
| + List<int> charCodes() { |
| + int len = this.length; |
| + final result = new List<int>(len); |
| + for (int i = 0; i < len; i++) { |
| + result[i] = this.charCodeAt(i); |
| + } |
| + return result; |
| + } |
| + |
| + String toUpperCase() native "String_toUpperCase"; |
| + |
| + String toLowerCase() native "String_toLowerCase"; |
| + |
| + // Implementations of Strings methods follow below. |
| + static String join(List<String> strings, String separator) { |
| + final int length = strings.length; |
| + if (length === 0) { |
| + return ""; |
| + } |
| + |
| + List stringsList = strings; |
| + if (separator.length != 0) { |
| + stringsList = new List(2 * length - 1); |
| + stringsList[0] = strings[0]; |
| + int j = 1; |
| + for (int i = 1; i < length; i++) { |
| + stringsList[j++] = separator; |
| + stringsList[j++] = strings[i]; |
| + } |
| + } |
| + return concatAll(stringsList); |
| + } |
| + |
| + static String concatAll(List<String> strings) { |
| + ObjectArray stringsArray; |
| + if (strings is ObjectArray) { |
| + stringsArray = strings; |
| + } else { |
| + int len = strings.length; |
| + stringsArray = new ObjectArray(len); |
| + for (int i = 0; i < len; i++) { |
| + stringsArray[i] = strings[i]; |
| + } |
| + } |
| + return _concatAll(stringsArray); |
| + } |
| + |
| + static String _concatAll(ObjectArray<String> strings) |
| + native "Strings_concatAll"; |
| +} |
| + |
| + |
| +class OneByteString extends StringBase implements String { |
| + factory OneByteString._uninstantiable() { |
| + throw const UnsupportedOperationException( |
| + "OneByteString can only be allocated by the VM"); |
| + } |
| + |
| + // Checks for one-byte whitespaces only. |
| + // TODO(srdjan): Investigate if 0x85 (NEL) and 0xA0 (NBSP) are valid |
| + // whitespaces for one byte strings. |
| + bool _isWhitespace(int codePoint) { |
| + return |
| + (codePoint === 32) || // Space. |
| + ((9 <= codePoint) && (codePoint <= 13)); // CR, LF, TAB, etc. |
| + } |
| + |
| +} |
| + |
| + |
| +class TwoByteString extends StringBase implements String { |
| + factory TwoByteString._uninstantiable() { |
| + throw const UnsupportedOperationException( |
| + "TwoByteString can only be allocated by the VM"); |
| + } |
| + |
| + // Checks for one-byte whitespaces only. |
| + // TODO(srdjan): Investigate if 0x85 (NEL) and 0xA0 (NBSP) are valid |
| + // whitespaces. Add checking for multi-byte whitespace codepoints. |
| + bool _isWhitespace(int codePoint) { |
| + return |
| + (codePoint === 32) || // Space. |
| + ((9 <= codePoint) && (codePoint <= 13)); // CR, LF, TAB, etc. |
| + } |
| +} |
| + |
| + |
| +class FourByteString extends StringBase implements String { |
| + factory FourByteString._uninstantiable() { |
| + throw const UnsupportedOperationException( |
| + "FourByteString can only be allocated by the VM"); |
| + } |
| + |
| + // Checks for one-byte whitespaces only. |
| + // TODO(srdjan): Investigate if 0x85 (NEL) and 0xA0 (NBSP) are valid |
| + // whitespaces. Add checking for multi-byte whitespace codepoints. |
| + bool _isWhitespace(int codePoint) { |
| + return |
| + (codePoint === 32) || // Space. |
| + ((9 <= codePoint) && (codePoint <= 13)); // CR, LF, TAB, etc. |
| + } |
| +} |
| + |
| + |
| +class ExternalOneByteString extends StringBase implements String { |
| + factory ExternalOneByteString._uninstantiable() { |
| + throw const UnsupportedOperationException( |
| + "ExternalOneByteString can only be allocated by the VM"); |
| + } |
| + |
| + // Checks for one-byte whitespaces only. |
| + // TODO(srdjan): Investigate if 0x85 (NEL) and 0xA0 (NBSP) are valid |
| + // whitespaces for one byte strings. |
| + bool _isWhitespace(int codePoint) { |
| + return |
| + (codePoint === 32) || // Space. |
| + ((9 <= codePoint) && (codePoint <= 13)); // CR, LF, TAB, etc. |
| + } |
| +} |
| + |
| + |
| +class ExternalTwoByteString extends StringBase implements String { |
| + factory ExternalTwoByteString._uninstantiable() { |
| + throw const UnsupportedOperationException( |
| + "ExternalTwoByteString can only be allocated by the VM"); |
| + } |
| + |
| + // Checks for one-byte whitespaces only. |
| + // TODO(srdjan): Investigate if 0x85 (NEL) and 0xA0 (NBSP) are valid |
| + // whitespaces. Add checking for multi-byte whitespace codepoints. |
| + bool _isWhitespace(int codePoint) { |
| + return |
| + (codePoint === 32) || // Space. |
| + ((9 <= codePoint) && (codePoint <= 13)); // CR, LF, TAB, etc. |
| + } |
| +} |
| + |
| + |
| +class ExternalFourByteString extends StringBase implements String { |
| + factory ExternalFourByteString._uninstantiable() { |
| + throw const UnsupportedOperationException( |
| + "ExternalFourByteString can only be allocated by the VM"); |
| + } |
| + |
| + // Checks for one-byte whitespaces only. |
| + // TODO(srdjan): Investigate if 0x85 (NEL) and 0xA0 (NBSP) are valid |
| + // whitespaces. Add checking for multi-byte whitespace codepoints. |
| + bool _isWhitespace(int codePoint) { |
| + return |
| + (codePoint === 32) || // Space. |
| + ((9 <= codePoint) && (codePoint <= 13)); // CR, LF, TAB, etc. |
| + } |
| +} |
| + |
| + |
| +class _StringMatch implements Match { |
| + const _StringMatch(int this._start, |
| + String this.str, |
| + String this.pattern); |
| + |
| + int start() => _start; |
| + int end() => _start + pattern.length; |
| + String operator[](int g) => group(g); |
| + int groupCount() => 0; |
| + |
| + String group(int group) { |
| + if (group != 0) { |
| + throw new IndexOutOfRangeException(group); |
| + } |
| + return pattern; |
| + } |
| + |
| + List<String> groups(List<int> groups) { |
| + List<String> result = new List<String>(); |
| + for (int g in groups) { |
| + result.add(group(g)); |
| + } |
| + return result; |
| + } |
| + |
| + final int _start; |
| + final String str; |
| + final String pattern; |
| +} |