OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013, 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 library dart2js.util.setlet; | |
6 | |
7 import 'dart:collection' show IterableBase; | |
8 | |
9 class Setlet<E> extends IterableBase<E> implements Set<E> { | |
10 static const _MARKER = const _SetletMarker(); | |
11 static const CAPACITY = 8; | |
12 | |
13 // The setlet can be in one of four states: | |
14 // | |
15 // * Empty (extra: null, contents: marker) | |
16 // * Single element (extra: null, contents: element) | |
17 // * List-backed (extra: length, contents: list) | |
18 // * Set-backed (extra: marker, contents: set) | |
19 // | |
20 // When the setlet is list-backed, the list in the contents field | |
21 // may have empty slots filled with the marker value. | |
22 var _contents = _MARKER; | |
23 var _extra; | |
24 | |
25 Setlet(); | |
26 Setlet.from(Iterable<E> elements) { | |
27 addAll(elements); | |
28 } | |
29 | |
30 Iterator<E> get iterator { | |
31 if (_extra == null) { | |
32 return new _SetletSingleIterator<E>(_contents); | |
33 } else if (_MARKER == _extra) { | |
34 return _contents.iterator; | |
35 } else { | |
36 return new _SetletListIterator<E>(_contents, _extra); | |
37 } | |
38 } | |
39 | |
40 int get length { | |
41 if (_extra == null) { | |
42 return (_MARKER == _contents) ? 0 : 1; | |
43 } else if (_MARKER == _extra) { | |
44 return _contents.length; | |
45 } else { | |
46 return _extra; | |
47 } | |
48 } | |
49 | |
50 bool get isEmpty { | |
51 if (_extra == null) { | |
52 return _MARKER == _contents; | |
53 } else if (_MARKER == _extra) { | |
54 return _contents.isEmpty; | |
55 } else { | |
56 return _extra == 0; | |
57 } | |
58 } | |
59 | |
60 bool contains(E element) { | |
61 if (_extra == null) { | |
62 return _contents == element; | |
63 } else if (_MARKER == _extra) { | |
64 return _contents.contains(element); | |
65 } else { | |
66 for (int remaining = _extra, i = 0; remaining > 0 && i < CAPACITY; i++) { | |
67 var candidate = _contents[i]; | |
68 if (_MARKER == candidate) continue; | |
69 if (candidate == element) return true; | |
70 remaining--; | |
71 } | |
72 return false; | |
73 } | |
74 } | |
75 | |
76 bool add(E element) { | |
77 if (_extra == null) { | |
78 if (_MARKER == _contents) { | |
79 _contents = element; | |
80 return true; | |
81 } else if (_contents == element) { | |
82 // Do nothing. | |
83 return false; | |
84 } else { | |
85 List list = new List(CAPACITY); | |
86 list[0] = _contents; | |
87 list[1] = element; | |
88 _contents = list; | |
89 _extra = 2; // Two elements. | |
90 return true; | |
91 } | |
92 } else if (_MARKER == _extra) { | |
93 return _contents.add(element); | |
94 } else { | |
95 int remaining = _extra; | |
96 int index = 0; | |
97 int copyTo, copyFrom; | |
98 while (remaining > 0 && index < CAPACITY) { | |
99 var candidate = _contents[index++]; | |
100 if (_MARKER == candidate) { | |
101 // Keep track of the last range of empty slots in the | |
102 // list. When we're done we'll move all the elements | |
103 // after those empty slots down, so that adding an element | |
104 // after that will preserve the insertion order. | |
105 if (copyFrom == index - 1) { | |
106 copyFrom++; | |
107 } else { | |
108 copyTo = index - 1; | |
109 copyFrom = index; | |
110 } | |
111 continue; | |
112 } else if (candidate == element) { | |
113 return false; | |
114 } | |
115 remaining--; | |
116 } | |
117 if (index < CAPACITY) { | |
118 _contents[index] = element; | |
119 _extra++; | |
120 } else if (_extra < CAPACITY) { | |
121 // Move the last elements down into the last empty slots | |
122 // so that we have empty slots after the last element. | |
123 while (copyFrom < CAPACITY) { | |
124 _contents[copyTo++] = _contents[copyFrom++]; | |
125 } | |
126 // Insert the new element as the last element. | |
127 _contents[copyTo++] = element; | |
128 _extra++; | |
129 // Clear all elements after the new last elements to | |
130 // make sure we don't keep extra stuff alive. | |
131 while (copyTo < CAPACITY) _contents[copyTo++] = null; | |
132 } else { | |
133 _contents = new Set<E>()..addAll(_contents)..add(element); | |
134 _extra = _MARKER; | |
135 } | |
136 return true; | |
137 } | |
138 } | |
139 | |
140 void addAll(Iterable<E> elements) { | |
141 elements.forEach((each) => add(each)); | |
142 } | |
143 | |
144 E lookup(Object element) { | |
145 if (_extra == null) { | |
146 return _contents == element ? _contents : null; | |
147 } else if (_MARKER == _extra) { | |
148 return _contents.lookup(element); | |
149 } else { | |
150 for (int remaining = _extra, i = 0; remaining > 0 && i < CAPACITY; i++) { | |
151 var candidate = _contents[i]; | |
152 if (_MARKER == candidate) continue; | |
153 if (candidate == element) return candidate; | |
154 remaining--; | |
155 } | |
156 return null; | |
157 } | |
158 } | |
159 | |
160 bool remove(E element) { | |
161 if (_extra == null) { | |
162 if (_contents == element) { | |
163 _contents = _MARKER; | |
164 return true; | |
165 } else { | |
166 return false; | |
167 } | |
168 } else if (_MARKER == _extra) { | |
169 return _contents.remove(element); | |
170 } else { | |
171 for (int remaining = _extra, i = 0; remaining > 0 && i < CAPACITY; i++) { | |
172 var candidate = _contents[i]; | |
173 if (_MARKER == candidate) continue; | |
174 if (candidate == element) { | |
175 _contents[i] = _MARKER; | |
176 _extra--; | |
177 return true; | |
178 } | |
179 remaining--; | |
180 } | |
181 return false; | |
182 } | |
183 } | |
184 | |
185 void removeAll(Iterable<Object> other) { | |
186 other.forEach(remove); | |
187 } | |
188 | |
189 void removeWhere(bool test(E element)) { | |
190 if (_extra == null) { | |
191 if (test(_contents)) { | |
192 _contents = _MARKER; | |
193 } | |
194 } else if (_MARKER == _extra) { | |
195 _contents.removeWhere(test); | |
196 } else { | |
197 for (int remaining = _extra, i = 0; remaining > 0 && i < CAPACITY; i++) { | |
198 var candidate = _contents[i]; | |
199 if (_MARKER == candidate) continue; | |
200 if (test(candidate)) { | |
201 _contents[i] = _MARKER; | |
202 _extra--; | |
203 } | |
204 remaining--; | |
205 } | |
206 } | |
207 } | |
208 | |
209 void retainWhere(bool test(E element)) { | |
210 removeWhere((E element) => !test(element)); | |
211 } | |
212 | |
213 void retainAll(Iterable<Object> elements) { | |
214 Set set = elements is Set ? elements : elements.toSet(); | |
215 removeWhere((E element) => !set.contains(element)); | |
216 } | |
217 | |
218 void forEach(void action(E element)) { | |
219 if (_extra == null) { | |
220 if (_MARKER != _contents) action(_contents); | |
221 } else if (_MARKER == _extra) { | |
222 _contents.forEach(action); | |
223 } else { | |
224 for (int remaining = _extra, i = 0; remaining > 0 && i < CAPACITY; i++) { | |
225 var element = _contents[i]; | |
226 if (_MARKER == element) continue; | |
227 action(element); | |
228 remaining--; | |
229 } | |
230 } | |
231 } | |
232 | |
233 bool containsAll(Iterable<E> other) { | |
234 for (E e in other) { | |
235 if (!this.contains(e)) return false; | |
236 }; | |
237 return true; | |
238 } | |
239 | |
240 clear() { | |
241 _contents = _MARKER; | |
242 _extra = null; | |
243 } | |
244 | |
245 Set<E> union(Set<E> other) => new Set<E>.from(this)..addAll(other); | |
246 | |
247 Setlet<E> intersection(Set<E> other) => | |
248 new Setlet<E>.from(this.where((e) => other.contains(e))); | |
249 | |
250 Setlet<E> difference(Set<E> other) => | |
251 new Setlet<E>.from(this.where((e) => !other.contains(e))); | |
252 | |
253 Setlet<E> toSet() { | |
254 Setlet<E> result = new Setlet<E>(); | |
255 if (_extra == null) { | |
256 result._contents = _contents; | |
257 } else if (_extra == _MARKER) { | |
258 result._extra = _MARKER; | |
259 result._contents = _contents.toSet(); | |
260 } else { | |
261 result._extra = _extra; | |
262 result._contents = _contents.toList(); | |
263 } | |
264 return result; | |
265 } | |
266 } | |
267 | |
268 class _SetletMarker { | |
269 const _SetletMarker(); | |
270 toString() => "-"; | |
271 } | |
272 | |
273 class _SetletSingleIterator<E> implements Iterator<E> { | |
274 var _element; | |
275 E _current; | |
276 _SetletSingleIterator(this._element); | |
277 | |
278 E get current => _current; | |
279 | |
280 bool moveNext() { | |
281 if (Setlet._MARKER == _element) { | |
282 _current = null; | |
283 return false; | |
284 } | |
285 _current = _element; | |
286 _element = Setlet._MARKER; | |
287 return true; | |
288 } | |
289 } | |
290 | |
291 class _SetletListIterator<E> implements Iterator<E> { | |
292 final List _list; | |
293 int _remaining; | |
294 int _index = 0; | |
295 E _current; | |
296 _SetletListIterator(this._list, this._remaining); | |
297 | |
298 E get current => _current; | |
299 | |
300 bool moveNext() { | |
301 while (_remaining > 0) { | |
302 var candidate = _list[_index++]; | |
303 if (Setlet._MARKER != candidate) { | |
304 _current = candidate; | |
305 _remaining--; | |
306 return true; | |
307 } | |
308 } | |
309 _current = null; | |
310 return false; | |
311 } | |
312 } | |
OLD | NEW |