// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file // 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. part of "dart:_internal"; /** * Marker interface for [Iterable] subclasses that have an efficient * [length] implementation. */ abstract class EfficientLengthIterable extends Iterable { const EfficientLengthIterable(); /** * Returns the number of elements in the iterable. * * This is an efficient operation that doesn't require iterating through * the elements. */ int get length; } /// An interface which hides [EfficientLengthIterable] from upper bounds. /// /// Every type which implements [EfficientLengthIterable] also implements /// this interface, and they have the same *depth*, so it's impossible /// for the upper-bound algorithm to get [EfficientLengthIterable] /// as the result. abstract interface class HideEfficientLengthIterable implements Iterable {} /** * An [Iterable] for classes that have efficient [length] and [elementAt]. * * All other methods are implemented in terms of [length] and [elementAt], * including [iterator]. */ abstract class ListIterable extends EfficientLengthIterable implements HideEfficientLengthIterable { int get length; E elementAt(int i); const ListIterable(); Iterator get iterator => ListIterator(this); void forEach(void action(E element)) { int length = this.length; for (int i = 0; i < length; i++) { action(elementAt(i)); if (length != this.length) { throw ConcurrentModificationError(this); } } } bool get isEmpty => length == 0; E get first { if (length == 0) throw IterableElementError.noElement(); return elementAt(0); } E get last { if (length == 0) throw IterableElementError.noElement(); return elementAt(length - 1); } E get single { if (length == 0) throw IterableElementError.noElement(); if (length > 1) throw IterableElementError.tooMany(); return elementAt(0); } bool contains(Object? element) { int length = this.length; for (int i = 0; i < length; i++) { if (elementAt(i) == element) return true; if (length != this.length) { throw ConcurrentModificationError(this); } } return false; } bool every(bool test(E element)) { int length = this.length; for (int i = 0; i < length; i++) { if (!test(elementAt(i))) return false; if (length != this.length) { throw ConcurrentModificationError(this); } } return true; } bool any(bool test(E element)) { int length = this.length; for (int i = 0; i < length; i++) { if (test(elementAt(i))) return true; if (length != this.length) { throw ConcurrentModificationError(this); } } return false; } E firstWhere(bool test(E element), {E Function()? orElse}) { int length = this.length; for (int i = 0; i < length; i++) { E element = elementAt(i); if (test(element)) return element; if (length != this.length) { throw ConcurrentModificationError(this); } } if (orElse != null) return orElse(); throw IterableElementError.noElement(); } E lastWhere(bool test(E element), {E Function()? orElse}) { int length = this.length; for (int i = length - 1; i >= 0; i--) { E element = elementAt(i); if (test(element)) return element; if (length != this.length) { throw ConcurrentModificationError(this); } } if (orElse != null) return orElse(); throw IterableElementError.noElement(); } E singleWhere(bool test(E element), {E Function()? orElse}) { int length = this.length; late E match; bool matchFound = false; for (int i = 0; i < length; i++) { E element = elementAt(i); if (test(element)) { if (matchFound) { throw IterableElementError.tooMany(); } matchFound = true; match = element; } if (length != this.length) { throw ConcurrentModificationError(this); } } if (matchFound) return match; if (orElse != null) return orElse(); throw IterableElementError.noElement(); } String join([String separator = ""]) { int length = this.length; if (!separator.isEmpty) { if (length == 0) return ""; String first = "${elementAt(0)}"; if (length != this.length) { throw ConcurrentModificationError(this); } StringBuffer buffer = StringBuffer(first); for (int i = 1; i < length; i++) { buffer.write(separator); buffer.write(elementAt(i)); if (length != this.length) { throw ConcurrentModificationError(this); } } return buffer.toString(); } else { StringBuffer buffer = StringBuffer(); for (int i = 0; i < length; i++) { buffer.write(elementAt(i)); if (length != this.length) { throw ConcurrentModificationError(this); } } return buffer.toString(); } } Iterable where(bool test(E element)) => super.where(test); Iterable map(T toElement(E element)) => MappedListIterable(this, toElement); E reduce(E combine(E value, E element)) { int length = this.length; if (length == 0) throw IterableElementError.noElement(); E value = elementAt(0); for (int i = 1; i < length; i++) { value = combine(value, elementAt(i)); if (length != this.length) { throw ConcurrentModificationError(this); } } return value; } T fold(T initialValue, T combine(T previousValue, E element)) { var value = initialValue; int length = this.length; for (int i = 0; i < length; i++) { value = combine(value, elementAt(i)); if (length != this.length) { throw ConcurrentModificationError(this); } } return value; } Iterable skip(int count) => SubListIterable(this, count, null); Iterable skipWhile(bool test(E element)) => super.skipWhile(test); Iterable take(int count) => SubListIterable(this, 0, checkNotNullable(count, "count")); Iterable takeWhile(bool test(E element)) => super.takeWhile(test); List toList({bool growable = true}) => List.of(this, growable: growable); Set toSet() { Set result = Set(); for (int i = 0; i < length; i++) { result.add(elementAt(i)); } return result; } } base class SubListIterable extends ListIterable { final Iterable _iterable; // Has efficient length and elementAt. final int _start; /** If null, represents the length of the iterable. */ final int? _endOrLength; /// Returns `_iterable` for for internal code. static Iterable iterableOf(SubListIterable subListIterable) => subListIterable._iterable; /// Returns `_start` for for internal code. static int startOf(SubListIterable subListIterable) => subListIterable._start; SubListIterable(this._iterable, this._start, this._endOrLength) { RangeError.checkNotNegative(_start, "start"); int? endOrLength = _endOrLength; if (endOrLength != null) { RangeError.checkNotNegative(endOrLength, "end"); if (_start > endOrLength) { throw RangeError.range(_start, 0, endOrLength, "start"); } } } int get _endIndex { int length = _iterable.length; int? endOrLength = _endOrLength; if (endOrLength == null || endOrLength > length) return length; return endOrLength; } int get _startIndex { int length = _iterable.length; if (_start > length) return length; return _start; } int get length { int length = _iterable.length; if (_start >= length) return 0; int? endOrLength = _endOrLength; if (endOrLength == null || endOrLength >= length) { return length - _start; } return endOrLength - _start; } E elementAt(int index) { int realIndex = _startIndex + index; if (index < 0 || realIndex >= _endIndex) { throw IndexError.withLength( index, length, indexable: this, name: "index", ); } return _iterable.elementAt(realIndex); } Iterable skip(int count) { RangeError.checkNotNegative(count, "count"); int newStart = _start + count; int? endOrLength = _endOrLength; if (endOrLength != null && newStart >= endOrLength) { return EmptyIterable(); } return SubListIterable(_iterable, newStart, _endOrLength); } Iterable take(int count) { RangeError.checkNotNegative(count, "count"); int? endOrLength = _endOrLength; if (endOrLength == null) { return SubListIterable(_iterable, _start, _start + count); } else { int newEnd = _start + count; if (endOrLength < newEnd) return this; return SubListIterable(_iterable, _start, newEnd); } } List toList({bool growable = true}) { int start = _start; int end = _iterable.length; int? endOrLength = _endOrLength; if (endOrLength != null && endOrLength < end) end = endOrLength; int length = end - start; if (length <= 0) return List.empty(growable: growable); List result = List.filled( length, _iterable.elementAt(start), growable: growable, ); for (int i = 1; i < length; i++) { result[i] = _iterable.elementAt(start + i); if (_iterable.length < end) throw ConcurrentModificationError(this); } return result; } } /** * An [Iterator] that iterates a list-like [Iterable]. * * All iterations is done in terms of [Iterable.length] and * [Iterable.elementAt]. These operations are fast for list-like * iterables. */ class ListIterator implements Iterator { final Iterable _iterable; final int _length; int _index; E? _current; @pragma("wasm:prefer-inline") ListIterator(Iterable iterable) : _iterable = iterable, _length = iterable.length, _index = 0; E get current => _current as E; @pragma("vm:prefer-inline") @pragma("wasm:prefer-inline") bool moveNext() { int length = _iterable.length; if (_length != length) { throw ConcurrentModificationError(_iterable); } if (_index >= length) { _current = null; return false; } _current = _iterable.elementAt(_index); _index++; return true; } } typedef T _Transformation(S value); class MappedIterable extends Iterable { final Iterable _iterable; final _Transformation _f; factory MappedIterable(Iterable iterable, T function(S value)) { if (iterable is EfficientLengthIterable) { return EfficientLengthMappedIterable(iterable, function); } return MappedIterable._(iterable, function); } MappedIterable._(this._iterable, this._f); Iterator get iterator => MappedIterator(_iterable.iterator, _f); // Length related functions are independent of the mapping. int get length => _iterable.length; bool get isEmpty => _iterable.isEmpty; // Index based lookup can be done before transforming. T get first => _f(_iterable.first); T get last => _f(_iterable.last); T get single => _f(_iterable.single); T elementAt(int index) => _f(_iterable.elementAt(index)); } class EfficientLengthMappedIterable extends MappedIterable implements EfficientLengthIterable, HideEfficientLengthIterable { EfficientLengthMappedIterable(Iterable iterable, T function(S value)) : super._(iterable, function); } class MappedIterator implements Iterator { T? _current; final Iterator _iterator; final _Transformation _f; MappedIterator(this._iterator, this._f); bool moveNext() { if (_iterator.moveNext()) { _current = _f(_iterator.current); return true; } _current = null; return false; } T get current => _current as T; } /** * Specialized alternative to [MappedIterable] for mapped [List]s. * * Expects efficient `length` and `elementAt` on the source iterable. */ base class MappedListIterable extends ListIterable { final Iterable _source; final _Transformation _f; MappedListIterable(this._source, this._f); int get length => _source.length; T elementAt(int index) => _f(_source.elementAt(index)); } typedef bool _ElementPredicate(E element); class WhereIterable extends Iterable { final Iterable _iterable; final _ElementPredicate _f; WhereIterable(this._iterable, this._f); Iterator get iterator => WhereIterator(_iterable.iterator, _f); // Specialization of [Iterable.map] to non-EfficientLengthIterable. Iterable map(T toElement(E element)) => MappedIterable._(this, toElement); } class WhereIterator implements Iterator { final Iterator _iterator; final _ElementPredicate _f; WhereIterator(this._iterator, this._f); bool moveNext() { while (_iterator.moveNext()) { if (_f(_iterator.current)) { return true; } } return false; } E get current => _iterator.current; } typedef Iterable _ExpandFunction(S sourceElement); class ExpandIterable extends Iterable { final Iterable _iterable; final _ExpandFunction _f; ExpandIterable(this._iterable, this._f); Iterator get iterator => ExpandIterator(_iterable.iterator, _f); } class ExpandIterator implements Iterator { final Iterator _iterator; final _ExpandFunction _f; // Initialize _currentExpansion to an empty iterable. A null value // marks the end of iteration, and we don't want to call _f before // the first moveNext call. Iterator? _currentExpansion = const EmptyIterator(); T? _current; ExpandIterator(this._iterator, this._f); T get current => _current as T; bool moveNext() { if (_currentExpansion == null) return false; while (!_currentExpansion!.moveNext()) { _current = null; if (_iterator.moveNext()) { // If _f throws, this ends iteration. Otherwise _currentExpansion and // _current will be set again below. _currentExpansion = null; _currentExpansion = _f(_iterator.current).iterator; } else { return false; } } _current = _currentExpansion!.current; return true; } } class TakeIterable extends Iterable { final Iterable _iterable; final int _takeCount; factory TakeIterable(Iterable iterable, int takeCount) { ArgumentError.checkNotNull(takeCount, "takeCount"); RangeError.checkNotNegative(takeCount, "takeCount"); if (iterable is EfficientLengthIterable) { return EfficientLengthTakeIterable(iterable, takeCount); } return TakeIterable._(iterable, takeCount); } TakeIterable._(this._iterable, this._takeCount); Iterator get iterator { return TakeIterator(_iterable.iterator, _takeCount); } } class EfficientLengthTakeIterable extends TakeIterable implements EfficientLengthIterable, HideEfficientLengthIterable { EfficientLengthTakeIterable(Iterable iterable, int takeCount) : super._(iterable, takeCount); int get length { int iterableLength = _iterable.length; if (iterableLength > _takeCount) return _takeCount; return iterableLength; } } class TakeIterator implements Iterator { final Iterator _iterator; int _remaining; TakeIterator(this._iterator, this._remaining) { assert(_remaining >= 0); } bool moveNext() { _remaining--; if (_remaining >= 0) { return _iterator.moveNext(); } _remaining = -1; return false; } E get current { // Before NNBD, this returned null when iteration was complete. In order to // avoid a hard breaking change, we return "null as E" in that case so that // if strong checking is not enabled or E is nullable, the existing // behavior is preserved. if (_remaining < 0) return null as E; return _iterator.current; } } class TakeWhileIterable extends Iterable { final Iterable _iterable; final _ElementPredicate _f; TakeWhileIterable(this._iterable, this._f); Iterator get iterator { return TakeWhileIterator(_iterable.iterator, _f); } } class TakeWhileIterator implements Iterator { final Iterator _iterator; final _ElementPredicate _f; bool _isFinished = false; TakeWhileIterator(this._iterator, this._f); bool moveNext() { if (_isFinished) return false; if (!_iterator.moveNext() || !_f(_iterator.current)) { _isFinished = true; return false; } return true; } E get current { if (_isFinished) return null as E; return _iterator.current; } } class SkipIterable extends Iterable { final Iterable _iterable; final int _skipCount; factory SkipIterable(Iterable iterable, int count) { if (iterable is EfficientLengthIterable) { return EfficientLengthSkipIterable(iterable, count); } return SkipIterable._(iterable, _checkCount(count)); } SkipIterable._(this._iterable, this._skipCount); Iterable skip(int count) { return SkipIterable._(_iterable, _skipCount + _checkCount(count)); } Iterator get iterator { return SkipIterator(_iterable.iterator, _skipCount); } } class EfficientLengthSkipIterable extends SkipIterable implements EfficientLengthIterable, HideEfficientLengthIterable { factory EfficientLengthSkipIterable(Iterable iterable, int count) { return EfficientLengthSkipIterable._(iterable, _checkCount(count)); } EfficientLengthSkipIterable._(Iterable iterable, int count) : super._(iterable, count); int get length { int length = _iterable.length - _skipCount; if (length >= 0) return length; return 0; } Iterable skip(int count) { return EfficientLengthSkipIterable._( _iterable, _skipCount + _checkCount(count), ); } } int _checkCount(int count) { ArgumentError.checkNotNull(count, "count"); RangeError.checkNotNegative(count, "count"); return count; } class SkipIterator implements Iterator { final Iterator _iterator; int _skipCount; SkipIterator(this._iterator, this._skipCount) { assert(_skipCount >= 0); } bool moveNext() { for (int i = 0; i < _skipCount; i++) _iterator.moveNext(); _skipCount = 0; return _iterator.moveNext(); } E get current => _iterator.current; } class SkipWhileIterable extends Iterable { final Iterable _iterable; final _ElementPredicate _f; SkipWhileIterable(this._iterable, this._f); Iterator get iterator { return SkipWhileIterator(_iterable.iterator, _f); } } class SkipWhileIterator implements Iterator { final Iterator _iterator; final _ElementPredicate _f; bool _hasSkipped = false; SkipWhileIterator(this._iterator, this._f); bool moveNext() { if (!_hasSkipped) { _hasSkipped = true; while (_iterator.moveNext()) { if (!_f(_iterator.current)) return true; } } return _iterator.moveNext(); } E get current => _iterator.current; } /** * The always empty [Iterable]. */ class EmptyIterable extends EfficientLengthIterable implements HideEfficientLengthIterable { const EmptyIterable(); Iterator get iterator => const EmptyIterator(); void forEach(void action(E element)) {} bool get isEmpty => true; int get length => 0; E get first { throw IterableElementError.noElement(); } E get last { throw IterableElementError.noElement(); } E get single { throw IterableElementError.noElement(); } E elementAt(int index) { throw RangeError.range(index, 0, 0, "index"); } bool contains(Object? element) => false; bool every(bool test(E element)) => true; bool any(bool test(E element)) => false; E firstWhere(bool test(E element), {E Function()? orElse}) { if (orElse != null) return orElse(); throw IterableElementError.noElement(); } E lastWhere(bool test(E element), {E Function()? orElse}) { if (orElse != null) return orElse(); throw IterableElementError.noElement(); } E singleWhere(bool test(E element), {E Function()? orElse}) { if (orElse != null) return orElse(); throw IterableElementError.noElement(); } String join([String separator = ""]) => ""; Iterable where(bool test(E element)) => this; Iterable map(T toElement(E element)) => EmptyIterable(); E reduce(E combine(E value, E element)) { throw IterableElementError.noElement(); } T fold(T initialValue, T combine(T previousValue, E element)) { return initialValue; } Iterable skip(int count) { RangeError.checkNotNegative(count, "count"); return this; } Iterable skipWhile(bool test(E element)) => this; Iterable take(int count) { RangeError.checkNotNegative(count, "count"); return this; } Iterable takeWhile(bool test(E element)) => this; List toList({bool growable = true}) => List.empty(growable: growable); Set toSet() => Set(); } /** The always empty iterator. */ class EmptyIterator implements Iterator { const EmptyIterator(); bool moveNext() => false; E get current { throw IterableElementError.noElement(); } } class FollowedByIterable extends Iterable { final Iterable _first; final Iterable _second; FollowedByIterable(this._first, this._second); factory FollowedByIterable.firstEfficient( EfficientLengthIterable first, Iterable second, ) { if (second is EfficientLengthIterable) { return EfficientLengthFollowedByIterable(first, second); } return FollowedByIterable(first, second); } Iterator get iterator => FollowedByIterator(_first, _second); int get length => _first.length + _second.length; bool get isEmpty => _first.isEmpty && _second.isEmpty; bool get isNotEmpty => _first.isNotEmpty || _second.isNotEmpty; // May be more efficient if either iterable is a Set. bool contains(Object? value) => _first.contains(value) || _second.contains(value); E get first { var iterator = _first.iterator; if (iterator.moveNext()) return iterator.current; return _second.first; } E get last { var iterator = _second.iterator; if (iterator.moveNext()) { E last = iterator.current; while (iterator.moveNext()) last = iterator.current; return last; } return _first.last; } // If linear sequences of `followedBy` becomes an issue, we can flatten // into a list of iterables instead of a tree or spine. } class EfficientLengthFollowedByIterable extends FollowedByIterable implements EfficientLengthIterable, HideEfficientLengthIterable { EfficientLengthFollowedByIterable( EfficientLengthIterable first, EfficientLengthIterable second, ) : super(first, second); E elementAt(int index) { int firstLength = _first.length; if (index < firstLength) return _first.elementAt(index); return _second.elementAt(index - firstLength); } E get first { if (_first.isNotEmpty) return _first.first; return _second.first; } E get last { if (_second.isNotEmpty) return _second.last; return _first.last; } } class FollowedByIterator implements Iterator { Iterator _currentIterator; Iterable? _nextIterable; FollowedByIterator(Iterable first, this._nextIterable) : _currentIterator = first.iterator; bool moveNext() { if (_currentIterator.moveNext()) return true; if (_nextIterable != null) { _currentIterator = _nextIterable!.iterator; _nextIterable = null; return _currentIterator.moveNext(); } return false; } E get current => _currentIterator.current; } class WhereTypeIterable extends Iterable { final Iterable _source; WhereTypeIterable(this._source); Iterator get iterator => WhereTypeIterator(_source.iterator); } class WhereTypeIterator implements Iterator { final Iterator _source; WhereTypeIterator(this._source); bool moveNext() { while (_source.moveNext()) { if (_source.current is T) return true; } return false; } T get current => _source.current as T; } /// Implementation of [NullableIterableExtensions.nonNulls]. /// /// A filtering iterable, so it doesn't have efficient length /// and cannot forward most methods to the underlying [_source]. class NonNullsIterable extends Iterable { final Iterable _source; NonNullsIterable(this._source); T? get _firstNonNull { for (var element in _source) { if (element != null) return element; } return null; } bool get isEmpty => _firstNonNull == null; bool get isNotEmpty => _firstNonNull != null; T get first => _firstNonNull ?? (throw IterableElementError.noElement()); Iterator get iterator => NonNullsIterator(_source.iterator); } class NonNullsIterator implements Iterator { final Iterator _source; T? _current; NonNullsIterator(this._source); bool moveNext() { _current = null; while (_source.moveNext()) { var next = _source.current; if (next != null) { _current = next; return true; } } return false; } T get current => _current ?? (throw IterableElementError.noElement()); } /// Implementation of [IterableExtensions.indexed]. /// /// Maps elements of [_source] one-to-one to record values, /// so has the same length as the original, and can define many /// operations in terms of the underlying source. class IndexedIterable extends Iterable<(int, T)> { final Iterable _source; /// Offset applied to indices. /// /// Used to implement `skip` efficiently for iterables which can skip /// efficiently. final int _start; @pragma('vm:prefer-inline') factory IndexedIterable(Iterable source, int start) { if (source is EfficientLengthIterable) { return EfficientLengthIndexedIterable(source, start); } return IndexedIterable._(source, start); } IndexedIterable.nonEfficientLength(Iterable source, int start) : this._(source, start); IndexedIterable._(this._source, this._start); int get length => _source.length; bool get isEmpty => _source.isEmpty; bool get isNotEmpty => _source.isNotEmpty; (int, T) get first => (_start, _source.first); (int, T) get single => (_start, _source.single); (int, T) elementAt(int index) => (index + _start, _source.elementAt(index)); bool contains(Object? element) { if (element case (int index, Object? other) when index >= _start) { // Try to find the `index`th element without looking at the // intermediate values, and without throwing if there are fewer. var unbiasedIndex = index - _start; var iterator = _source.skip(unbiasedIndex).iterator; return iterator.moveNext() && iterator.current == other; } return false; } Iterable<(int, T)> take(int count) => IndexedIterable.nonEfficientLength( _source.take(_checkCount(count)), _start, ); Iterable<(int, T)> skip(int count) => IndexedIterable.nonEfficientLength( _source.skip(_checkCount(count)), count + _start, ); @pragma('vm:prefer-inline') Iterator<(int, T)> get iterator => IndexedIterator(_source.iterator, _start); } class EfficientLengthIndexedIterable extends IndexedIterable implements EfficientLengthIterable<(int, T)>, HideEfficientLengthIterable<(int, T)> { EfficientLengthIndexedIterable(super._source, super._start) : super._(); (int, T) get last { var length = _source.length; if (length <= 0) throw IterableElementError.noElement(); var last = _source.last; if (length != this.length) { throw ConcurrentModificationError(this); } return (length - 1 + _start, last); } bool contains(Object? element) { if (element case (int index, Object? other) when index >= _start) { var unbiasedIndex = index - _start; return unbiasedIndex < _source.length && _source.elementAt(unbiasedIndex) == other; } return false; } Iterable<(int, T)> take(int count) => EfficientLengthIndexedIterable( _source.take(_checkCount(count)), _start, ); Iterable<(int, T)> skip(int count) => EfficientLengthIndexedIterable( _source.skip(_checkCount(count)), _start + count, ); } class IndexedIterator implements Iterator<(int, T)> { final Iterator _source; final int _start; int _index = -1; IndexedIterator(this._source, this._start); bool moveNext() { var index = ++_index; if (index >= 0 && _source.moveNext()) { return true; } _index = -2; // Ensures moveNext won't get called again. return false; } (int, T) get current => _index >= 0 ? (_start + _index, _source.current) : (throw IterableElementError.noElement()); } /** * Creates errors throw by [Iterable] when the element count is wrong. */ abstract class IterableElementError { /** Error thrown by, e.g., [Iterable.first] when there is no result. */ static StateError noElement() => StateError("No element"); /** Error thrown by, e.g., [Iterable.single] if there are too many results. */ static StateError tooMany() => StateError("Too many elements"); /** Error thrown by, e.g., [List.setRange] if there are too few elements. */ static StateError tooFew() => StateError("Too few elements"); }