OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2011 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 | |
8 #ifndef SkTArray_DEFINED | |
9 #define SkTArray_DEFINED | |
10 | |
11 #include "../private/SkTLogic.h" | |
12 #include "../private/SkTemplates.h" | |
13 #include "SkTypes.h" | |
14 | |
15 #include <new> | |
16 #include <utility> | |
17 | |
18 /** When MEM_COPY is true T will be bit copied when moved. | |
19 When MEM_COPY is false, T will be copy constructed / destructed. | |
20 In all cases T will be default-initialized on allocation, | |
21 and its destructor will be called from this object's destructor. | |
22 */ | |
23 template <typename T, bool MEM_COPY = false> class SkTArray { | |
24 public: | |
25 /** | |
26 * Creates an empty array with no initial storage | |
27 */ | |
28 SkTArray() { | |
29 fCount = 0; | |
30 fReserveCount = gMIN_ALLOC_COUNT; | |
31 fAllocCount = 0; | |
32 fMemArray = NULL; | |
33 fPreAllocMemArray = NULL; | |
34 } | |
35 | |
36 /** | |
37 * Creates an empty array that will preallocate space for reserveCount | |
38 * elements. | |
39 */ | |
40 explicit SkTArray(int reserveCount) { | |
41 this->init(NULL, 0, NULL, reserveCount); | |
42 } | |
43 | |
44 /** | |
45 * Copies one array to another. The new array will be heap allocated. | |
46 */ | |
47 explicit SkTArray(const SkTArray& array) { | |
48 this->init(array.fItemArray, array.fCount, NULL, 0); | |
49 } | |
50 | |
51 /** | |
52 * Creates a SkTArray by copying contents of a standard C array. The new | |
53 * array will be heap allocated. Be careful not to use this constructor | |
54 * when you really want the (void*, int) version. | |
55 */ | |
56 SkTArray(const T* array, int count) { | |
57 this->init(array, count, NULL, 0); | |
58 } | |
59 | |
60 /** | |
61 * assign copy of array to this | |
62 */ | |
63 SkTArray& operator =(const SkTArray& array) { | |
64 for (int i = 0; i < fCount; ++i) { | |
65 fItemArray[i].~T(); | |
66 } | |
67 fCount = 0; | |
68 this->checkRealloc((int)array.count()); | |
69 fCount = array.count(); | |
70 this->copy(static_cast<const T*>(array.fMemArray)); | |
71 return *this; | |
72 } | |
73 | |
74 ~SkTArray() { | |
75 for (int i = 0; i < fCount; ++i) { | |
76 fItemArray[i].~T(); | |
77 } | |
78 if (fMemArray != fPreAllocMemArray) { | |
79 sk_free(fMemArray); | |
80 } | |
81 } | |
82 | |
83 /** | |
84 * Resets to count() == 0 | |
85 */ | |
86 void reset() { this->pop_back_n(fCount); } | |
87 | |
88 /** | |
89 * Resets to count() = n newly constructed T objects. | |
90 */ | |
91 void reset(int n) { | |
92 SkASSERT(n >= 0); | |
93 for (int i = 0; i < fCount; ++i) { | |
94 fItemArray[i].~T(); | |
95 } | |
96 // set fCount to 0 before calling checkRealloc so that no copy cons. are
called. | |
97 fCount = 0; | |
98 this->checkRealloc(n); | |
99 fCount = n; | |
100 for (int i = 0; i < fCount; ++i) { | |
101 new (fItemArray + i) T; | |
102 } | |
103 } | |
104 | |
105 /** | |
106 * Resets to a copy of a C array. | |
107 */ | |
108 void reset(const T* array, int count) { | |
109 for (int i = 0; i < fCount; ++i) { | |
110 fItemArray[i].~T(); | |
111 } | |
112 int delta = count - fCount; | |
113 this->checkRealloc(delta); | |
114 fCount = count; | |
115 this->copy(array); | |
116 } | |
117 | |
118 void removeShuffle(int n) { | |
119 SkASSERT(n < fCount); | |
120 int newCount = fCount - 1; | |
121 fCount = newCount; | |
122 fItemArray[n].~T(); | |
123 if (n != newCount) { | |
124 this->move(n, newCount); | |
125 } | |
126 } | |
127 | |
128 /** | |
129 * Number of elements in the array. | |
130 */ | |
131 int count() const { return fCount; } | |
132 | |
133 /** | |
134 * Is the array empty. | |
135 */ | |
136 bool empty() const { return !fCount; } | |
137 | |
138 /** | |
139 * Adds 1 new default-initialized T value and returns it by reference. Note | |
140 * the reference only remains valid until the next call that adds or removes | |
141 * elements. | |
142 */ | |
143 T& push_back() { | |
144 T* newT = reinterpret_cast<T*>(this->push_back_raw(1)); | |
145 new (newT) T; | |
146 return *newT; | |
147 } | |
148 | |
149 /** | |
150 * Version of above that uses a copy constructor to initialize the new item | |
151 */ | |
152 T& push_back(const T& t) { | |
153 T* newT = reinterpret_cast<T*>(this->push_back_raw(1)); | |
154 new (newT) T(t); | |
155 return *newT; | |
156 } | |
157 | |
158 /** | |
159 * Construct a new T at the back of this array. | |
160 */ | |
161 template<class... Args> T& emplace_back(Args&&... args) { | |
162 T* newT = reinterpret_cast<T*>(this->push_back_raw(1)); | |
163 return *new (newT) T(std::forward<Args>(args)...); | |
164 } | |
165 | |
166 /** | |
167 * Allocates n more default-initialized T values, and returns the address of | |
168 * the start of that new range. Note: this address is only valid until the | |
169 * next API call made on the array that might add or remove elements. | |
170 */ | |
171 T* push_back_n(int n) { | |
172 SkASSERT(n >= 0); | |
173 T* newTs = reinterpret_cast<T*>(this->push_back_raw(n)); | |
174 for (int i = 0; i < n; ++i) { | |
175 new (newTs + i) T; | |
176 } | |
177 return newTs; | |
178 } | |
179 | |
180 /** | |
181 * Version of above that uses a copy constructor to initialize all n items | |
182 * to the same T. | |
183 */ | |
184 T* push_back_n(int n, const T& t) { | |
185 SkASSERT(n >= 0); | |
186 T* newTs = reinterpret_cast<T*>(this->push_back_raw(n)); | |
187 for (int i = 0; i < n; ++i) { | |
188 new (newTs[i]) T(t); | |
189 } | |
190 return newTs; | |
191 } | |
192 | |
193 /** | |
194 * Version of above that uses a copy constructor to initialize the n items | |
195 * to separate T values. | |
196 */ | |
197 T* push_back_n(int n, const T t[]) { | |
198 SkASSERT(n >= 0); | |
199 this->checkRealloc(n); | |
200 for (int i = 0; i < n; ++i) { | |
201 new (fItemArray + fCount + i) T(t[i]); | |
202 } | |
203 fCount += n; | |
204 return fItemArray + fCount - n; | |
205 } | |
206 | |
207 /** | |
208 * Removes the last element. Not safe to call when count() == 0. | |
209 */ | |
210 void pop_back() { | |
211 SkASSERT(fCount > 0); | |
212 --fCount; | |
213 fItemArray[fCount].~T(); | |
214 this->checkRealloc(0); | |
215 } | |
216 | |
217 /** | |
218 * Removes the last n elements. Not safe to call when count() < n. | |
219 */ | |
220 void pop_back_n(int n) { | |
221 SkASSERT(n >= 0); | |
222 SkASSERT(fCount >= n); | |
223 fCount -= n; | |
224 for (int i = 0; i < n; ++i) { | |
225 fItemArray[fCount + i].~T(); | |
226 } | |
227 this->checkRealloc(0); | |
228 } | |
229 | |
230 /** | |
231 * Pushes or pops from the back to resize. Pushes will be default | |
232 * initialized. | |
233 */ | |
234 void resize_back(int newCount) { | |
235 SkASSERT(newCount >= 0); | |
236 | |
237 if (newCount > fCount) { | |
238 this->push_back_n(newCount - fCount); | |
239 } else if (newCount < fCount) { | |
240 this->pop_back_n(fCount - newCount); | |
241 } | |
242 } | |
243 | |
244 /** Swaps the contents of this array with that array. Does a pointer swap if
possible, | |
245 otherwise copies the T values. */ | |
246 void swap(SkTArray* that) { | |
247 if (this == that) { | |
248 return; | |
249 } | |
250 if (this->fPreAllocMemArray != this->fItemArray && | |
251 that->fPreAllocMemArray != that->fItemArray) { | |
252 // If neither is using a preallocated array then just swap. | |
253 SkTSwap(fItemArray, that->fItemArray); | |
254 SkTSwap(fCount, that->fCount); | |
255 SkTSwap(fAllocCount, that->fAllocCount); | |
256 } else { | |
257 // This could be more optimal... | |
258 SkTArray copy(*that); | |
259 *that = *this; | |
260 *this = copy; | |
261 } | |
262 } | |
263 | |
264 T* begin() { | |
265 return fItemArray; | |
266 } | |
267 const T* begin() const { | |
268 return fItemArray; | |
269 } | |
270 T* end() { | |
271 return fItemArray ? fItemArray + fCount : NULL; | |
272 } | |
273 const T* end() const { | |
274 return fItemArray ? fItemArray + fCount : NULL; | |
275 } | |
276 | |
277 /** | |
278 * Get the i^th element. | |
279 */ | |
280 T& operator[] (int i) { | |
281 SkASSERT(i < fCount); | |
282 SkASSERT(i >= 0); | |
283 return fItemArray[i]; | |
284 } | |
285 | |
286 const T& operator[] (int i) const { | |
287 SkASSERT(i < fCount); | |
288 SkASSERT(i >= 0); | |
289 return fItemArray[i]; | |
290 } | |
291 | |
292 /** | |
293 * equivalent to operator[](0) | |
294 */ | |
295 T& front() { SkASSERT(fCount > 0); return fItemArray[0];} | |
296 | |
297 const T& front() const { SkASSERT(fCount > 0); return fItemArray[0];} | |
298 | |
299 /** | |
300 * equivalent to operator[](count() - 1) | |
301 */ | |
302 T& back() { SkASSERT(fCount); return fItemArray[fCount - 1];} | |
303 | |
304 const T& back() const { SkASSERT(fCount > 0); return fItemArray[fCount - 1];
} | |
305 | |
306 /** | |
307 * equivalent to operator[](count()-1-i) | |
308 */ | |
309 T& fromBack(int i) { | |
310 SkASSERT(i >= 0); | |
311 SkASSERT(i < fCount); | |
312 return fItemArray[fCount - i - 1]; | |
313 } | |
314 | |
315 const T& fromBack(int i) const { | |
316 SkASSERT(i >= 0); | |
317 SkASSERT(i < fCount); | |
318 return fItemArray[fCount - i - 1]; | |
319 } | |
320 | |
321 bool operator==(const SkTArray<T, MEM_COPY>& right) const { | |
322 int leftCount = this->count(); | |
323 if (leftCount != right.count()) { | |
324 return false; | |
325 } | |
326 for (int index = 0; index < leftCount; ++index) { | |
327 if (fItemArray[index] != right.fItemArray[index]) { | |
328 return false; | |
329 } | |
330 } | |
331 return true; | |
332 } | |
333 | |
334 bool operator!=(const SkTArray<T, MEM_COPY>& right) const { | |
335 return !(*this == right); | |
336 } | |
337 | |
338 protected: | |
339 /** | |
340 * Creates an empty array that will use the passed storage block until it | |
341 * is insufficiently large to hold the entire array. | |
342 */ | |
343 template <int N> | |
344 SkTArray(SkAlignedSTStorage<N,T>* storage) { | |
345 this->init(NULL, 0, storage->get(), N); | |
346 } | |
347 | |
348 /** | |
349 * Copy another array, using preallocated storage if preAllocCount >= | |
350 * array.count(). Otherwise storage will only be used when array shrinks | |
351 * to fit. | |
352 */ | |
353 template <int N> | |
354 SkTArray(const SkTArray& array, SkAlignedSTStorage<N,T>* storage) { | |
355 this->init(array.fItemArray, array.fCount, storage->get(), N); | |
356 } | |
357 | |
358 /** | |
359 * Copy a C array, using preallocated storage if preAllocCount >= | |
360 * count. Otherwise storage will only be used when array shrinks | |
361 * to fit. | |
362 */ | |
363 template <int N> | |
364 SkTArray(const T* array, int count, SkAlignedSTStorage<N,T>* storage) { | |
365 this->init(array, count, storage->get(), N); | |
366 } | |
367 | |
368 void init(const T* array, int count, | |
369 void* preAllocStorage, int preAllocOrReserveCount) { | |
370 SkASSERT(count >= 0); | |
371 SkASSERT(preAllocOrReserveCount >= 0); | |
372 fCount = count; | |
373 fReserveCount = (preAllocOrReserveCount > 0) ? | |
374 preAllocOrReserveCount : | |
375 gMIN_ALLOC_COUNT; | |
376 fPreAllocMemArray = preAllocStorage; | |
377 if (fReserveCount >= fCount && | |
378 preAllocStorage) { | |
379 fAllocCount = fReserveCount; | |
380 fMemArray = preAllocStorage; | |
381 } else { | |
382 fAllocCount = SkMax32(fCount, fReserveCount); | |
383 fMemArray = sk_malloc_throw(fAllocCount * sizeof(T)); | |
384 } | |
385 | |
386 this->copy(array); | |
387 } | |
388 | |
389 private: | |
390 /** In the following move and copy methods, 'dst' is assumed to be uninitial
ized raw storage. | |
391 * In the following move methods, 'src' is destroyed leaving behind uniniti
alized raw storage. | |
392 */ | |
393 template <bool E = MEM_COPY> SK_WHEN(E, void) copy(const T* src) { | |
394 sk_careful_memcpy(fMemArray, src, fCount * sizeof(T)); | |
395 } | |
396 template <bool E = MEM_COPY> SK_WHEN(E, void) move(int dst, int src) { | |
397 memcpy(&fItemArray[dst], &fItemArray[src], sizeof(T)); | |
398 } | |
399 template <bool E = MEM_COPY> SK_WHEN(E, void) move(char* dst) { | |
400 sk_careful_memcpy(dst, fMemArray, fCount * sizeof(T)); | |
401 } | |
402 | |
403 template <bool E = MEM_COPY> SK_WHEN(!E, void) copy(const T* src) { | |
404 for (int i = 0; i < fCount; ++i) { | |
405 new (fItemArray + i) T(src[i]); | |
406 } | |
407 } | |
408 template <bool E = MEM_COPY> SK_WHEN(!E, void) move(int dst, int src) { | |
409 new (&fItemArray[dst]) T(std::move(fItemArray[src])); | |
410 fItemArray[src].~T(); | |
411 } | |
412 template <bool E = MEM_COPY> SK_WHEN(!E, void) move(char* dst) { | |
413 for (int i = 0; i < fCount; ++i) { | |
414 new (dst + sizeof(T) * i) T(std::move(fItemArray[i])); | |
415 fItemArray[i].~T(); | |
416 } | |
417 } | |
418 | |
419 static const int gMIN_ALLOC_COUNT = 8; | |
420 | |
421 // Helper function that makes space for n objects, adjusts the count, but do
es not initialize | |
422 // the new objects. | |
423 void* push_back_raw(int n) { | |
424 this->checkRealloc(n); | |
425 void* ptr = fItemArray + fCount; | |
426 fCount += n; | |
427 return ptr; | |
428 } | |
429 | |
430 inline void checkRealloc(int delta) { | |
431 SkASSERT(fCount >= 0); | |
432 SkASSERT(fAllocCount >= 0); | |
433 | |
434 SkASSERT(-delta <= fCount); | |
435 | |
436 int newCount = fCount + delta; | |
437 int newAllocCount = fAllocCount; | |
438 | |
439 if (newCount > fAllocCount || newCount < (fAllocCount / 3)) { | |
440 // whether we're growing or shrinking, we leave at least 50% extra s
pace for future | |
441 // growth (clamped to the reserve count). | |
442 newAllocCount = SkMax32(newCount + ((newCount + 1) >> 1), fReserveCo
unt); | |
443 } | |
444 if (newAllocCount != fAllocCount) { | |
445 | |
446 fAllocCount = newAllocCount; | |
447 char* newMemArray; | |
448 | |
449 if (fAllocCount == fReserveCount && fPreAllocMemArray) { | |
450 newMemArray = (char*) fPreAllocMemArray; | |
451 } else { | |
452 newMemArray = (char*) sk_malloc_throw(fAllocCount*sizeof(T)); | |
453 } | |
454 | |
455 this->move(newMemArray); | |
456 | |
457 if (fMemArray != fPreAllocMemArray) { | |
458 sk_free(fMemArray); | |
459 } | |
460 fMemArray = newMemArray; | |
461 } | |
462 } | |
463 | |
464 int fReserveCount; | |
465 int fCount; | |
466 int fAllocCount; | |
467 void* fPreAllocMemArray; | |
468 union { | |
469 T* fItemArray; | |
470 void* fMemArray; | |
471 }; | |
472 }; | |
473 | |
474 /** | |
475 * Subclass of SkTArray that contains a preallocated memory block for the array. | |
476 */ | |
477 template <int N, typename T, bool MEM_COPY = false> | |
478 class SkSTArray : public SkTArray<T, MEM_COPY> { | |
479 private: | |
480 typedef SkTArray<T, MEM_COPY> INHERITED; | |
481 | |
482 public: | |
483 SkSTArray() : INHERITED(&fStorage) { | |
484 } | |
485 | |
486 SkSTArray(const SkSTArray& array) | |
487 : INHERITED(array, &fStorage) { | |
488 } | |
489 | |
490 explicit SkSTArray(const INHERITED& array) | |
491 : INHERITED(array, &fStorage) { | |
492 } | |
493 | |
494 explicit SkSTArray(int reserveCount) | |
495 : INHERITED(reserveCount) { | |
496 } | |
497 | |
498 SkSTArray(const T* array, int count) | |
499 : INHERITED(array, count, &fStorage) { | |
500 } | |
501 | |
502 SkSTArray& operator= (const SkSTArray& array) { | |
503 return *this = *(const INHERITED*)&array; | |
504 } | |
505 | |
506 SkSTArray& operator= (const INHERITED& array) { | |
507 INHERITED::operator=(array); | |
508 return *this; | |
509 } | |
510 | |
511 private: | |
512 SkAlignedSTStorage<N,T> fStorage; | |
513 }; | |
514 | |
515 #endif | |
OLD | NEW |