blob: 298ce516cc01ffa1b0dce0cb872d286730f72ed3 [file] [log] [blame]
bsalomon@google.combbe52902012-12-03 18:01:45 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
bsalomon@google.com4c2443e2012-12-06 20:58:57 +00008#ifndef SkTLList_DEFINED
9#define SkTLList_DEFINED
10
bsalomon@google.combbe52902012-12-03 18:01:45 +000011#include "SkTInternalLList.h"
12#include "SkTemplates.h"
13
bsalomon@google.comdd3f7a92012-12-03 19:47:41 +000014template <typename T> class SkTLList;
15template <typename T>
16inline void* operator new(size_t, SkTLList<T>* list,
17 typename SkTLList<T>::Placement placement,
18 const typename SkTLList<T>::Iter& location);
19
bsalomon@google.combbe52902012-12-03 18:01:45 +000020/** Doubly-linked list of objects. The objects' lifetimes are controlled by the list. I.e. the
21 the list creates the objects and they are deleted upon removal. This class block-allocates
bsalomon@google.comdd3f7a92012-12-03 19:47:41 +000022 space for entries based on a param passed to the constructor.
23
24 Elements of the list can be constructed in place using the following macros:
25 SkNEW_INSERT_IN_LLIST_BEFORE(list, location, type_name, args)
26 SkNEW_INSERT_IN_LLIST_AFTER(list, location, type_name, args)
27 where list is a SkTLList<type_name>*, location is an iterator, and args is the paren-surrounded
28 constructor arguments for type_name. These macros behave like addBefore() and addAfter().
29*/
bsalomon@google.combbe52902012-12-03 18:01:45 +000030template <typename T>
31class SkTLList : public SkNoncopyable {
32private:
33 struct Block;
34 struct Node {
35 char fObj[sizeof(T)];
36 SK_DECLARE_INTERNAL_LLIST_INTERFACE(Node);
37 Block* fBlock; // owning block.
38 };
39 typedef SkTInternalLList<Node> NodeList;
40
41public:
bsalomon@google.comdd3f7a92012-12-03 19:47:41 +000042
43 class Iter;
44
bsalomon@google.combbe52902012-12-03 18:01:45 +000045 /** allocCnt is the number of objects to allocate as a group. In the worst case fragmentation
46 each object is using the space required for allocCnt unfragmented objects. */
47 SkTLList(int allocCnt = 1) : fCount(0), fAllocCnt(allocCnt) {
48 SkASSERT(allocCnt > 0);
49 this->validate();
50 }
51
52 ~SkTLList() {
53 this->validate();
54 typename NodeList::Iter iter;
55 Node* node = iter.init(fList, Iter::kHead_IterStart);
56 while (NULL != node) {
bsalomon@google.comdd3f7a92012-12-03 19:47:41 +000057 SkTCast<T*>(node->fObj)->~T();
bsalomon@google.combbe52902012-12-03 18:01:45 +000058 Block* block = node->fBlock;
59 node = iter.next();
60 if (0 == --block->fNodesInUse) {
61 for (int i = 0; i < fAllocCnt; ++i) {
62 block->fNodes[i].~Node();
63 }
64 sk_free(block);
65 }
66 }
67 }
skia.committer@gmail.come659c2e2012-12-04 02:01:25 +000068
bsalomon@google.comc6b3e482012-12-07 20:43:52 +000069 T* addToHead(const T& t) {
bsalomon@google.combbe52902012-12-03 18:01:45 +000070 this->validate();
71 Node* node = this->createNode();
72 fList.addToHead(node);
73 SkNEW_PLACEMENT_ARGS(node->fObj, T, (t));
74 this->validate();
bsalomon@google.comc6b3e482012-12-07 20:43:52 +000075 return reinterpret_cast<T*>(node->fObj);
bsalomon@google.combbe52902012-12-03 18:01:45 +000076 }
77
bsalomon@google.comc6b3e482012-12-07 20:43:52 +000078 T* addToTail(const T& t) {
bsalomon@google.combbe52902012-12-03 18:01:45 +000079 this->validate();
80 Node* node = this->createNode();
81 fList.addToTail(node);
82 SkNEW_PLACEMENT_ARGS(node->fObj, T, (t));
83 this->validate();
bsalomon@google.comc6b3e482012-12-07 20:43:52 +000084 return reinterpret_cast<T*>(node->fObj);
bsalomon@google.combbe52902012-12-03 18:01:45 +000085 }
86
bsalomon@google.comdd3f7a92012-12-03 19:47:41 +000087 /** Adds a new element to the list before the location indicated by the iterator. If the
88 iterator refers to a NULL location then the new element is added at the tail */
bsalomon@google.comc6b3e482012-12-07 20:43:52 +000089 T* addBefore(const T& t, const Iter& location) {
90 return SkNEW_PLACEMENT_ARGS(this->internalAddBefore(location), T, (t));
bsalomon@google.comdd3f7a92012-12-03 19:47:41 +000091 }
92
93 /** Adds a new element to the list after the location indicated by the iterator. If the
94 iterator refers to a NULL location then the new element is added at the head */
bsalomon@google.comc6b3e482012-12-07 20:43:52 +000095 T* addAfter(const T& t, const Iter& location) {
96 return SkNEW_PLACEMENT_ARGS(this->internalAddAfter(location), T, (t));
bsalomon@google.comdd3f7a92012-12-03 19:47:41 +000097 }
98
99 /** Convenience methods for getting an iterator initialized to the head/tail of the list. */
100 Iter headIter() const { return Iter(*this, Iter::kHead_IterStart); }
101 Iter tailIter() const { return Iter(*this, Iter::kTail_IterStart); }
102
bsalomon@google.com4c2443e2012-12-06 20:58:57 +0000103 T* head() { return Iter(*this, Iter::kHead_IterStart).get(); }
104 T* tail() { return Iter(*this, Iter::kTail_IterStart).get(); }
105 const T* head() const { return Iter(*this, Iter::kHead_IterStart).get(); }
106 const T* tail() const { return Iter(*this, Iter::kTail_IterStart).get(); }
107
bsalomon@google.combbe52902012-12-03 18:01:45 +0000108 void popHead() {
109 this->validate();
110 Node* node = fList.head();
111 if (NULL != node) {
112 this->removeNode(node);
113 }
114 this->validate();
115 }
116
117 void popTail() {
118 this->validate();
119 Node* node = fList.head();
120 if (NULL != node) {
121 this->removeNode(node);
122 }
123 this->validate();
124 }
125
126 void remove(T* t) {
127 this->validate();
128 Node* node = reinterpret_cast<Node*>(t);
129 SkASSERT(reinterpret_cast<T*>(node->fObj) == t);
130 this->removeNode(node);
131 this->validate();
132 }
133
134 void reset() {
135 this->validate();
136 Iter iter(*this, Iter::kHead_IterStart);
137 while (iter.get()) {
138 Iter next = iter;
139 next.next();
140 this->remove(iter.get());
141 iter = next;
142 }
143 SkASSERT(0 == fCount);
144 this->validate();
145 }
146
147 int count() const { return fCount; }
148 bool isEmpty() const { this->validate(); return 0 == fCount; }
149
150 bool operator== (const SkTLList& list) const {
151 if (this == &list) {
152 return true;
153 }
154 if (fCount != list.fCount) {
155 return false;
156 }
157 for (Iter a(*this, Iter::kHead_IterStart), b(list, Iter::kHead_IterStart);
158 a.get();
159 a.next(), b.next()) {
160 SkASSERT(NULL != b.get()); // already checked that counts match.
161 if (!(*a.get() == *b.get())) {
162 return false;
163 }
164 }
165 return true;
166 }
167 bool operator!= (const SkTLList& list) const { return !(*this == list); }
168
169 /** The iterator becomes invalid if the element it refers to is removed from the list. */
170 class Iter : private NodeList::Iter {
171 private:
172 typedef typename NodeList::Iter INHERITED;
173
174 public:
175 typedef typename INHERITED::IterStart IterStart;
176 //!< Start the iterator at the head of the list.
177 static const IterStart kHead_IterStart = INHERITED::kHead_IterStart;
178 //!< Start the iterator at the tail of the list.
179 static const IterStart kTail_IterStart = INHERITED::kTail_IterStart;
180
181 Iter() {}
182
183 Iter(const SkTLList& list, IterStart start) {
184 INHERITED::init(list.fList, start);
185 }
186
187 T* init(const SkTLList& list, IterStart start) {
188 return this->nodeToObj(INHERITED::init(list.fList, start));
189 }
190
191 T* get() { return this->nodeToObj(INHERITED::get()); }
192
193 T* next() { return this->nodeToObj(INHERITED::next()); }
194
195 T* prev() { return this->nodeToObj(INHERITED::prev()); }
196
197 Iter& operator= (const Iter& iter) { INHERITED::operator=(iter); return *this; }
198
199 private:
bsalomon@google.comdd3f7a92012-12-03 19:47:41 +0000200 friend class SkTLList;
201 Node* getNode() { return INHERITED::get(); }
202
bsalomon@google.combbe52902012-12-03 18:01:45 +0000203 T* nodeToObj(Node* node) {
204 if (NULL != node) {
205 return reinterpret_cast<T*>(node->fObj);
206 } else {
207 return NULL;
208 }
209 }
210 };
211
bsalomon@google.comdd3f7a92012-12-03 19:47:41 +0000212 // For use with operator new
213 enum Placement {
214 kBefore_Placement,
215 kAfter_Placement,
216 };
217
bsalomon@google.combbe52902012-12-03 18:01:45 +0000218private:
219 struct Block {
220 int fNodesInUse;
221 Node fNodes[1];
222 };
223
224 size_t blockSize() const { return sizeof(Block) + sizeof(Node) * (fAllocCnt-1); }
225
226 Node* createNode() {
227 Node* node = fFreeList.head();
228 if (NULL != node) {
229 fFreeList.remove(node);
230 ++node->fBlock->fNodesInUse;
231 } else {
232 Block* block = reinterpret_cast<Block*>(sk_malloc_flags(this->blockSize(), 0));
233 node = &block->fNodes[0];
234 SkNEW_PLACEMENT(node, Node);
235 node->fBlock = block;
236 block->fNodesInUse = 1;
237 for (int i = 1; i < fAllocCnt; ++i) {
238 SkNEW_PLACEMENT(block->fNodes + i, Node);
239 fFreeList.addToHead(block->fNodes + i);
240 block->fNodes[i].fBlock = block;
241 }
242 }
243 ++fCount;
244 return node;
245 }
246
247 void removeNode(Node* node) {
248 SkASSERT(NULL != node);
249 fList.remove(node);
bsalomon@google.comdd3f7a92012-12-03 19:47:41 +0000250 SkTCast<T*>(node->fObj)->~T();
bsalomon@google.combbe52902012-12-03 18:01:45 +0000251 if (0 == --node->fBlock->fNodesInUse) {
bsalomon@google.combbe52902012-12-03 18:01:45 +0000252 Block* block = node->fBlock;
253 for (int i = 0; i < fAllocCnt; ++i) {
254 if (block->fNodes + i != node) {
255 fFreeList.remove(block->fNodes + i);
256 }
257 block->fNodes[i].~Node();
258 }
259 sk_free(block);
260 } else {
261 fFreeList.addToHead(node);
262 }
263 --fCount;
264 this->validate();
265 }
266
267 void validate() const {
268#ifdef SK_DEBUG
269 SkASSERT((0 == fCount) == fList.isEmpty());
270 SkASSERT((0 != fCount) || fFreeList.isEmpty());
271
272 fList.validate();
273 fFreeList.validate();
274 typename NodeList::Iter iter;
275 Node* freeNode = iter.init(fFreeList, Iter::kHead_IterStart);
276 while (freeNode) {
277 SkASSERT(fFreeList.isInList(freeNode));
278 Block* block = freeNode->fBlock;
279 SkASSERT(block->fNodesInUse > 0 && block->fNodesInUse < fAllocCnt);
280
281 int activeCnt = 0;
282 int freeCnt = 0;
283 for (int i = 0; i < fAllocCnt; ++i) {
284 bool free = fFreeList.isInList(block->fNodes + i);
285 bool active = fList.isInList(block->fNodes + i);
286 SkASSERT(free != active);
287 activeCnt += active;
288 freeCnt += free;
289 }
290 SkASSERT(activeCnt == block->fNodesInUse);
291 freeNode = iter.next();
292 }
293
294 int count = 0;
295 Node* activeNode = iter.init(fList, Iter::kHead_IterStart);
296 while (activeNode) {
297 ++count;
298 SkASSERT(fList.isInList(activeNode));
299 Block* block = activeNode->fBlock;
300 SkASSERT(block->fNodesInUse > 0 && block->fNodesInUse <= fAllocCnt);
301
302 int activeCnt = 0;
303 int freeCnt = 0;
304 for (int i = 0; i < fAllocCnt; ++i) {
305 bool free = fFreeList.isInList(block->fNodes + i);
306 bool active = fList.isInList(block->fNodes + i);
307 SkASSERT(free != active);
308 activeCnt += active;
309 freeCnt += free;
310 }
311 SkASSERT(activeCnt == block->fNodesInUse);
312 activeNode = iter.next();
313 }
314 SkASSERT(count == fCount);
315#endif
316 }
317
bsalomon@google.comdd3f7a92012-12-03 19:47:41 +0000318 // Support in-place initializing of objects inserted into the list via operator new.
319 friend void* operator new<T>(size_t,
320 SkTLList* list,
321 Placement placement,
322 const Iter& location);
323
324
325 // Helpers that insert the node and returns a pointer to where the new object should be init'ed.
326 void* internalAddBefore(Iter location) {
327 this->validate();
328 Node* node = this->createNode();
329 fList.addBefore(node, location.getNode());
330 this->validate();
331 return node->fObj;
332 }
333
334 void* internalAddAfter(Iter location) {
335 this->validate();
336 Node* node = this->createNode();
337 fList.addAfter(node, location.getNode());
338 this->validate();
339 return node->fObj;
340 }
341
bsalomon@google.combbe52902012-12-03 18:01:45 +0000342 NodeList fList;
343 NodeList fFreeList;
344 int fCount;
345 int fAllocCnt;
bsalomon@google.comdd3f7a92012-12-03 19:47:41 +0000346
bsalomon@google.combbe52902012-12-03 18:01:45 +0000347};
bsalomon@google.comdd3f7a92012-12-03 19:47:41 +0000348
349// Use the below macros rather than calling this directly
350template <typename T>
351void *operator new(size_t, SkTLList<T>* list,
352 typename SkTLList<T>::Placement placement,
353 const typename SkTLList<T>::Iter& location) {
354 SkASSERT(NULL != list);
355 if (SkTLList<T>::kBefore_Placement == placement) {
356 return list->internalAddBefore(location);
357 } else {
358 return list->internalAddAfter(location);
359 }
360}
361
bsalomon@google.com8958dc92012-12-05 14:51:39 +0000362// Skia doesn't use C++ exceptions but it may be compiled with them enabled. Having an op delete
363// to match the op new silences warnings about missing op delete when a constructor throws an
364// exception.
365template <typename T>
366void operator delete(void*,
367 SkTLList<T>*,
368 typename SkTLList<T>::Placement,
369 const typename SkTLList<T>::Iter&) {
370 SK_CRASH();
371}
372
bsalomon@google.comdd3f7a92012-12-03 19:47:41 +0000373#define SkNEW_INSERT_IN_LLIST_BEFORE(list, location, type_name, args) \
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000374 (new ((list), SkTLList< type_name >::kBefore_Placement, (location)) type_name args)
bsalomon@google.comdd3f7a92012-12-03 19:47:41 +0000375
376#define SkNEW_INSERT_IN_LLIST_AFTER(list, location, type_name, args) \
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000377 (new ((list), SkTLList< type_name >::kAfter_Placement, (location)) type_name args)
378
379#define SkNEW_INSERT_AT_LLIST_HEAD(list, type_name, args) \
380 SkNEW_INSERT_IN_LLIST_BEFORE((list), (list)->headIter(), type_name, args)
381
382#define SkNEW_INSERT_AT_LLIST_TAIL(list, type_name, args) \
bsalomon@google.comebce0302012-12-04 14:48:57 +0000383 SkNEW_INSERT_IN_LLIST_AFTER((list), (list)->tailIter(), type_name, args)
384
bsalomon@google.com4c2443e2012-12-06 20:58:57 +0000385#endif