1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:collection';
6
7// COMMON SIGNATURES
8
9/// Signature for callbacks that report that an underlying value has changed.
10///
11/// See also:
12///
13/// * [ValueSetter], for callbacks that report that a value has been set.
14typedef ValueChanged<T> = void Function(T value);
15
16/// Signature for callbacks that report that a value has been set.
17///
18/// This is the same signature as [ValueChanged], but is used when the
19/// callback is called even if the underlying value has not changed.
20/// For example, service extensions use this callback because they
21/// call the callback whenever the extension is called with a
22/// value, regardless of whether the given value is new or not.
23///
24/// See also:
25///
26/// * [ValueGetter], the getter equivalent of this signature.
27/// * [AsyncValueSetter], an asynchronous version of this signature.
28typedef ValueSetter<T> = void Function(T value);
29
30/// Signature for callbacks that are to report a value on demand.
31///
32/// See also:
33///
34/// * [ValueSetter], the setter equivalent of this signature.
35/// * [AsyncValueGetter], an asynchronous version of this signature.
36typedef ValueGetter<T> = T Function();
37
38/// Signature for callbacks that filter an iterable.
39typedef IterableFilter<T> = Iterable<T> Function(Iterable<T> input);
40
41/// Signature of callbacks that have no arguments and return no data, but that
42/// return a [Future] to indicate when their work is complete.
43///
44/// See also:
45///
46/// * [VoidCallback], a synchronous version of this signature.
47/// * [AsyncValueGetter], a signature for asynchronous getters.
48/// * [AsyncValueSetter], a signature for asynchronous setters.
49typedef AsyncCallback = Future<void> Function();
50
51/// Signature for callbacks that report that a value has been set and return a
52/// [Future] that completes when the value has been saved.
53///
54/// See also:
55///
56/// * [ValueSetter], a synchronous version of this signature.
57/// * [AsyncValueGetter], the getter equivalent of this signature.
58typedef AsyncValueSetter<T> = Future<void> Function(T value);
59
60/// Signature for callbacks that are to asynchronously report a value on demand.
61///
62/// See also:
63///
64/// * [ValueGetter], a synchronous version of this signature.
65/// * [AsyncValueSetter], the setter equivalent of this signature.
66typedef AsyncValueGetter<T> = Future<T> Function();
67
68// LAZY CACHING ITERATOR
69
70/// A lazy caching version of [Iterable].
71///
72/// This iterable is efficient in the following ways:
73///
74/// * It will not walk the given iterator more than you ask for.
75///
76/// * If you use it twice (e.g. you check [isNotEmpty], then
77/// use [single]), it will only walk the given iterator
78/// once. This caching will even work efficiently if you are
79/// running two side-by-side iterators on the same iterable.
80///
81/// * [toList] uses its EfficientLength variant to create its
82/// list quickly.
83///
84/// It is inefficient in the following ways:
85///
86/// * The first iteration through has caching overhead.
87///
88/// * It requires more memory than a non-caching iterator.
89///
90/// * The [length] and [toList] properties immediately pre-cache the
91/// entire list. Using these fields therefore loses the laziness of
92/// the iterable. However, it still gets cached.
93///
94/// The caching behavior is propagated to the iterators that are
95/// created by [map], [where], [expand], [take], [takeWhile], [skip],
96/// and [skipWhile], and is used by the built-in methods that use an
97/// iterator like [isNotEmpty] and [single].
98///
99/// Because a CachingIterable only walks the underlying data once, it
100/// cannot be used multiple times with the underlying data changing
101/// between each use. You must create a new iterable each time. This
102/// also applies to any iterables derived from this one, e.g. as
103/// returned by `where`.
104class CachingIterable<E> extends IterableBase<E> {
105 /// Creates a [CachingIterable] using the given [Iterator] as the source of
106 /// data. The iterator must not throw exceptions.
107 ///
108 /// Since the argument is an [Iterator], not an [Iterable], it is
109 /// guaranteed that the underlying data set will only be walked
110 /// once. If you have an [Iterable], you can pass its [iterator]
111 /// field as the argument to this constructor.
112 ///
113 /// You can this with an existing `sync*` function as follows:
114 ///
115 /// ```dart
116 /// Iterable<int> range(int start, int end) sync* {
117 /// for (int index = start; index <= end; index += 1) {
118 /// yield index;
119 /// }
120 /// }
121 ///
122 /// Iterable<int> i = CachingIterable<int>(range(1, 5).iterator);
123 /// print(i.length); // walks the list
124 /// print(i.length); // efficient
125 /// ```
126 ///
127 /// Beware that this will eagerly evaluate the `range` iterable, and because
128 /// of that it would be better to just implement `range` as something that
129 /// returns a `List` to begin with if possible.
130 CachingIterable(this._prefillIterator);
131
132 final Iterator<E> _prefillIterator;
133 final List<E> _results = <E>[];
134
135 @override
136 Iterator<E> get iterator {
137 return _LazyListIterator<E>(this);
138 }
139
140 @override
141 Iterable<T> map<T>(T Function(E e) toElement) {
142 return CachingIterable<T>(super.map<T>(toElement).iterator);
143 }
144
145 @override
146 Iterable<E> where(bool Function(E element) test) {
147 return CachingIterable<E>(super.where(test).iterator);
148 }
149
150 @override
151 Iterable<T> expand<T>(Iterable<T> Function(E element) toElements) {
152 return CachingIterable<T>(super.expand<T>(toElements).iterator);
153 }
154
155 @override
156 Iterable<E> take(int count) {
157 return CachingIterable<E>(super.take(count).iterator);
158 }
159
160 @override
161 Iterable<E> takeWhile(bool Function(E value) test) {
162 return CachingIterable<E>(super.takeWhile(test).iterator);
163 }
164
165 @override
166 Iterable<E> skip(int count) {
167 return CachingIterable<E>(super.skip(count).iterator);
168 }
169
170 @override
171 Iterable<E> skipWhile(bool Function(E value) test) {
172 return CachingIterable<E>(super.skipWhile(test).iterator);
173 }
174
175 @override
176 int get length {
177 _precacheEntireList();
178 return _results.length;
179 }
180
181 @override
182 List<E> toList({ bool growable = true }) {
183 _precacheEntireList();
184 return List<E>.of(_results, growable: growable);
185 }
186
187 void _precacheEntireList() {
188 while (_fillNext()) { }
189 }
190
191 bool _fillNext() {
192 if (!_prefillIterator.moveNext()) {
193 return false;
194 }
195 _results.add(_prefillIterator.current);
196 return true;
197 }
198}
199
200class _LazyListIterator<E> implements Iterator<E> {
201 _LazyListIterator(this._owner) : _index = -1;
202
203 final CachingIterable<E> _owner;
204 int _index;
205
206 @override
207 E get current {
208 assert(_index >= 0); // called "current" before "moveNext()"
209 if (_index < 0 || _index == _owner._results.length) {
210 throw StateError('current can not be call after moveNext has returned false');
211 }
212 return _owner._results[_index];
213 }
214
215 @override
216 bool moveNext() {
217 if (_index >= _owner._results.length) {
218 return false;
219 }
220 _index += 1;
221 if (_index == _owner._results.length) {
222 return _owner._fillNext();
223 }
224 return true;
225 }
226}
227
228/// A factory interface that also reports the type of the created objects.
229class Factory<T> {
230 /// Creates a new factory.
231 const Factory(this.constructor);
232
233 /// Creates a new object of type T.
234 final ValueGetter<T> constructor;
235
236 /// The type of the objects created by this factory.
237 Type get type => T;
238
239 @override
240 String toString() {
241 return 'Factory(type: $type)';
242 }
243}
244
245/// Linearly interpolate between two `Duration`s.
246Duration lerpDuration(Duration a, Duration b, double t) {
247 return Duration(
248 microseconds: (a.inMicroseconds + (b.inMicroseconds - a.inMicroseconds) * t).round(),
249 );
250}
251