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