Line data Source code
1 : // Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2 : // for details. All rights reserved. Use of this source code is governed by a
3 : // BSD-style license that can be found in the LICENSE file.
4 :
5 : import "dart:collection";
6 : import "dart:typed_data";
7 :
8 : import "package:collection/collection.dart";
9 :
10 : import 'typed_buffer.dart';
11 :
12 : /// The shared superclass of all the typed queue subclasses.
13 : abstract class _TypedQueue<E, L extends List<E>> with ListMixin<E> {
14 : /// The underlying data buffer.
15 : ///
16 : /// This is always both a List<E> and a TypedData, which we don't have a type
17 : /// for that. For example, for a `Uint8Queue`, this is a `Uint8List`.
18 : L _table;
19 :
20 : int _head;
21 : int _tail;
22 :
23 : /// Create an empty queue.
24 0 : _TypedQueue(List<E> table)
25 : : _table = table as L,
26 : _head = 0,
27 : _tail = 0;
28 :
29 : // Iterable interface.
30 :
31 0 : int get length => (_tail - _head) & (_table.length - 1);
32 :
33 0 : List<E> toList({bool growable = true}) {
34 0 : var list = growable ? _createBuffer(length) : _createList(length);
35 0 : _writeToList(list);
36 : return list;
37 : }
38 :
39 0 : QueueList<T> cast<T>() {
40 0 : if (this is QueueList<T>) return this as QueueList<T>;
41 0 : throw UnsupportedError("$this cannot be cast to the desired type.");
42 : }
43 :
44 0 : @deprecated
45 0 : QueueList<T> retype<T>() => cast<T>();
46 :
47 : // Queue interface.
48 :
49 0 : void addLast(E value) {
50 0 : _table[_tail] = value;
51 0 : _tail = (_tail + 1) & (_table.length - 1);
52 0 : if (_head == _tail) _growAtCapacity();
53 : }
54 :
55 0 : void addFirst(E value) {
56 0 : _head = (_head - 1) & (_table.length - 1);
57 0 : _table[_head] = value;
58 0 : if (_head == _tail) _growAtCapacity();
59 : }
60 :
61 0 : E removeFirst() {
62 0 : if (_head == _tail) throw StateError("No element");
63 0 : var result = _table[_head];
64 0 : _head = (_head + 1) & (_table.length - 1);
65 : return result;
66 : }
67 :
68 0 : E removeLast() {
69 0 : if (_head == _tail) throw StateError("No element");
70 0 : _tail = (_tail - 1) & (_table.length - 1);
71 0 : return _table[_tail];
72 : }
73 :
74 : // List interface.
75 :
76 0 : void add(E value) => addLast(value);
77 :
78 0 : set length(int value) {
79 0 : RangeError.checkNotNegative(value, "length");
80 :
81 0 : var delta = value - length;
82 0 : if (delta >= 0) {
83 0 : var needsToGrow = _table.length <= value;
84 0 : if (needsToGrow) _growTo(value);
85 0 : _tail = (_tail + delta) & (_table.length - 1);
86 :
87 : // If we didn't copy into a new table, make sure that we overwrite the
88 : // existing data so that users don't accidentally depend on it still
89 : // existing.
90 0 : if (!needsToGrow) fillRange(value - delta, value, _defaultValue);
91 : } else {
92 0 : removeRange(value, length);
93 : }
94 : }
95 :
96 0 : E operator [](int index) {
97 0 : RangeError.checkValidIndex(index, this, null, length);
98 0 : return _table[(_head + index) & (_table.length - 1)];
99 : }
100 :
101 0 : void operator []=(int index, E value) {
102 0 : RangeError.checkValidIndex(index, this);
103 0 : _table[(_head + index) & (_table.length - 1)] = value;
104 : }
105 :
106 0 : void removeRange(int start, int end) {
107 0 : var length = this.length;
108 0 : RangeError.checkValidRange(start, end, length);
109 :
110 : // Special-case removing an initial or final range because we can do it very
111 : // efficiently by adjusting `_head` or `_tail`.
112 0 : if (start == 0) {
113 0 : _head = (_head + end) & (_table.length - 1);
114 : return;
115 : }
116 :
117 0 : var elementsAfter = length - end;
118 0 : if (elementsAfter == 0) {
119 0 : _tail = (_head + start) & (_table.length - 1);
120 : return;
121 : }
122 :
123 : // Choose whether to copy from the beginning of the end of the queue based
124 : // on which will require fewer copied elements.
125 0 : var removedElements = end - start;
126 0 : if (start < elementsAfter) {
127 0 : setRange(removedElements, end, this);
128 0 : _head = (_head + removedElements) & (_table.length - 1);
129 : } else {
130 0 : setRange(start, length - removedElements, this, end);
131 0 : _tail = (_tail - removedElements) & (_table.length - 1);
132 : }
133 : }
134 :
135 0 : void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) {
136 0 : RangeError.checkValidRange(start, end, length);
137 0 : if (start == end) return;
138 :
139 0 : var targetStart = (_head + start) & (_table.length - 1);
140 0 : var targetEnd = (_head + end) & (_table.length - 1);
141 0 : var targetIsContiguous = targetStart < targetEnd;
142 : if (identical(iterable, this)) {
143 : // If we're copying this queue to itself, we can copy [_table] in directly
144 : // which requires some annoying case analysis but in return bottoms out on
145 : // an extremely efficient `memmove` call. However, we may need to do three
146 : // copies to avoid overwriting data we'll need to use later.
147 0 : var sourceStart = (_head + skipCount) & (_table.length - 1);
148 0 : var sourceEnd = (sourceStart + (end - start)) & (_table.length - 1);
149 0 : if (sourceStart == targetStart) return;
150 :
151 0 : var sourceIsContiguous = sourceStart < sourceEnd;
152 : if (targetIsContiguous && sourceIsContiguous) {
153 : // If both the source and destination ranges are contiguous, we can
154 : // do a single [setRange]. Hooray!
155 0 : _table.setRange(targetStart, targetEnd, _table, sourceStart);
156 : } else if (!targetIsContiguous && !sourceIsContiguous) {
157 : // If neither range is contiguous, we need to do three copies.
158 0 : if (sourceStart > targetStart) {
159 : // [=====| targetEnd targetStart |======]
160 : // [========| sourceEnd sourceStart |===]
161 :
162 : // Copy front to back.
163 0 : var startGap = sourceStart - targetStart;
164 0 : var firstEnd = _table.length - startGap;
165 0 : _table.setRange(targetStart, firstEnd, _table, sourceStart);
166 0 : _table.setRange(firstEnd, _table.length, _table);
167 0 : _table.setRange(0, targetEnd, _table, startGap);
168 0 : } else if (sourceEnd < targetEnd) {
169 : // [=====| targetEnd targetStart |======]
170 : // [==| sourceEnd sourceStart |=========]
171 :
172 : // Copy back to front.
173 0 : var firstStart = targetEnd - sourceEnd;
174 0 : _table.setRange(firstStart, targetEnd, _table);
175 0 : _table.setRange(0, firstStart, _table, _table.length - firstStart);
176 0 : _table.setRange(targetStart, _table.length, _table, sourceStart);
177 : }
178 0 : } else if (sourceStart < targetEnd) {
179 : // Copying twice is safe here as long as we copy front to back.
180 : if (sourceIsContiguous) {
181 : // [=====| targetEnd targetStart |======]
182 : // [ |===========| sourceEnd ]
183 : // sourceStart
184 0 : _table.setRange(targetStart, _table.length, _table, sourceStart);
185 0 : _table.setRange(0, targetEnd, _table,
186 0 : sourceStart + (_table.length - targetStart));
187 : } else {
188 : // targetEnd
189 : // [ targetStart |===========| ]
190 : // [=====| sourceEnd sourceStart |======]
191 0 : var firstEnd = _table.length - sourceStart;
192 0 : _table.setRange(targetStart, firstEnd, _table, sourceStart);
193 0 : _table.setRange(firstEnd, targetEnd, _table);
194 : }
195 : } else {
196 : // Copying twice is safe here as long as we copy back to front. This
197 : // also covers the case where there's no overlap between the source and
198 : // target ranges, in which case the direction doesn't matter.
199 : if (sourceIsContiguous) {
200 : // [=====| targetEnd targetStart |======]
201 : // [ sourceStart |===========| ]
202 : // sourceEnd
203 0 : _table.setRange(0, targetEnd, _table,
204 0 : sourceStart + (_table.length - targetStart));
205 0 : _table.setRange(targetStart, _table.length, _table, sourceStart);
206 : } else {
207 : // targetStart
208 : // [ |===========| targetEnd ]
209 : // [=====| sourceEnd sourceStart |======]
210 0 : var firstStart = targetEnd - sourceEnd;
211 0 : _table.setRange(firstStart, targetEnd, _table);
212 0 : _table.setRange(targetStart, firstStart, _table, sourceStart);
213 : }
214 : }
215 : } else if (targetIsContiguous) {
216 : // If the range is contiguous within the table, we can set it with a single
217 : // underlying [setRange] call.
218 0 : _table.setRange(targetStart, targetEnd, iterable, skipCount);
219 0 : } else if (iterable is List<E>) {
220 : // If the range isn't contiguous and [iterable] is actually a [List] (but
221 : // not this queue), set it with two underlying [setRange] calls.
222 0 : _table.setRange(targetStart, _table.length, iterable, skipCount);
223 0 : _table.setRange(
224 0 : 0, targetEnd, iterable, skipCount + (_table.length - targetStart));
225 : } else {
226 : // If [iterable] isn't a [List], we don't want to make two different
227 : // [setRange] calls because it could materialize a lazy iterable twice.
228 : // Instead we just fall back to the default iteration-based
229 : // implementation.
230 0 : super.setRange(start, end, iterable, skipCount);
231 : }
232 : }
233 :
234 0 : void fillRange(int start, int end, [E? value]) {
235 0 : var startInTable = (_head + start) & (_table.length - 1);
236 0 : var endInTable = (_head + end) & (_table.length - 1);
237 0 : if (startInTable <= endInTable) {
238 0 : _table.fillRange(startInTable, endInTable, value);
239 : } else {
240 0 : _table.fillRange(startInTable, _table.length, value);
241 0 : _table.fillRange(0, endInTable, value);
242 : }
243 : }
244 :
245 0 : L sublist(int start, [int? end]) {
246 0 : var length = this.length;
247 0 : var nonNullEnd = RangeError.checkValidRange(start, end, length);
248 :
249 0 : var list = _createList(nonNullEnd - start);
250 0 : _writeToList(list, start, nonNullEnd);
251 : return list;
252 : }
253 :
254 : // Internal helper functions.
255 :
256 : /// Writes the contents of `this` between [start] (which defaults to 0) and
257 : /// [end] (which defaults to [length]) to the beginning of [target].
258 : ///
259 : /// This is functionally identical to `target.setRange(0, end - start, this,
260 : /// start)`, but it's more efficient when [target] is typed data.
261 : ///
262 : /// Returns the number of elements written to [target].
263 0 : int _writeToList(List<E> target, [int? start, int? end]) {
264 : start ??= 0;
265 0 : end ??= length;
266 0 : assert(target.length >= end - start);
267 0 : assert(start <= end);
268 :
269 0 : var elementsToWrite = end - start;
270 0 : var startInTable = (_head + start) & (_table.length - 1);
271 0 : var endInTable = (_head + end) & (_table.length - 1);
272 0 : if (startInTable <= endInTable) {
273 0 : target.setRange(0, elementsToWrite, _table, startInTable);
274 : } else {
275 0 : var firstPartSize = _table.length - startInTable;
276 0 : target.setRange(0, firstPartSize, _table, startInTable);
277 0 : target.setRange(firstPartSize, firstPartSize + endInTable, _table, 0);
278 : }
279 : return elementsToWrite;
280 : }
281 :
282 : /// Assumes the table is currently full to capacity, and grows it to the next
283 : /// power of two.
284 0 : void _growAtCapacity() {
285 0 : assert(_head == _tail);
286 :
287 0 : var newTable = _createList(_table.length * 2);
288 :
289 : // We can't use [_writeToList] here because when `_head == _tail` it thinks
290 : // the queue is empty rather than full.
291 0 : var partitionPoint = _table.length - _head;
292 0 : newTable.setRange(0, partitionPoint, _table, _head);
293 0 : if (partitionPoint != _table.length) {
294 0 : newTable.setRange(partitionPoint, _table.length, _table);
295 : }
296 0 : _head = 0;
297 0 : _tail = _table.length;
298 0 : _table = newTable;
299 : }
300 :
301 : /// Grows the tableso it's at least large enough size to include that many
302 : /// elements.
303 0 : void _growTo(int newElementCount) {
304 0 : assert(newElementCount >= length);
305 :
306 : // Add some extra room to ensure that there's room for more elements after
307 : // expansion.
308 0 : newElementCount += newElementCount >> 1;
309 0 : var newTable = _createList(_nextPowerOf2(newElementCount));
310 0 : _tail = _writeToList(newTable);
311 0 : _table = newTable;
312 0 : _head = 0;
313 : }
314 :
315 : // Specialization for the specific type.
316 :
317 : // Create a new typed list.
318 : L _createList(int size);
319 :
320 : // Create a new typed buffer of the given type.
321 : List<E> _createBuffer(int size);
322 :
323 : /// The default value used to fill the queue when changing length.
324 : E get _defaultValue;
325 : }
326 :
327 : abstract class _IntQueue<L extends List<int>> extends _TypedQueue<int, L> {
328 0 : _IntQueue(L queue) : super(queue);
329 :
330 0 : int get _defaultValue => 0;
331 : }
332 :
333 : abstract class _FloatQueue<L extends List<double>>
334 : extends _TypedQueue<double, L> {
335 0 : _FloatQueue(L queue) : super(queue);
336 :
337 0 : double get _defaultValue => 0.0;
338 : }
339 :
340 : /// A [QueueList] that efficiently stores 8-bit unsigned integers.
341 : ///
342 : /// For long queues, this implementation can be considerably more space- and
343 : /// time-efficient than a default [QueueList] implementation.
344 : ///
345 : /// Integers stored in this are truncated to their low eight bits, interpreted
346 : /// as an unsigned 8-bit integer with values in the range 0 to 255.
347 : class Uint8Queue extends _IntQueue<Uint8List> implements QueueList<int> {
348 : /// Creates an empty [Uint8Queue] with the given initial internal capacity (in
349 : /// elements).
350 0 : Uint8Queue([int? initialCapacity])
351 0 : : super(Uint8List(_chooseRealInitialCapacity(initialCapacity)));
352 :
353 : /// Creates a [Uint8Queue] with the same length and contents as [elements].
354 0 : factory Uint8Queue.fromList(List<int> elements) =>
355 0 : Uint8Queue(elements.length)..addAll(elements);
356 :
357 0 : Uint8List _createList(int size) => Uint8List(size);
358 0 : Uint8Buffer _createBuffer(int size) => Uint8Buffer(size);
359 : }
360 :
361 : /// A [QueueList] that efficiently stores 8-bit signed integers.
362 : ///
363 : /// For long queues, this implementation can be considerably more space- and
364 : /// time-efficient than a default [QueueList] implementation.
365 : ///
366 : /// Integers stored in this are truncated to their low eight bits, interpreted
367 : /// as a signed 8-bit two's complement integer with values in the range -128 to
368 : /// +127.
369 : class Int8Queue extends _IntQueue<Int8List> implements QueueList<int> {
370 : /// Creates an empty [Int8Queue] with the given initial internal capacity (in
371 : /// elements).
372 0 : Int8Queue([int? initialCapacity])
373 0 : : super(Int8List(_chooseRealInitialCapacity(initialCapacity)));
374 :
375 : /// Creates a [Int8Queue] with the same length and contents as [elements].
376 0 : factory Int8Queue.fromList(List<int> elements) =>
377 0 : Int8Queue(elements.length)..addAll(elements);
378 :
379 0 : Int8List _createList(int size) => Int8List(size);
380 0 : Int8Buffer _createBuffer(int size) => Int8Buffer(size);
381 : }
382 :
383 : /// A [QueueList] that efficiently stores 8-bit unsigned integers.
384 : ///
385 : /// For long queues, this implementation can be considerably more space- and
386 : /// time-efficient than a default [QueueList] implementation.
387 : ///
388 : /// Integers stored in this are clamped to an unsigned eight bit value. That is,
389 : /// all values below zero are stored as zero and all values above 255 are stored
390 : /// as 255.
391 : class Uint8ClampedQueue extends _IntQueue<Uint8ClampedList>
392 : implements QueueList<int> {
393 : /// Creates an empty [Uint8ClampedQueue] with the given initial internal
394 : /// capacity (in elements).
395 0 : Uint8ClampedQueue([int? initialCapacity])
396 0 : : super(Uint8ClampedList(_chooseRealInitialCapacity(initialCapacity)));
397 :
398 : /// Creates a [Uint8ClampedQueue] with the same length and contents as
399 : /// [elements].
400 0 : factory Uint8ClampedQueue.fromList(List<int> elements) =>
401 0 : Uint8ClampedQueue(elements.length)..addAll(elements);
402 :
403 0 : Uint8ClampedList _createList(int size) => Uint8ClampedList(size);
404 0 : Uint8ClampedBuffer _createBuffer(int size) => Uint8ClampedBuffer(size);
405 : }
406 :
407 : /// A [QueueList] that efficiently stores 16-bit unsigned integers.
408 : ///
409 : /// For long queues, this implementation can be considerably more space- and
410 : /// time-efficient than a default [QueueList] implementation.
411 : ///
412 : /// Integers stored in this are truncated to their low 16 bits, interpreted as
413 : /// an unsigned 16-bit integer with values in the range 0 to 65535.
414 : class Uint16Queue extends _IntQueue<Uint16List> implements QueueList<int> {
415 : /// Creates an empty [Uint16Queue] with the given initial internal capacity
416 : /// (in elements).
417 0 : Uint16Queue([int? initialCapacity])
418 0 : : super(Uint16List(_chooseRealInitialCapacity(initialCapacity)));
419 :
420 : /// Creates a [Uint16Queue] with the same length and contents as [elements].
421 0 : factory Uint16Queue.fromList(List<int> elements) =>
422 0 : Uint16Queue(elements.length)..addAll(elements);
423 :
424 0 : Uint16List _createList(int size) => Uint16List(size);
425 0 : Uint16Buffer _createBuffer(int size) => Uint16Buffer(size);
426 : }
427 :
428 : /// A [QueueList] that efficiently stores 16-bit signed integers.
429 : ///
430 : /// For long queues, this implementation can be considerably more space- and
431 : /// time-efficient than a default [QueueList] implementation.
432 : ///
433 : /// Integers stored in this are truncated to their low 16 bits, interpreted as a
434 : /// signed 16-bit two's complement integer with values in the range -32768 to
435 : /// +32767.
436 : class Int16Queue extends _IntQueue<Int16List> implements QueueList<int> {
437 : /// Creates an empty [Int16Queue] with the given initial internal capacity (in
438 : /// elements).
439 0 : Int16Queue([int? initialCapacity])
440 0 : : super(Int16List(_chooseRealInitialCapacity(initialCapacity)));
441 :
442 : /// Creates a [Int16Queue] with the same length and contents as [elements].
443 0 : factory Int16Queue.fromList(List<int> elements) =>
444 0 : Int16Queue(elements.length)..addAll(elements);
445 :
446 0 : Int16List _createList(int size) => Int16List(size);
447 0 : Int16Buffer _createBuffer(int size) => Int16Buffer(size);
448 : }
449 :
450 : /// A [QueueList] that efficiently stores 32-bit unsigned integers.
451 : ///
452 : /// For long queues, this implementation can be considerably more space- and
453 : /// time-efficient than a default [QueueList] implementation.
454 : ///
455 : /// Integers stored in this are truncated to their low 32 bits, interpreted as
456 : /// an unsigned 32-bit integer with values in the range 0 to 4294967295.
457 : class Uint32Queue extends _IntQueue<Uint32List> implements QueueList<int> {
458 : /// Creates an empty [Uint32Queue] with the given initial internal capacity
459 : /// (in elements).
460 0 : Uint32Queue([int? initialCapacity])
461 0 : : super(Uint32List(_chooseRealInitialCapacity(initialCapacity)));
462 :
463 : /// Creates a [Uint32Queue] with the same length and contents as [elements].
464 0 : factory Uint32Queue.fromList(List<int> elements) =>
465 0 : Uint32Queue(elements.length)..addAll(elements);
466 :
467 0 : Uint32List _createList(int size) => Uint32List(size);
468 0 : Uint32Buffer _createBuffer(int size) => Uint32Buffer(size);
469 : }
470 :
471 : /// A [QueueList] that efficiently stores 32-bit signed integers.
472 : ///
473 : /// For long queues, this implementation can be considerably more space- and
474 : /// time-efficient than a default [QueueList] implementation.
475 : ///
476 : /// Integers stored in this are truncated to their low 32 bits, interpreted as a
477 : /// signed 32-bit two's complement integer with values in the range -2147483648
478 : /// to 2147483647.
479 : class Int32Queue extends _IntQueue<Int32List> implements QueueList<int> {
480 : /// Creates an empty [Int32Queue] with the given initial internal capacity (in
481 : /// elements).
482 0 : Int32Queue([int? initialCapacity])
483 0 : : super(Int32List(_chooseRealInitialCapacity(initialCapacity)));
484 :
485 : /// Creates a [Int32Queue] with the same length and contents as [elements].
486 0 : factory Int32Queue.fromList(List<int> elements) =>
487 0 : Int32Queue(elements.length)..addAll(elements);
488 :
489 0 : Int32List _createList(int size) => Int32List(size);
490 0 : Int32Buffer _createBuffer(int size) => Int32Buffer(size);
491 : }
492 :
493 : /// A [QueueList] that efficiently stores 64-bit unsigned integers.
494 : ///
495 : /// For long queues, this implementation can be considerably more space- and
496 : /// time-efficient than a default [QueueList] implementation.
497 : ///
498 : /// Integers stored in this are truncated to their low 64 bits, interpreted as
499 : /// an unsigned 64-bit integer with values in the range 0 to
500 : /// 18446744073709551615.
501 : class Uint64Queue extends _IntQueue<Uint64List> implements QueueList<int> {
502 : /// Creates an empty [Uint64Queue] with the given initial internal capacity
503 : /// (in elements).
504 0 : Uint64Queue([int? initialCapacity])
505 0 : : super(Uint64List(_chooseRealInitialCapacity(initialCapacity)));
506 :
507 : /// Creates a [Uint64Queue] with the same length and contents as [elements].
508 0 : factory Uint64Queue.fromList(List<int> elements) =>
509 0 : Uint64Queue(elements.length)..addAll(elements);
510 :
511 0 : Uint64List _createList(int size) => Uint64List(size);
512 0 : Uint64Buffer _createBuffer(int size) => Uint64Buffer(size);
513 : }
514 :
515 : /// A [QueueList] that efficiently stores 64-bit signed integers.
516 : ///
517 : /// For long queues, this implementation can be considerably more space- and
518 : /// time-efficient than a default [QueueList] implementation.
519 : ///
520 : /// Integers stored in this are truncated to their low 64 bits, interpreted as a
521 : /// signed 64-bit two's complement integer with values in the range
522 : /// -9223372036854775808 to +9223372036854775807.
523 : class Int64Queue extends _IntQueue<Int64List> implements QueueList<int> {
524 : /// Creates an empty [Int64Queue] with the given initial internal capacity (in
525 : /// elements).
526 0 : Int64Queue([int? initialCapacity])
527 0 : : super(Int64List(_chooseRealInitialCapacity(initialCapacity)));
528 :
529 : /// Creates a [Int64Queue] with the same length and contents as [elements].
530 0 : factory Int64Queue.fromList(List<int> elements) =>
531 0 : Int64Queue(elements.length)..addAll(elements);
532 :
533 0 : Int64List _createList(int size) => Int64List(size);
534 0 : Int64Buffer _createBuffer(int size) => Int64Buffer(size);
535 : }
536 :
537 : /// A [QueueList] that efficiently stores IEEE 754 single-precision binary
538 : /// floating-point numbers.
539 : ///
540 : /// For long queues, this implementation can be considerably more space- and
541 : /// time-efficient than a default [QueueList] implementation.
542 : ///
543 : /// Doubles stored in this are converted to the nearest single-precision value.
544 : /// Values read are converted to a double value with the same value.
545 : class Float32Queue extends _FloatQueue<Float32List>
546 : implements QueueList<double> {
547 : /// Creates an empty [Float32Queue] with the given initial internal capacity
548 : /// (in elements).
549 0 : Float32Queue([int? initialCapacity])
550 0 : : super(Float32List(_chooseRealInitialCapacity(initialCapacity)));
551 :
552 : /// Creates a [Float32Queue] with the same length and contents as [elements].
553 0 : factory Float32Queue.fromList(List<double> elements) =>
554 0 : Float32Queue(elements.length)..addAll(elements);
555 :
556 0 : Float32List _createList(int size) => Float32List(size);
557 0 : Float32Buffer _createBuffer(int size) => Float32Buffer(size);
558 : }
559 :
560 : /// A [QueueList] that efficiently stores IEEE 754 double-precision binary
561 : /// floating-point numbers.
562 : ///
563 : /// For long queues, this implementation can be considerably more space- and
564 : /// time-efficient than a default [QueueList] implementation.
565 : class Float64Queue extends _FloatQueue<Float64List>
566 : implements QueueList<double> {
567 : /// Creates an empty [Float64Queue] with the given initial internal capacity
568 : /// (in elements).
569 0 : Float64Queue([int? initialCapacity])
570 0 : : super(Float64List(_chooseRealInitialCapacity(initialCapacity)));
571 :
572 : /// Creates a [Float64Queue] with the same length and contents as [elements].
573 0 : factory Float64Queue.fromList(List<double> elements) =>
574 0 : Float64Queue(elements.length)..addAll(elements);
575 :
576 0 : Float64List _createList(int size) => Float64List(size);
577 0 : Float64Buffer _createBuffer(int size) => Float64Buffer(size);
578 : }
579 :
580 : /// A [QueueList] that efficiently stores [Int32x4] numbers.
581 : ///
582 : /// For long queues, this implementation can be considerably more space- and
583 : /// time-efficient than a default [QueueList] implementation.
584 : class Int32x4Queue extends _TypedQueue<Int32x4, Int32x4List>
585 : implements QueueList<Int32x4> {
586 0 : static final Int32x4 _zero = Int32x4(0, 0, 0, 0);
587 :
588 : /// Creates an empty [Int32x4Queue] with the given initial internal capacity
589 : /// (in elements).
590 0 : Int32x4Queue([int? initialCapacity])
591 0 : : super(Int32x4List(_chooseRealInitialCapacity(initialCapacity)));
592 :
593 : /// Creates a [Int32x4Queue] with the same length and contents as [elements].
594 0 : factory Int32x4Queue.fromList(List<Int32x4> elements) =>
595 0 : Int32x4Queue(elements.length)..addAll(elements);
596 :
597 0 : Int32x4List _createList(int size) => Int32x4List(size);
598 0 : Int32x4Buffer _createBuffer(int size) => Int32x4Buffer(size);
599 0 : Int32x4 get _defaultValue => _zero;
600 : }
601 :
602 : /// A [QueueList] that efficiently stores [Float32x4] numbers.
603 : ///
604 : /// For long queues, this implementation can be considerably more space- and
605 : /// time-efficient than a default [QueueList] implementation.
606 : class Float32x4Queue extends _TypedQueue<Float32x4, Float32x4List>
607 : implements QueueList<Float32x4> {
608 : /// Creates an empty [Float32x4Queue] with the given initial internal capacity (in
609 : /// elements).
610 0 : Float32x4Queue([int? initialCapacity])
611 0 : : super(Float32x4List(_chooseRealInitialCapacity(initialCapacity)));
612 :
613 : /// Creates a [Float32x4Queue] with the same length and contents as [elements].
614 0 : factory Float32x4Queue.fromList(List<Float32x4> elements) =>
615 0 : Float32x4Queue(elements.length)..addAll(elements);
616 :
617 0 : Float32x4List _createList(int size) => Float32x4List(size);
618 0 : Float32x4Buffer _createBuffer(int size) => Float32x4Buffer(size);
619 0 : Float32x4 get _defaultValue => Float32x4.zero();
620 : }
621 :
622 : /// The initial capacity of queues if the user doesn't specify one.
623 : const _defaultInitialCapacity = 16;
624 :
625 : /// Choose the next-highest power of two given a user-specified
626 : /// [initialCapacity] for a queue.
627 0 : int _chooseRealInitialCapacity(int? initialCapacity) {
628 0 : if (initialCapacity == null || initialCapacity < _defaultInitialCapacity) {
629 : return _defaultInitialCapacity;
630 0 : } else if (!_isPowerOf2(initialCapacity)) {
631 0 : return _nextPowerOf2(initialCapacity);
632 : } else {
633 : return initialCapacity;
634 : }
635 : }
636 :
637 : /// Whether [number] is a power of two.
638 : ///
639 : /// Only works for positive numbers.
640 0 : bool _isPowerOf2(int number) => (number & (number - 1)) == 0;
641 :
642 : /// Rounds [number] up to the nearest power of 2.
643 : ///
644 : /// If [number] is a power of 2 already, it is returned.
645 : ///
646 : /// Only works for positive numbers.
647 0 : int _nextPowerOf2(int number) {
648 0 : assert(number > 0);
649 0 : number = (number << 1) - 1;
650 : for (;;) {
651 0 : var nextNumber = number & (number - 1);
652 0 : if (nextNumber == 0) return number;
653 : number = nextNumber;
654 : }
655 : }
|