Line data Source code
1 : // Copyright (c) 2014, 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 :
7 : /// A class that efficiently implements both [Queue] and [List].
8 : // TODO(nweiz): Currently this code is copied almost verbatim from
9 : // dart:collection. The only changes are to implement List and to remove methods
10 : // that are redundant with ListMixin. Remove or simplify it when issue 21330 is
11 : // fixed.
12 : class QueueList<E> extends Object with ListMixin<E> implements Queue<E> {
13 : static const int _INITIAL_CAPACITY = 8;
14 : List<E> _table;
15 : int _head;
16 : int _tail;
17 :
18 : /// Create an empty queue.
19 : ///
20 : /// If [initialCapacity] is given, prepare the queue for at least that many
21 : /// elements.
22 : QueueList([int initialCapacity])
23 : : _head = 0,
24 5 : _tail = 0 {
25 0 : if (initialCapacity == null || initialCapacity < _INITIAL_CAPACITY) {
26 : initialCapacity = _INITIAL_CAPACITY;
27 0 : } else if (!_isPowerOf2(initialCapacity)) {
28 0 : initialCapacity = _nextPowerOf2(initialCapacity);
29 : }
30 : assert(_isPowerOf2(initialCapacity));
31 10 : _table = new List<E>(initialCapacity);
32 : }
33 :
34 : /// Create a queue initially containing the elements of [source].
35 : factory QueueList.from(Iterable<E> source) {
36 0 : if (source is List) {
37 0 : int length = source.length;
38 0 : QueueList<E> queue = new QueueList(length + 1);
39 : assert(queue._table.length > length);
40 : var sourceList = source;
41 0 : queue._table.setRange(0, length, sourceList, 0);
42 0 : queue._tail = length;
43 : return queue;
44 : } else {
45 0 : return new QueueList<E>()..addAll(source);
46 : }
47 : }
48 :
49 : // Collection interface.
50 :
51 : void add(E element) {
52 5 : _add(element);
53 : }
54 :
55 : void addAll(Iterable<E> elements) {
56 0 : if (elements is List) {
57 : var list = elements;
58 0 : int addCount = list.length;
59 0 : int length = this.length;
60 0 : if (length + addCount >= _table.length) {
61 0 : _preGrow(length + addCount);
62 : // After preGrow, all elements are at the start of the list.
63 0 : _table.setRange(length, length + addCount, list, 0);
64 0 : _tail += addCount;
65 : } else {
66 : // Adding addCount elements won't reach _head.
67 0 : int endSpace = _table.length - _tail;
68 0 : if (addCount < endSpace) {
69 0 : _table.setRange(_tail, _tail + addCount, list, 0);
70 0 : _tail += addCount;
71 : } else {
72 0 : int preSpace = addCount - endSpace;
73 0 : _table.setRange(_tail, _tail + endSpace, list, 0);
74 0 : _table.setRange(0, preSpace, list, endSpace);
75 0 : _tail = preSpace;
76 : }
77 : }
78 : } else {
79 0 : for (E element in elements) _add(element);
80 : }
81 : }
82 :
83 0 : String toString() => IterableBase.iterableToFullString(this, "{", "}");
84 :
85 : // Queue interface.
86 :
87 : void addLast(E element) {
88 0 : _add(element);
89 : }
90 :
91 : void addFirst(E element) {
92 0 : _head = (_head - 1) & (_table.length - 1);
93 0 : _table[_head] = element;
94 0 : if (_head == _tail) _grow();
95 : }
96 :
97 : E removeFirst() {
98 0 : if (_head == _tail) throw new StateError("No element");
99 0 : E result = _table[_head];
100 0 : _table[_head] = null;
101 0 : _head = (_head + 1) & (_table.length - 1);
102 : return result;
103 : }
104 :
105 : E removeLast() {
106 0 : if (_head == _tail) throw new StateError("No element");
107 0 : _tail = (_tail - 1) & (_table.length - 1);
108 0 : E result = _table[_tail];
109 0 : _table[_tail] = null;
110 : return result;
111 : }
112 :
113 : // List interface.
114 :
115 35 : int get length => (_tail - _head) & (_table.length - 1);
116 :
117 : void set length(int value) {
118 5 : if (value < 0) throw new RangeError("Length $value may not be negative.");
119 :
120 10 : int delta = value - length;
121 5 : if (delta >= 0) {
122 0 : if (_table.length <= value) {
123 0 : _preGrow(value);
124 : }
125 0 : _tail = (_tail + delta) & (_table.length - 1);
126 : return;
127 : }
128 :
129 10 : int newTail = _tail + delta; // [delta] is negative.
130 5 : if (newTail >= 0) {
131 15 : _table.fillRange(newTail, _tail, null);
132 : } else {
133 0 : newTail += _table.length;
134 0 : _table.fillRange(0, _tail, null);
135 0 : _table.fillRange(newTail, _table.length, null);
136 : }
137 5 : _tail = newTail;
138 : }
139 :
140 : E operator [](int index) {
141 15 : if (index < 0 || index >= length) {
142 0 : throw new RangeError("Index $index must be in the range [0..$length).");
143 : }
144 :
145 40 : return _table[(_head + index) & (_table.length - 1)];
146 : }
147 :
148 : void operator []=(int index, E value) {
149 0 : if (index < 0 || index >= length) {
150 0 : throw new RangeError("Index $index must be in the range [0..$length).");
151 : }
152 :
153 0 : _table[(_head + index) & (_table.length - 1)] = value;
154 : }
155 :
156 : // Internal helper functions.
157 :
158 : /// Whether [number] is a power of two.
159 : ///
160 : /// Only works for positive numbers.
161 0 : static bool _isPowerOf2(int number) => (number & (number - 1)) == 0;
162 :
163 : /// Rounds [number] up to the nearest power of 2.
164 : ///
165 : /// If [number] is a power of 2 already, it is returned.
166 : ///
167 : /// Only works for positive numbers.
168 : static int _nextPowerOf2(int number) {
169 : assert(number > 0);
170 0 : number = (number << 1) - 1;
171 : for (;;) {
172 0 : int nextNumber = number & (number - 1);
173 0 : if (nextNumber == 0) return number;
174 : number = nextNumber;
175 : }
176 : }
177 :
178 : /// Adds element at end of queue. Used by both [add] and [addAll].
179 : void _add(E element) {
180 15 : _table[_tail] = element;
181 35 : _tail = (_tail + 1) & (_table.length - 1);
182 15 : if (_head == _tail) _grow();
183 : }
184 :
185 : /// Grow the table when full.
186 : void _grow() {
187 0 : List<E> newTable = new List<E>(_table.length * 2);
188 0 : int split = _table.length - _head;
189 0 : newTable.setRange(0, split, _table, _head);
190 0 : newTable.setRange(split, split + _head, _table, 0);
191 0 : _head = 0;
192 0 : _tail = _table.length;
193 0 : _table = newTable;
194 : }
195 :
196 : int _writeToList(List<E> target) {
197 : assert(target.length >= length);
198 0 : if (_head <= _tail) {
199 0 : int length = _tail - _head;
200 0 : target.setRange(0, length, _table, _head);
201 : return length;
202 : } else {
203 0 : int firstPartSize = _table.length - _head;
204 0 : target.setRange(0, firstPartSize, _table, _head);
205 0 : target.setRange(firstPartSize, firstPartSize + _tail, _table, 0);
206 0 : return _tail + firstPartSize;
207 : }
208 : }
209 :
210 : /// Grows the table even if it is not full.
211 : void _preGrow(int newElementCount) {
212 : assert(newElementCount >= length);
213 :
214 : // Add 1.5x extra room to ensure that there's room for more elements after
215 : // expansion.
216 0 : newElementCount += newElementCount >> 1;
217 0 : int newCapacity = _nextPowerOf2(newElementCount);
218 0 : List<E> newTable = new List<E>(newCapacity);
219 0 : _tail = _writeToList(newTable);
220 0 : _table = newTable;
221 0 : _head = 0;
222 : }
223 : }
|