OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Chromium 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 "cc/base/list_container.h" | |
6 | |
7 #include <algorithm> | |
8 #include <vector> | |
9 | |
10 #include "cc/base/scoped_ptr_vector.h" | |
11 | |
12 namespace { | |
13 const size_t kDefaultNumElementTypesToReserve = 32; | |
14 } // namespace | |
15 | |
16 namespace cc { | |
17 | |
18 // ListContainerCharAllocator | |
19 //////////////////////////////////////////////////// | |
20 // This class deals only with char* and void*. It does allocation and passing | |
21 // out raw pointers, as well as memory deallocation when being destroyed. | |
22 class ListContainerBase::ListContainerCharAllocator { | |
23 public: | |
24 // ListContainerCharAllocator::InnerList | |
25 ///////////////////////////////////////////// | |
26 // This class holds the raw memory chunk, as well as information about its | |
27 // size and availability. | |
28 struct InnerList { | |
29 scoped_ptr<char[]> data; | |
30 // The number of elements in total the memory can hold. The difference | |
31 // between capacity and size is the how many more elements this list can | |
32 // hold. | |
33 size_t capacity; | |
34 // The number of elements have been put into this list. | |
35 size_t size; | |
36 // The size of each element is in bytes. This is used to move from between | |
37 // elements' memory locations. | |
38 size_t step; | |
39 | |
40 InnerList() : capacity(0), size(0), step(0) {} | |
41 | |
42 void Erase(char* position) { | |
43 // Confident that destructor is called by caller of this function. Since | |
44 // ListContainerCharAllocator does not handle construction after | |
45 // allocation, it doesn't handle desctrution before deallocation. | |
46 DCHECK_LE(position, LastElement()); | |
47 DCHECK_GE(position, Begin()); | |
48 char* start = position + step; | |
49 std::copy(start, End(), position); | |
50 | |
51 --size; | |
52 // Decrease capacity to avoid creating not full not last InnerList. | |
53 --capacity; | |
54 } | |
55 | |
56 void InsertBefore(char** position, size_t count) { | |
57 DCHECK_LE(*position, LastElement() + step); | |
58 DCHECK_GE(*position, Begin()); | |
59 | |
60 // Adjust the size and capacity | |
61 size_t old_size = size; | |
62 size += count; | |
63 capacity = size; | |
64 | |
65 // Allocate the new data and update the iterator's pointer. | |
66 scoped_ptr<char[]> new_data(new char[size * step]); | |
67 size_t position_offset = *position - Begin(); | |
68 *position = new_data.get() + position_offset; | |
69 | |
70 // Copy the data before the inserted segment | |
71 memcpy(new_data.get(), data.get(), position_offset); | |
72 // Copy the data after the inserted segment. | |
73 memcpy(new_data.get() + position_offset + count * step, | |
74 data.get() + position_offset, old_size * step - position_offset); | |
75 new_data.swap(data); | |
76 } | |
77 | |
78 bool IsEmpty() const { return !size; } | |
79 bool IsFull() { return capacity == size; } | |
80 size_t NumElementsAvailable() const { return capacity - size; } | |
81 | |
82 void* AddElement() { | |
83 DCHECK_LT(size, capacity); | |
84 ++size; | |
85 return LastElement(); | |
86 } | |
87 | |
88 void RemoveLast() { | |
89 DCHECK(!IsEmpty()); | |
90 --size; | |
91 } | |
92 | |
93 char* Begin() const { return data.get(); } | |
94 char* End() const { return data.get() + size * step; } | |
95 char* LastElement() const { return data.get() + (size - 1) * step; } | |
96 char* ElementAt(size_t index) const { return data.get() + index * step; } | |
97 | |
98 private: | |
99 DISALLOW_COPY_AND_ASSIGN(InnerList); | |
100 }; | |
101 | |
102 explicit ListContainerCharAllocator(size_t element_size) | |
103 : element_size_(element_size), | |
104 size_(0), | |
105 last_list_index_(0), | |
106 last_list_(NULL) { | |
107 AllocateNewList(kDefaultNumElementTypesToReserve); | |
108 last_list_ = storage_[last_list_index_]; | |
109 } | |
110 | |
111 ListContainerCharAllocator(size_t element_size, size_t element_count) | |
112 : element_size_(element_size), | |
113 size_(0), | |
114 last_list_index_(0), | |
115 last_list_(NULL) { | |
116 AllocateNewList(element_count > 0 ? element_count | |
117 : kDefaultNumElementTypesToReserve); | |
118 last_list_ = storage_[last_list_index_]; | |
119 } | |
120 | |
121 ~ListContainerCharAllocator() {} | |
122 | |
123 void* Allocate() { | |
124 if (last_list_->IsFull()) { | |
125 // Only allocate a new list if there isn't a spare one still there from | |
126 // previous usage. | |
127 if (last_list_index_ + 1 >= storage_.size()) | |
128 AllocateNewList(last_list_->capacity * 2); | |
129 | |
130 ++last_list_index_; | |
131 last_list_ = storage_[last_list_index_]; | |
132 } | |
133 | |
134 ++size_; | |
135 return last_list_->AddElement(); | |
136 } | |
137 | |
138 size_t element_size() const { return element_size_; } | |
139 size_t list_count() const { return storage_.size(); } | |
140 size_t size() const { return size_; } | |
141 bool IsEmpty() const { return size() == 0; } | |
142 | |
143 size_t Capacity() const { | |
144 size_t capacity_sum = 0; | |
145 for (const auto& inner_list : storage_) | |
146 capacity_sum += inner_list->capacity; | |
147 return capacity_sum; | |
148 } | |
149 | |
150 void Clear() { | |
151 // Remove all except for the first InnerList. | |
152 DCHECK(!storage_.empty()); | |
153 storage_.erase(storage_.begin() + 1, storage_.end()); | |
154 last_list_index_ = 0; | |
155 last_list_ = storage_[0]; | |
156 last_list_->size = 0; | |
157 size_ = 0; | |
158 } | |
159 | |
160 void RemoveLast() { | |
161 DCHECK(!IsEmpty()); | |
162 last_list_->RemoveLast(); | |
163 if (last_list_->IsEmpty() && last_list_index_ > 0) { | |
164 --last_list_index_; | |
165 last_list_ = storage_[last_list_index_]; | |
166 | |
167 // If there are now two empty inner lists, free one of them. | |
168 if (last_list_index_ + 2 < storage_.size()) | |
169 storage_.pop_back(); | |
170 } | |
171 --size_; | |
172 } | |
173 | |
174 void Erase(PositionInListContainerCharAllocator* position) { | |
175 DCHECK_EQ(this, position->ptr_to_container); | |
176 | |
177 // Update |position| to point to the element after the erased element. | |
178 InnerList* list = storage_[position->vector_index]; | |
179 char* item_iterator = position->item_iterator; | |
180 if (item_iterator == list->LastElement()) | |
181 position->Increment(); | |
182 | |
183 list->Erase(item_iterator); | |
184 // TODO(weiliangc): Free the InnerList if it is empty. | |
185 --size_; | |
186 } | |
187 | |
188 void InsertBefore(ListContainerBase::Iterator* position, size_t count) { | |
189 if (!count) | |
190 return; | |
191 | |
192 // If |position| is End(), then append |count| elements at the end. This | |
193 // will happen to not invalidate any iterators or memory. | |
194 if (!position->item_iterator) { | |
195 // Set |position| to be the first inserted element. | |
196 Allocate(); | |
197 position->vector_index = storage_.size() - 1; | |
198 position->item_iterator = storage_[position->vector_index]->LastElement(); | |
199 // Allocate the rest. | |
200 for (size_t i = 1; i < count; ++i) | |
201 Allocate(); | |
202 } else { | |
203 storage_[position->vector_index]->InsertBefore(&position->item_iterator, | |
204 count); | |
205 size_ += count; | |
206 } | |
207 } | |
208 | |
209 InnerList* InnerListById(size_t id) const { | |
210 DCHECK_LT(id, storage_.size()); | |
211 return storage_[id]; | |
212 } | |
213 | |
214 size_t FirstInnerListId() const { | |
215 // |size_| > 0 means that at least one vector in |storage_| will be | |
216 // non-empty. | |
217 DCHECK_GT(size_, 0u); | |
218 size_t id = 0; | |
219 while (storage_[id]->size == 0) | |
220 ++id; | |
221 return id; | |
222 } | |
223 | |
224 size_t LastInnerListId() const { | |
225 // |size_| > 0 means that at least one vector in |storage_| will be | |
226 // non-empty. | |
227 DCHECK_GT(size_, 0u); | |
228 size_t id = storage_.size() - 1; | |
229 while (storage_[id]->size == 0) | |
230 --id; | |
231 return id; | |
232 } | |
233 | |
234 size_t NumAvailableElementsInLastList() const { | |
235 return last_list_->NumElementsAvailable(); | |
236 } | |
237 | |
238 private: | |
239 void AllocateNewList(size_t list_size) { | |
240 scoped_ptr<InnerList> new_list(new InnerList); | |
241 new_list->capacity = list_size; | |
242 new_list->size = 0; | |
243 new_list->step = element_size_; | |
244 new_list->data.reset(new char[list_size * element_size_]); | |
245 storage_.push_back(new_list.Pass()); | |
246 } | |
247 | |
248 ScopedPtrVector<InnerList> storage_; | |
249 const size_t element_size_; | |
250 | |
251 // The number of elements in the list. | |
252 size_t size_; | |
253 | |
254 // The index of the last list to have had elements added to it, or the only | |
255 // list if the container has not had elements added since being cleared. | |
256 size_t last_list_index_; | |
257 | |
258 // This is equivalent to |storage_[last_list_index_]|. | |
259 InnerList* last_list_; | |
260 | |
261 DISALLOW_COPY_AND_ASSIGN(ListContainerCharAllocator); | |
262 }; | |
263 | |
264 // PositionInListContainerCharAllocator | |
265 ////////////////////////////////////////////////////// | |
266 ListContainerBase::PositionInListContainerCharAllocator:: | |
267 PositionInListContainerCharAllocator( | |
268 const ListContainerBase::PositionInListContainerCharAllocator& other) | |
269 : ptr_to_container(other.ptr_to_container), | |
270 vector_index(other.vector_index), | |
271 item_iterator(other.item_iterator) { | |
272 } | |
273 | |
274 ListContainerBase::PositionInListContainerCharAllocator:: | |
275 PositionInListContainerCharAllocator( | |
276 ListContainerBase::ListContainerCharAllocator* container, | |
277 size_t vector_ind, | |
278 char* item_iter) | |
279 : ptr_to_container(container), | |
280 vector_index(vector_ind), | |
281 item_iterator(item_iter) { | |
282 } | |
283 | |
284 bool ListContainerBase::PositionInListContainerCharAllocator::operator==( | |
285 const ListContainerBase::PositionInListContainerCharAllocator& other) | |
286 const { | |
287 DCHECK_EQ(ptr_to_container, other.ptr_to_container); | |
288 return vector_index == other.vector_index && | |
289 item_iterator == other.item_iterator; | |
290 } | |
291 | |
292 bool ListContainerBase::PositionInListContainerCharAllocator::operator!=( | |
293 const ListContainerBase::PositionInListContainerCharAllocator& other) | |
294 const { | |
295 return !(*this == other); | |
296 } | |
297 | |
298 ListContainerBase::PositionInListContainerCharAllocator | |
299 ListContainerBase::PositionInListContainerCharAllocator::Increment() { | |
300 ListContainerCharAllocator::InnerList* list = | |
301 ptr_to_container->InnerListById(vector_index); | |
302 if (item_iterator == list->LastElement()) { | |
303 ++vector_index; | |
304 while (vector_index < ptr_to_container->list_count()) { | |
305 if (ptr_to_container->InnerListById(vector_index)->size != 0) | |
306 break; | |
307 ++vector_index; | |
308 } | |
309 if (vector_index < ptr_to_container->list_count()) | |
310 item_iterator = ptr_to_container->InnerListById(vector_index)->Begin(); | |
311 else | |
312 item_iterator = NULL; | |
313 } else { | |
314 item_iterator += list->step; | |
315 } | |
316 return *this; | |
317 } | |
318 | |
319 ListContainerBase::PositionInListContainerCharAllocator | |
320 ListContainerBase::PositionInListContainerCharAllocator::ReverseIncrement() { | |
321 ListContainerCharAllocator::InnerList* list = | |
322 ptr_to_container->InnerListById(vector_index); | |
323 if (item_iterator == list->Begin()) { | |
324 --vector_index; | |
325 // Since |vector_index| is unsigned, we compare < list_count() instead of | |
326 // comparing >= 0, as the variable will wrap around when it goes out of | |
327 // range (below 0). | |
328 while (vector_index < ptr_to_container->list_count()) { | |
329 if (ptr_to_container->InnerListById(vector_index)->size != 0) | |
330 break; | |
331 --vector_index; | |
332 } | |
333 if (vector_index < ptr_to_container->list_count()) { | |
334 item_iterator = | |
335 ptr_to_container->InnerListById(vector_index)->LastElement(); | |
336 } else { | |
337 item_iterator = NULL; | |
338 } | |
339 } else { | |
340 item_iterator -= list->step; | |
341 } | |
342 return *this; | |
343 } | |
344 | |
345 // ListContainerBase | |
346 //////////////////////////////////////////// | |
347 ListContainerBase::ListContainerBase(size_t max_size_for_derived_class) | |
348 : data_(new ListContainerCharAllocator(max_size_for_derived_class)) { | |
349 } | |
350 | |
351 ListContainerBase::ListContainerBase(size_t max_size_for_derived_class, | |
352 size_t num_of_elements_to_reserve_for) | |
353 : data_(new ListContainerCharAllocator(max_size_for_derived_class, | |
354 num_of_elements_to_reserve_for)) { | |
355 } | |
356 | |
357 ListContainerBase::~ListContainerBase() { | |
358 } | |
359 | |
360 void ListContainerBase::RemoveLast() { | |
361 data_->RemoveLast(); | |
362 } | |
363 | |
364 void ListContainerBase::EraseAndInvalidateAllPointers( | |
365 ListContainerBase::Iterator* position) { | |
366 data_->Erase(position); | |
367 } | |
368 | |
369 void ListContainerBase::InsertBeforeAndInvalidateAllPointers( | |
370 ListContainerBase::Iterator* position, | |
371 size_t count) { | |
372 data_->InsertBefore(position, count); | |
373 } | |
374 | |
375 ListContainerBase::ConstReverseIterator ListContainerBase::crbegin() const { | |
376 if (data_->IsEmpty()) | |
377 return crend(); | |
378 | |
379 size_t id = data_->LastInnerListId(); | |
380 return ConstReverseIterator(data_.get(), id, | |
381 data_->InnerListById(id)->LastElement(), 0); | |
382 } | |
383 | |
384 ListContainerBase::ConstReverseIterator ListContainerBase::crend() const { | |
385 return ConstReverseIterator(data_.get(), static_cast<size_t>(-1), NULL, | |
386 size()); | |
387 } | |
388 | |
389 ListContainerBase::ReverseIterator ListContainerBase::rbegin() { | |
390 if (data_->IsEmpty()) | |
391 return rend(); | |
392 | |
393 size_t id = data_->LastInnerListId(); | |
394 return ReverseIterator(data_.get(), id, | |
395 data_->InnerListById(id)->LastElement(), 0); | |
396 } | |
397 | |
398 ListContainerBase::ReverseIterator ListContainerBase::rend() { | |
399 return ReverseIterator(data_.get(), static_cast<size_t>(-1), NULL, size()); | |
400 } | |
401 | |
402 ListContainerBase::ConstIterator ListContainerBase::cbegin() const { | |
403 if (data_->IsEmpty()) | |
404 return cend(); | |
405 | |
406 size_t id = data_->FirstInnerListId(); | |
407 return ConstIterator(data_.get(), id, data_->InnerListById(id)->Begin(), 0); | |
408 } | |
409 | |
410 ListContainerBase::ConstIterator ListContainerBase::cend() const { | |
411 if (data_->IsEmpty()) | |
412 return ConstIterator(data_.get(), 0, NULL, size()); | |
413 | |
414 size_t id = data_->list_count(); | |
415 return ConstIterator(data_.get(), id, NULL, size()); | |
416 } | |
417 | |
418 ListContainerBase::Iterator ListContainerBase::begin() { | |
419 if (data_->IsEmpty()) | |
420 return end(); | |
421 | |
422 size_t id = data_->FirstInnerListId(); | |
423 return Iterator(data_.get(), id, data_->InnerListById(id)->Begin(), 0); | |
424 } | |
425 | |
426 ListContainerBase::Iterator ListContainerBase::end() { | |
427 if (data_->IsEmpty()) | |
428 return Iterator(data_.get(), 0, NULL, size()); | |
429 | |
430 size_t id = data_->list_count(); | |
431 return Iterator(data_.get(), id, NULL, size()); | |
432 } | |
433 | |
434 ListContainerBase::ConstIterator ListContainerBase::IteratorAt( | |
435 size_t index) const { | |
436 DCHECK_LT(index, size()); | |
437 size_t original_index = index; | |
438 size_t list_index; | |
439 for (list_index = 0; list_index < data_->list_count(); ++list_index) { | |
440 size_t current_size = data_->InnerListById(list_index)->size; | |
441 if (index < current_size) | |
442 break; | |
443 index -= current_size; | |
444 } | |
445 return ConstIterator(data_.get(), list_index, | |
446 data_->InnerListById(list_index)->ElementAt(index), | |
447 original_index); | |
448 } | |
449 | |
450 ListContainerBase::Iterator ListContainerBase::IteratorAt(size_t index) { | |
451 DCHECK_LT(index, size()); | |
452 size_t original_index = index; | |
453 size_t list_index; | |
454 for (list_index = 0; list_index < data_->list_count(); ++list_index) { | |
455 size_t current_size = data_->InnerListById(list_index)->size; | |
456 if (index < current_size) | |
457 break; | |
458 index -= current_size; | |
459 } | |
460 return Iterator(data_.get(), list_index, | |
461 data_->InnerListById(list_index)->ElementAt(index), | |
462 original_index); | |
463 } | |
464 | |
465 void* ListContainerBase::Allocate(size_t size_of_actual_element_in_bytes) { | |
466 DCHECK_LE(size_of_actual_element_in_bytes, data_->element_size()); | |
467 return data_->Allocate(); | |
468 } | |
469 | |
470 size_t ListContainerBase::size() const { | |
471 return data_->size(); | |
472 } | |
473 | |
474 bool ListContainerBase::empty() const { | |
475 return data_->IsEmpty(); | |
476 } | |
477 | |
478 size_t ListContainerBase::MaxSizeForDerivedClass() const { | |
479 return data_->element_size(); | |
480 } | |
481 | |
482 size_t ListContainerBase::GetCapacityInBytes() const { | |
483 return data_->Capacity() * data_->element_size(); | |
484 } | |
485 | |
486 void ListContainerBase::clear() { | |
487 data_->Clear(); | |
488 } | |
489 | |
490 size_t ListContainerBase::AvailableSizeWithoutAnotherAllocationForTesting() | |
491 const { | |
492 return data_->NumAvailableElementsInLastList(); | |
493 } | |
494 | |
495 // ListContainerBase::Iterator | |
496 ///////////////////////////////////////////////// | |
497 ListContainerBase::Iterator::Iterator(ListContainerCharAllocator* container, | |
498 size_t vector_ind, | |
499 char* item_iter, | |
500 size_t index) | |
501 : PositionInListContainerCharAllocator(container, vector_ind, item_iter), | |
502 index_(index) { | |
503 } | |
504 | |
505 ListContainerBase::Iterator::~Iterator() { | |
506 } | |
507 | |
508 size_t ListContainerBase::Iterator::index() const { | |
509 return index_; | |
510 } | |
511 | |
512 // ListContainerBase::ConstIterator | |
513 ///////////////////////////////////////////////// | |
514 ListContainerBase::ConstIterator::ConstIterator( | |
515 const ListContainerBase::Iterator& other) | |
516 : PositionInListContainerCharAllocator(other), index_(other.index()) { | |
517 } | |
518 | |
519 ListContainerBase::ConstIterator::ConstIterator( | |
520 ListContainerCharAllocator* container, | |
521 size_t vector_ind, | |
522 char* item_iter, | |
523 size_t index) | |
524 : PositionInListContainerCharAllocator(container, vector_ind, item_iter), | |
525 index_(index) { | |
526 } | |
527 | |
528 ListContainerBase::ConstIterator::~ConstIterator() { | |
529 } | |
530 | |
531 size_t ListContainerBase::ConstIterator::index() const { | |
532 return index_; | |
533 } | |
534 | |
535 // ListContainerBase::ReverseIterator | |
536 ///////////////////////////////////////////////// | |
537 ListContainerBase::ReverseIterator::ReverseIterator( | |
538 ListContainerCharAllocator* container, | |
539 size_t vector_ind, | |
540 char* item_iter, | |
541 size_t index) | |
542 : PositionInListContainerCharAllocator(container, vector_ind, item_iter), | |
543 index_(index) { | |
544 } | |
545 | |
546 ListContainerBase::ReverseIterator::~ReverseIterator() { | |
547 } | |
548 | |
549 size_t ListContainerBase::ReverseIterator::index() const { | |
550 return index_; | |
551 } | |
552 | |
553 // ListContainerBase::ConstReverseIterator | |
554 ///////////////////////////////////////////////// | |
555 ListContainerBase::ConstReverseIterator::ConstReverseIterator( | |
556 const ListContainerBase::ReverseIterator& other) | |
557 : PositionInListContainerCharAllocator(other), index_(other.index()) { | |
558 } | |
559 | |
560 ListContainerBase::ConstReverseIterator::ConstReverseIterator( | |
561 ListContainerCharAllocator* container, | |
562 size_t vector_ind, | |
563 char* item_iter, | |
564 size_t index) | |
565 : PositionInListContainerCharAllocator(container, vector_ind, item_iter), | |
566 index_(index) { | |
567 } | |
568 | |
569 ListContainerBase::ConstReverseIterator::~ConstReverseIterator() { | |
570 } | |
571 | |
572 size_t ListContainerBase::ConstReverseIterator::index() const { | |
573 return index_; | |
574 } | |
575 | |
576 } // namespace cc | |
OLD | NEW |