diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ec7d68ad1d..8571676399f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ * `dart:core` * `Uri` added `removeFragment` method. - + * `String.allMatches` (implementing `Pattern.allMatches`) is now lazy, + as all `allMatches` implementations are intended to be. * `dart:io` * `HttpClient` no longer sends URI fragments in the requeust. This is not allowed by the HTTP protocol. diff --git a/runtime/lib/string_patch.dart b/runtime/lib/string_patch.dart index 21a5ab0090f..eb1a2a29553 100644 --- a/runtime/lib/string_patch.dart +++ b/runtime/lib/string_patch.dart @@ -312,7 +312,7 @@ class _StringBase { int indexOf(Pattern pattern, [int start = 0]) { if ((start < 0) || (start > this.length)) { - throw new RangeError.range(start, 0, this.length); + throw new RangeError.range(start, 0, this.length, "start"); } if (pattern is String) { String other = pattern; @@ -844,26 +844,10 @@ class _StringBase { } Iterable allMatches(String string, [int start = 0]) { - List result = new List(); - int length = string.length; - int patternLength = this.length; - int startIndex = start; - while (true) { - int position = string.indexOf(this, startIndex); - if (position == -1) { - break; - } - result.add(new _StringMatch(position, string, this)); - int endIndex = position + patternLength; - if (endIndex == length) { - break; - } else if (position == endIndex) { - ++startIndex; // empty match, advance and restart - } else { - startIndex = endIndex; - } + if (start < 0 || start > string.length) { + throw new RangeError.range(start, 0, string.length, "start"); } - return result; + return new _StringAllMatchesIterable(string, this, start); } Match matchAsPrefix(String string, [int start = 0]) { @@ -1323,3 +1307,53 @@ class _StringMatch implements Match { final String input; final String pattern; } + + +class _StringAllMatchesIterable extends Iterable { + final String _input; + final String _pattern; + final int _index; + + _StringAllMatchesIterable(this._input, this._pattern, this._index); + + Iterator get iterator => + new _StringAllMatchesIterator(_input, _pattern, _index); + + Match get first { + int index = _input.indexOf(_pattern, _index); + if (index >= 0) { + return new _StringMatch(index, _input, _pattern); + } + throw IterableElementError.noElement(); + } +} + +class _StringAllMatchesIterator implements Iterator { + final String _input; + final String _pattern; + int _index; + Match _current; + + _StringAllMatchesIterator(this._input, this._pattern, this._index); + + bool moveNext() { + if (_index + _pattern.length > _input.length) { + _current = null; + return false; + } + var index = _input.indexOf(_pattern, _index); + if (index < 0) { + _index = _input.length + 1; + _current = null; + return false; + } + int end = index + _pattern.length; + _current = new _StringMatch(index, _input, _pattern); + // Empty match, don't start at same location again. + if (end == _index) end++; + _index = end; + return true; + } + + Match get current => _current; +} diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart index fd53e61d99b..7cd2c69d730 100644 --- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart +++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart @@ -57,7 +57,10 @@ import 'dart:_foreign_helper' show import 'dart:_interceptors'; import 'dart:_internal' as _symbol_dev; -import 'dart:_internal' show EfficientLength, MappedIterable; +import 'dart:_internal' show + EfficientLength, + MappedIterable, + IterableElementError; import 'dart:_native_typed_data'; @@ -148,7 +151,7 @@ bool builtinIsSubtype(type, String other) { @ForceInline() bool isDartFunctionTypeRti(Object type) { return JS_BUILTIN('returns:bool;effects:none;depends:none', - JsBuiltin.isGivenTypeRti, + JsBuiltin.isGivenTypeRti, type, JS_GET_NAME(JsGetName.FUNCTION_CLASS_TYPE_NAME)); } @@ -158,7 +161,7 @@ bool isDartFunctionTypeRti(Object type) { @ForceInline() bool isDartObjectTypeRti(type) { return JS_BUILTIN('returns:bool;effects:none;depends:none', - JsBuiltin.isGivenTypeRti, + JsBuiltin.isGivenTypeRti, type, JS_GET_NAME(JsGetName.OBJECT_CLASS_TYPE_NAME)); } @@ -168,7 +171,7 @@ bool isDartObjectTypeRti(type) { @ForceInline() bool isNullTypeRti(type) { return JS_BUILTIN('returns:bool;effects:none;depends:none', - JsBuiltin.isGivenTypeRti, + JsBuiltin.isGivenTypeRti, type, JS_GET_NAME(JsGetName.NULL_CLASS_TYPE_NAME)); } diff --git a/sdk/lib/_internal/js_runtime/lib/string_helper.dart b/sdk/lib/_internal/js_runtime/lib/string_helper.dart index 51b696bc708..9ce91f8a179 100644 --- a/sdk/lib/_internal/js_runtime/lib/string_helper.dart +++ b/sdk/lib/_internal/js_runtime/lib/string_helper.dart @@ -49,29 +49,58 @@ class StringMatch implements Match { final String pattern; } -List allMatchesInStringUnchecked(String pattern, String string, - int startIndex) { - // Copied from StringBase.allMatches in - // /runtime/lib/string_base.dart - List result = new List(); - int length = string.length; - int patternLength = pattern.length; - while (true) { - int position = stringIndexOfStringUnchecked(string, pattern, startIndex); - if (position == -1) { - break; - } - result.add(new StringMatch(position, string, pattern)); - int endIndex = position + patternLength; - if (endIndex == length) { - break; - } else if (position == endIndex) { - ++startIndex; // empty match, advance and restart - } else { - startIndex = endIndex; +Iterable allMatchesInStringUnchecked(String pattern, String string, + int startIndex) { + return new _StringAllMatchesIterable(string, pattern, startIndex); +} + +class _StringAllMatchesIterable extends Iterable { + final String _input; + final String _pattern; + final int _index; + + _StringAllMatchesIterable(this._input, this._pattern, this._index); + + Iterator get iterator => + new _StringAllMatchesIterator(_input, _pattern, _index); + + Match get first { + int index = stringIndexOfStringUnchecked(_input, _pattern, _index); + if (index >= 0) { + return new StringMatch(index, _input, _pattern); } + throw IterableElementError.noElement(); } - return result; +} + +class _StringAllMatchesIterator implements Iterator { + final String _input; + final String _pattern; + int _index; + Match _current; + + _StringAllMatchesIterator(this._input, this._pattern, this._index); + + bool moveNext() { + if (_index + _pattern.length > _input.length) { + _current = null; + return false; + } + var index = stringIndexOfStringUnchecked(_input, _pattern, _index); + if (index < 0) { + _index = _input.length + 1; + _current = null; + return false; + } + int end = index + _pattern.length; + _current = new StringMatch(index, _input, _pattern); + // Empty match, don't start at same location again. + if (end == _index) end++; + _index = end; + return true; + } + + Match get current => _current; } stringContainsUnchecked(receiver, other, startIndex) {