OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 the V8 project 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 | |
5 #include <stdlib.h> | |
6 | |
7 #include "src/globals.h" | |
8 #include "src/zone/zone.h" | |
9 | |
10 #ifndef V8_SRC_ZONE_ZONE_CHUNK_LIST_H_ | |
11 #define V8_SRC_ZONE_ZONE_CHUNK_LIST_H_ | |
12 | |
13 namespace v8 { | |
14 namespace internal { | |
15 | |
16 template <typename T> | |
17 class ZoneChunkListIterator; | |
18 template <typename T> | |
19 class ForwardZoneChunkListIterator; | |
20 template <typename T> | |
21 class ReverseZoneChunkListIterator; | |
22 | |
23 // A zone-backed hybrid of a vector and a linked list. Use it if you need a | |
24 // collection that | |
25 // * needs to grow indefinitely, | |
26 // * will mostly grow at the back, but may sometimes grow in front as well | |
27 // (preferrably in batches), | |
28 // * needs to have very low overhead, | |
29 // * offers forward- and backwards-iteration, | |
30 // * offers relatively fast seeking, | |
31 // * offers bidirectional iterators, | |
32 // * can be rewound without freeing the backing store. | |
33 // This list will maintain a doubly-linked list of chunks. When a chunk is | |
34 // filled up, a new one gets appended. New chunks appended at the end will | |
35 // grow in size up to a certain limit to avoid over-allocation and to keep | |
36 // the zone clean. | |
37 template <typename T> | |
38 class ZoneChunkList : public ZoneObject { | |
39 public: | |
40 enum class StartMode { | |
41 // The list will not allocate a starting chunk. Use if you expect your | |
42 // list to remain empty in many cases. | |
43 kEmpty = 0, | |
44 // The list will start with a small initial chunk. Subsequent chunks will | |
45 // get bigger over time. | |
46 kSmall = 8, | |
47 // The list will start with one chunk at maximum size. Use this if you | |
48 // expect your list to contain many items to avoid growing chunks. | |
49 kBig = 256 | |
50 }; | |
51 | |
52 explicit ZoneChunkList(Zone* zone, StartMode start_mode = StartMode::kEmpty) | |
53 : zone_(zone) { | |
54 if (start_mode != StartMode::kEmpty) { | |
55 front_ = NewChunk(static_cast<uint32_t>(start_mode)); | |
56 back_ = front_; | |
57 } | |
58 } | |
59 | |
60 size_t size() const; | |
61 | |
62 T& front() const; | |
63 T& back() const; | |
64 | |
65 void push_back(const T& item); | |
66 void pop_back(); | |
67 | |
68 // Will push a separate chunk to the front of the chunk-list. | |
69 // Very memory-inefficient. Do only use sparsely! If you have many items to | |
70 // add in front, consider using 'push_front_many'. | |
71 void push_front(const T& item); | |
72 // TODO(heimbuef): Add 'push_front_many'. | |
73 | |
74 // Cuts the last list elements so at most 'limit' many remain. Does not | |
75 // free the actual memory, since it is zone allocated. | |
76 void Rewind(const size_t limit = 0); | |
77 | |
78 // Quickly scans the list to retrieve the element at the given index. Will | |
79 // *not* check bounds. | |
80 ForwardZoneChunkListIterator<T> Find(const size_t index); | |
81 ForwardZoneChunkListIterator<const T> Find(const size_t index) const; | |
82 // TODO(heimbuef): Add 'rFind', seeking from the end and returning a | |
83 // reverse iterator. | |
84 | |
85 ForwardZoneChunkListIterator<T> begin(); | |
86 ForwardZoneChunkListIterator<T> end(); | |
87 ReverseZoneChunkListIterator<T> rbegin(); | |
88 ReverseZoneChunkListIterator<T> rend(); | |
89 ForwardZoneChunkListIterator<const T> begin() const; | |
90 ForwardZoneChunkListIterator<const T> end() const; | |
91 ReverseZoneChunkListIterator<const T> rbegin() const; | |
92 ReverseZoneChunkListIterator<const T> rend() const; | |
93 | |
94 private: | |
95 friend class ZoneChunkListIterator<T>; | |
96 friend class ForwardZoneChunkListIterator<T>; | |
97 friend class ReverseZoneChunkListIterator<T>; | |
98 static const uint32_t kMaxChunkCapacity = 256u; | |
99 | |
100 STATIC_ASSERT(kMaxChunkCapacity == static_cast<uint32_t>(StartMode::kBig)); | |
101 | |
102 struct Chunk { | |
103 uint32_t capacity_ = 0; | |
104 uint32_t position_ = 0; | |
105 Chunk* next_ = nullptr; | |
106 Chunk* previous_ = nullptr; | |
107 T* items() { return reinterpret_cast<T*>(this + 1); } | |
108 }; | |
109 | |
110 Chunk* NewChunk(const uint32_t capacity) { | |
111 Chunk* chunk = | |
112 new (zone_->New(sizeof(Chunk) + capacity * sizeof(T))) Chunk(); | |
113 chunk->capacity_ = capacity; | |
114 return chunk; | |
115 } | |
116 | |
117 struct SeekResult { | |
118 Chunk* chunk_; | |
119 uint32_t chunk_index_; | |
120 }; | |
121 | |
122 // Returns the chunk and relative index of the element at the given global | |
123 // index. Will skip entire chunks and is therefore faster than iterating. | |
124 SeekResult SeekIndex(size_t index) const; | |
125 | |
126 Zone* zone_; | |
127 | |
128 size_t size_ = 0; | |
129 Chunk* front_ = nullptr; | |
130 Chunk* back_ = nullptr; | |
131 | |
132 DISALLOW_COPY_AND_ASSIGN(ZoneChunkList); | |
133 }; | |
134 | |
135 template <typename T> | |
136 class ZoneChunkListIterator { | |
137 public: | |
138 T& operator*() { return current_->items()[position_]; } | |
139 bool operator==(const ZoneChunkListIterator& other) { | |
140 return other.current_ == current_ && other.position_ == position_; | |
141 } | |
142 bool operator!=(const ZoneChunkListIterator& other) { | |
143 return !operator==(other); | |
144 } | |
145 | |
146 protected: | |
147 ZoneChunkListIterator(typename ZoneChunkList<T>::Chunk* current, | |
148 size_t position) | |
149 : current_(current), position_(position) {} | |
150 | |
151 void MoveNext() { | |
152 ++position_; | |
153 if (position_ >= current_->capacity_) { | |
154 current_ = current_->next_; | |
155 position_ = 0; | |
156 } | |
157 } | |
158 | |
159 void MoveRNext() { | |
160 if (position_ == 0) { | |
161 current_ = current_->previous_; | |
162 position_ = current_ ? current_->capacity_ - 1 : 0; | |
163 } else { | |
164 --position_; | |
165 } | |
166 } | |
167 | |
168 typename ZoneChunkList<T>::Chunk* current_; | |
169 size_t position_; | |
170 }; | |
171 | |
172 template <typename T> | |
173 class ForwardZoneChunkListIterator : public ZoneChunkListIterator<T> { | |
174 using ZoneChunkListIterator<T>::current_; | |
175 using ZoneChunkListIterator<T>::position_; | |
176 using ZoneChunkListIterator<T>::MoveNext; | |
177 using ZoneChunkListIterator<T>::MoveRNext; | |
178 | |
179 public: | |
180 ForwardZoneChunkListIterator(typename ZoneChunkList<T>::Chunk* current, | |
181 size_t position) | |
182 : ZoneChunkListIterator<T>(current, position) {} | |
183 | |
184 ForwardZoneChunkListIterator& operator++() { | |
185 MoveNext(); | |
186 return *this; | |
187 } | |
188 | |
189 ForwardZoneChunkListIterator operator++(int) { | |
190 ForwardZoneChunkListIterator<T> clone(*this); | |
191 MoveNext(); | |
192 return clone; | |
193 } | |
194 | |
195 ForwardZoneChunkListIterator& operator--() { | |
196 MoveRNext(); | |
197 return *this; | |
198 } | |
199 | |
200 ForwardZoneChunkListIterator operator--(int) { | |
201 ForwardZoneChunkListIterator<T> clone(*this); | |
202 MoveRNext(); | |
203 return clone; | |
204 } | |
205 | |
206 private: | |
207 friend class ZoneChunkList<T>; | |
208 static ForwardZoneChunkListIterator<T> Begin(ZoneChunkList<T>* list) { | |
209 return ForwardZoneChunkListIterator<T>(list->front_, 0); | |
210 } | |
211 static ForwardZoneChunkListIterator<T> End(ZoneChunkList<T>* list) { | |
212 if (list->back_ == nullptr) return Begin(list); | |
213 | |
214 DCHECK_LE(list->back_->position_, list->back_->capacity_); | |
215 if (list->back_->position_ == list->back_->capacity_) { | |
216 return ForwardZoneChunkListIterator<T>(nullptr, 0); | |
217 } | |
218 | |
219 return ForwardZoneChunkListIterator<T>(list->back_, list->back_->position_); | |
220 } | |
221 }; | |
222 | |
223 template <typename T> | |
224 class ReverseZoneChunkListIterator : public ZoneChunkListIterator<T> { | |
225 using ZoneChunkListIterator<T>::current_; | |
226 using ZoneChunkListIterator<T>::position_; | |
227 using ZoneChunkListIterator<T>::MoveNext; | |
228 using ZoneChunkListIterator<T>::MoveRNext; | |
229 | |
230 public: | |
231 ReverseZoneChunkListIterator(typename ZoneChunkList<T>::Chunk* current, | |
232 size_t position) | |
233 : ZoneChunkListIterator<T>(current, position) {} | |
234 | |
235 ReverseZoneChunkListIterator& operator++() { | |
236 MoveRNext(); | |
237 return *this; | |
238 } | |
239 | |
240 ReverseZoneChunkListIterator operator++(int) { | |
241 ReverseZoneChunkListIterator<T> clone(*this); | |
242 MoveRNext(); | |
243 return clone; | |
244 } | |
245 | |
246 ReverseZoneChunkListIterator& operator--() { | |
247 MoveNext(); | |
248 return *this; | |
249 } | |
250 | |
251 ReverseZoneChunkListIterator operator--(int) { | |
252 ForwardZoneChunkListIterator<T> clone(*this); | |
253 MoveNext(); | |
254 return clone; | |
255 } | |
256 | |
257 private: | |
258 friend class ZoneChunkList<T>; | |
259 static ReverseZoneChunkListIterator<T> Begin(ZoneChunkList<T>* list) { | |
260 if (list->back_ == nullptr) return End(list); | |
261 if (list->back_->position_ == 0) { | |
262 if (list->back_->previous_ != nullptr) { | |
263 return ReverseZoneChunkListIterator<T>( | |
264 list->back_->previous_, list->back_->previous_->capacity_ - 1); | |
265 } else { | |
266 return End(list); | |
267 } | |
268 } | |
269 return ReverseZoneChunkListIterator<T>(list->back_, | |
270 list->back_->position_ - 1); | |
271 } | |
272 static ReverseZoneChunkListIterator<T> End(ZoneChunkList<T>* list) { | |
273 return ReverseZoneChunkListIterator<T>(nullptr, 0); | |
274 } | |
275 }; | |
276 | |
277 template <typename T> | |
278 size_t ZoneChunkList<T>::size() const { | |
279 return size_; | |
280 } | |
281 | |
282 template <typename T> | |
283 T& ZoneChunkList<T>::front() const { | |
284 DCHECK_LT(size_t(0), size()); | |
285 return front_->items()[0]; | |
286 } | |
287 | |
288 template <typename T> | |
289 T& ZoneChunkList<T>::back() const { | |
290 DCHECK_LT(size_t(0), size()); | |
291 | |
292 if (back_->position_ == 0) { | |
293 return back_->previous_->items()[back_->previous_->position_ - 1]; | |
294 } else { | |
295 return back_->items()[back_->position_ - 1]; | |
296 } | |
297 } | |
298 | |
299 template <typename T> | |
300 void ZoneChunkList<T>::push_back(const T& item) { | |
301 if (back_ == nullptr) { | |
302 front_ = NewChunk(static_cast<uint32_t>(StartMode::kSmall)); | |
303 back_ = front_; | |
304 } | |
305 | |
306 DCHECK_LE(back_->position_, back_->capacity_); | |
307 if (back_->position_ == back_->capacity_) { | |
308 if (back_->next_ == nullptr) { | |
309 Chunk* chunk = NewChunk(Min(back_->capacity_ << 1, kMaxChunkCapacity)); | |
310 back_->next_ = chunk; | |
311 chunk->previous_ = back_; | |
312 } | |
313 back_ = back_->next_; | |
314 } | |
315 back_->items()[back_->position_] = item; | |
316 ++back_->position_; | |
317 ++size_; | |
318 } | |
319 | |
320 template <typename T> | |
321 void ZoneChunkList<T>::pop_back() { | |
322 DCHECK_LT(size_t(0), size()); | |
323 if (back_->position_ == 0) { | |
324 back_ = back_->previous_; | |
325 } | |
326 --back_->position_; | |
327 } | |
328 | |
329 template <typename T> | |
330 void ZoneChunkList<T>::push_front(const T& item) { | |
331 Chunk* chunk = NewChunk(1); // Yes, this gets really inefficient. | |
332 chunk->next_ = front_; | |
333 if (front_) { | |
334 front_->previous_ = chunk; | |
335 } else { | |
336 back_ = chunk; | |
337 } | |
338 front_ = chunk; | |
339 | |
340 chunk->items()[0] = item; | |
341 chunk->position_ = 1; | |
342 ++size_; | |
343 } | |
344 | |
345 template <typename T> | |
346 typename ZoneChunkList<T>::SeekResult ZoneChunkList<T>::SeekIndex( | |
347 size_t index) const { | |
348 DCHECK_LT(size_t(0), size()); | |
Jakob Kummerow
2016/10/26 16:08:10
Did you mean DCHECK_LT(index, size())?
| |
349 Chunk* current = front_; | |
350 while (index > current->capacity_) { | |
351 index -= current->capacity_; | |
352 current = current->next_; | |
353 } | |
354 return {current, static_cast<uint32_t>(index)}; | |
355 } | |
356 | |
357 template <typename T> | |
358 void ZoneChunkList<T>::Rewind(const size_t limit) { | |
359 if (limit >= size()) return; | |
360 | |
361 SeekResult seek_result = SeekIndex(limit); | |
362 DCHECK_NOT_NULL(seek_result.chunk_); | |
363 | |
364 // Do a partial rewind of the chunk containing the index. | |
365 seek_result.chunk_->position_ = seek_result.chunk_index_; | |
366 | |
367 // Set back_ so iterators will work correctly. | |
368 back_ = seek_result.chunk_; | |
369 | |
370 // Do full rewind of all subsequent chunks. | |
371 for (Chunk* current = seek_result.chunk_->next_; current != nullptr; | |
372 current = current->next_) { | |
373 current->position_ = 0; | |
374 } | |
375 | |
376 size_ = limit; | |
377 } | |
378 | |
379 template <typename T> | |
380 ForwardZoneChunkListIterator<T> ZoneChunkList<T>::Find(const size_t index) { | |
381 SeekResult seek_result = SeekIndex(index); | |
382 return ForwardZoneChunkListIterator<T>(seek_result.chunk_, | |
383 seek_result.chunk_index_); | |
384 } | |
385 | |
386 template <typename T> | |
387 ForwardZoneChunkListIterator<const T> ZoneChunkList<T>::Find( | |
388 const size_t index) const { | |
389 SeekResult seek_result = SeekIndex(index); | |
390 return ForwardZoneChunkListIterator<const T>(seek_result.chunk_, | |
391 seek_result.chunk_index_); | |
392 } | |
393 | |
394 template <typename T> | |
395 ForwardZoneChunkListIterator<T> ZoneChunkList<T>::begin() { | |
396 return ForwardZoneChunkListIterator<T>::Begin(this); | |
397 } | |
398 | |
399 template <typename T> | |
400 ForwardZoneChunkListIterator<T> ZoneChunkList<T>::end() { | |
401 return ForwardZoneChunkListIterator<T>::End(this); | |
402 } | |
403 | |
404 template <typename T> | |
405 ReverseZoneChunkListIterator<T> ZoneChunkList<T>::rbegin() { | |
406 return ReverseZoneChunkListIterator<T>::Begin(this); | |
407 } | |
408 | |
409 template <typename T> | |
410 ReverseZoneChunkListIterator<T> ZoneChunkList<T>::rend() { | |
411 return ReverseZoneChunkListIterator<T>::End(this); | |
412 } | |
413 | |
414 template <typename T> | |
415 ForwardZoneChunkListIterator<const T> ZoneChunkList<T>::begin() const { | |
416 return ForwardZoneChunkListIterator<const T>::Begin(this); | |
417 } | |
418 | |
419 template <typename T> | |
420 ForwardZoneChunkListIterator<const T> ZoneChunkList<T>::end() const { | |
421 return ForwardZoneChunkListIterator<const T>::End(this); | |
422 } | |
423 | |
424 template <typename T> | |
425 ReverseZoneChunkListIterator<const T> ZoneChunkList<T>::rbegin() const { | |
426 return ReverseZoneChunkListIterator<const T>::Begin(this); | |
427 } | |
428 | |
429 template <typename T> | |
430 ReverseZoneChunkListIterator<const T> ZoneChunkList<T>::rend() const { | |
431 return ReverseZoneChunkListIterator<const T>::End(this); | |
432 } | |
433 | |
434 } // namespace internal | |
435 } // namespace v8 | |
436 | |
437 #endif // V8_SRC_ZONE_ZONE_CHUNK_LIST_H_ | |
OLD | NEW |