• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_MOVE is true T will be bit copied when moved.
19     When MEM_MOVE 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_MOVE = false> class SkTArray {
24 public:
25     /**
26      * Creates an empty array with no initial storage
27      */
SkTArray()28     SkTArray() { this->init(); }
29 
30     /**
31      * Creates an empty array that will preallocate space for reserveCount
32      * elements.
33      */
SkTArray(int reserveCount)34     explicit SkTArray(int reserveCount) { this->init(0, reserveCount); }
35 
36     /**
37      * Copies one array to another. The new array will be heap allocated.
38      */
SkTArray(const SkTArray & that)39     explicit SkTArray(const SkTArray& that) {
40         this->init(that.fCount);
41         this->copy(that.fItemArray);
42     }
43 
SkTArray(SkTArray && that)44     explicit SkTArray(SkTArray&& that) {
45         // TODO: If 'that' owns its memory why don't we just steal the pointer?
46         this->init(that.fCount);
47         that.move(fMemArray);
48         that.fCount = 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      */
SkTArray(const T * array,int count)56     SkTArray(const T* array, int count) {
57         this->init(count);
58         this->copy(array);
59     }
60 
61     SkTArray& operator=(const SkTArray& that) {
62         if (this == &that) {
63             return *this;
64         }
65         for (int i = 0; i < fCount; ++i) {
66             fItemArray[i].~T();
67         }
68         fCount = 0;
69         this->checkRealloc(that.count());
70         fCount = that.count();
71         this->copy(that.fItemArray);
72         return *this;
73     }
74     SkTArray& operator=(SkTArray&& that) {
75         if (this == &that) {
76             return *this;
77         }
78         for (int i = 0; i < fCount; ++i) {
79             fItemArray[i].~T();
80         }
81         fCount = 0;
82         this->checkRealloc(that.count());
83         fCount = that.count();
84         that.move(fMemArray);
85         that.fCount = 0;
86         return *this;
87     }
88 
~SkTArray()89     ~SkTArray() {
90         for (int i = 0; i < fCount; ++i) {
91             fItemArray[i].~T();
92         }
93         if (fOwnMemory) {
94             sk_free(fMemArray);
95         }
96     }
97 
98     /**
99      * Resets to count() == 0 and resets any reserve count.
100      */
reset()101     void reset() {
102         this->pop_back_n(fCount);
103         fReserved = false;
104     }
105 
106     /**
107      * Resets to count() = n newly constructed T objects and resets any reserve count.
108      */
reset(int n)109     void reset(int n) {
110         SkASSERT(n >= 0);
111         for (int i = 0; i < fCount; ++i) {
112             fItemArray[i].~T();
113         }
114         // Set fCount to 0 before calling checkRealloc so that no elements are moved.
115         fCount = 0;
116         this->checkRealloc(n);
117         fCount = n;
118         for (int i = 0; i < fCount; ++i) {
119             new (fItemArray + i) T;
120         }
121         fReserved = false;
122     }
123 
124     /**
125      * Resets to a copy of a C array and resets any reserve count.
126      */
reset(const T * array,int count)127     void reset(const T* array, int count) {
128         for (int i = 0; i < fCount; ++i) {
129             fItemArray[i].~T();
130         }
131         fCount = 0;
132         this->checkRealloc(count);
133         fCount = count;
134         this->copy(array);
135         fReserved = false;
136     }
137 
138     /**
139      * Ensures there is enough reserved space for n additional elements. The is guaranteed at least
140      * until the array size grows above n and subsequently shrinks below n, any version of reset()
141      * is called, or reserve() is called again.
142      */
reserve(int n)143     void reserve(int n) {
144         SkASSERT(n >= 0);
145         if (n > 0) {
146             this->checkRealloc(n);
147             fReserved = fOwnMemory;
148         } else {
149             fReserved = false;
150         }
151     }
152 
removeShuffle(int n)153     void removeShuffle(int n) {
154         SkASSERT(n < fCount);
155         int newCount = fCount - 1;
156         fCount = newCount;
157         fItemArray[n].~T();
158         if (n != newCount) {
159             this->move(n, newCount);
160         }
161     }
162 
163     /**
164      * Number of elements in the array.
165      */
count()166     int count() const { return fCount; }
167 
168     /**
169      * Is the array empty.
170      */
empty()171     bool empty() const { return !fCount; }
172 
173     /**
174      * Adds 1 new default-initialized T value and returns it by reference. Note
175      * the reference only remains valid until the next call that adds or removes
176      * elements.
177      */
push_back()178     T& push_back() {
179         void* newT = this->push_back_raw(1);
180         return *new (newT) T;
181     }
182 
183     /**
184      * Version of above that uses a copy constructor to initialize the new item
185      */
push_back(const T & t)186     T& push_back(const T& t) {
187         void* newT = this->push_back_raw(1);
188         return *new (newT) T(t);
189     }
190 
191     /**
192      * Version of above that uses a move constructor to initialize the new item
193      */
push_back(T && t)194     T& push_back(T&& t) {
195         void* newT = this->push_back_raw(1);
196         return *new (newT) T(std::move(t));
197     }
198 
199     /**
200      *  Construct a new T at the back of this array.
201      */
emplace_back(Args &&...args)202     template<class... Args> T& emplace_back(Args&&... args) {
203         void* newT = this->push_back_raw(1);
204         return *new (newT) T(std::forward<Args>(args)...);
205     }
206 
207     /**
208      * Allocates n more default-initialized T values, and returns the address of
209      * the start of that new range. Note: this address is only valid until the
210      * next API call made on the array that might add or remove elements.
211      */
push_back_n(int n)212     T* push_back_n(int n) {
213         SkASSERT(n >= 0);
214         void* newTs = this->push_back_raw(n);
215         for (int i = 0; i < n; ++i) {
216             new (static_cast<char*>(newTs) + i * sizeof(T)) T;
217         }
218         return static_cast<T*>(newTs);
219     }
220 
221     /**
222      * Version of above that uses a copy constructor to initialize all n items
223      * to the same T.
224      */
push_back_n(int n,const T & t)225     T* push_back_n(int n, const T& t) {
226         SkASSERT(n >= 0);
227         void* newTs = this->push_back_raw(n);
228         for (int i = 0; i < n; ++i) {
229             new (static_cast<char*>(newTs) + i * sizeof(T)) T(t);
230         }
231         return static_cast<T*>(newTs);
232     }
233 
234     /**
235      * Version of above that uses a copy constructor to initialize the n items
236      * to separate T values.
237      */
push_back_n(int n,const T t[])238     T* push_back_n(int n, const T t[]) {
239         SkASSERT(n >= 0);
240         this->checkRealloc(n);
241         for (int i = 0; i < n; ++i) {
242             new (fItemArray + fCount + i) T(t[i]);
243         }
244         fCount += n;
245         return fItemArray + fCount - n;
246     }
247 
248     /**
249      * Version of above that uses the move constructor to set n items.
250      */
move_back_n(int n,T * t)251     T* move_back_n(int n, T* t) {
252         SkASSERT(n >= 0);
253         this->checkRealloc(n);
254         for (int i = 0; i < n; ++i) {
255             new (fItemArray + fCount + i) T(std::move(t[i]));
256         }
257         fCount += n;
258         return fItemArray + fCount - n;
259     }
260 
261     /**
262      * Removes the last element. Not safe to call when count() == 0.
263      */
pop_back()264     void pop_back() {
265         SkASSERT(fCount > 0);
266         --fCount;
267         fItemArray[fCount].~T();
268         this->checkRealloc(0);
269     }
270 
271     /**
272      * Removes the last n elements. Not safe to call when count() < n.
273      */
pop_back_n(int n)274     void pop_back_n(int n) {
275         SkASSERT(n >= 0);
276         SkASSERT(fCount >= n);
277         fCount -= n;
278         for (int i = 0; i < n; ++i) {
279             fItemArray[fCount + i].~T();
280         }
281         this->checkRealloc(0);
282     }
283 
284     /**
285      * Pushes or pops from the back to resize. Pushes will be default
286      * initialized.
287      */
resize_back(int newCount)288     void resize_back(int newCount) {
289         SkASSERT(newCount >= 0);
290 
291         if (newCount > fCount) {
292             this->push_back_n(newCount - fCount);
293         } else if (newCount < fCount) {
294             this->pop_back_n(fCount - newCount);
295         }
296     }
297 
298     /** Swaps the contents of this array with that array. Does a pointer swap if possible,
299         otherwise copies the T values. */
swap(SkTArray * that)300     void swap(SkTArray* that) {
301         if (this == that) {
302             return;
303         }
304         if (fOwnMemory && that->fOwnMemory) {
305             SkTSwap(fItemArray, that->fItemArray);
306             SkTSwap(fCount, that->fCount);
307             SkTSwap(fAllocCount, that->fAllocCount);
308         } else {
309             // This could be more optimal...
310             SkTArray copy(std::move(*that));
311             *that = std::move(*this);
312             *this = std::move(copy);
313         }
314     }
315 
begin()316     T* begin() {
317         return fItemArray;
318     }
begin()319     const T* begin() const {
320         return fItemArray;
321     }
end()322     T* end() {
323         return fItemArray ? fItemArray + fCount : NULL;
324     }
end()325     const T* end() const {
326         return fItemArray ? fItemArray + fCount : NULL;
327     }
328 
329    /**
330      * Get the i^th element.
331      */
332     T& operator[] (int i) {
333         SkASSERT(i < fCount);
334         SkASSERT(i >= 0);
335         return fItemArray[i];
336     }
337 
338     const T& operator[] (int i) const {
339         SkASSERT(i < fCount);
340         SkASSERT(i >= 0);
341         return fItemArray[i];
342     }
343 
344     /**
345      * equivalent to operator[](0)
346      */
front()347     T& front() { SkASSERT(fCount > 0); return fItemArray[0];}
348 
front()349     const T& front() const { SkASSERT(fCount > 0); return fItemArray[0];}
350 
351     /**
352      * equivalent to operator[](count() - 1)
353      */
back()354     T& back() { SkASSERT(fCount); return fItemArray[fCount - 1];}
355 
back()356     const T& back() const { SkASSERT(fCount > 0); return fItemArray[fCount - 1];}
357 
358     /**
359      * equivalent to operator[](count()-1-i)
360      */
fromBack(int i)361     T& fromBack(int i) {
362         SkASSERT(i >= 0);
363         SkASSERT(i < fCount);
364         return fItemArray[fCount - i - 1];
365     }
366 
fromBack(int i)367     const T& fromBack(int i) const {
368         SkASSERT(i >= 0);
369         SkASSERT(i < fCount);
370         return fItemArray[fCount - i - 1];
371     }
372 
373     bool operator==(const SkTArray<T, MEM_MOVE>& right) const {
374         int leftCount = this->count();
375         if (leftCount != right.count()) {
376             return false;
377         }
378         for (int index = 0; index < leftCount; ++index) {
379             if (fItemArray[index] != right.fItemArray[index]) {
380                 return false;
381             }
382         }
383         return true;
384     }
385 
386     bool operator!=(const SkTArray<T, MEM_MOVE>& right) const {
387         return !(*this == right);
388     }
389 
390     inline int allocCntForTest() const;
391 
392 protected:
393     /**
394      * Creates an empty array that will use the passed storage block until it
395      * is insufficiently large to hold the entire array.
396      */
397     template <int N>
SkTArray(SkAlignedSTStorage<N,T> * storage)398     SkTArray(SkAlignedSTStorage<N,T>* storage) {
399         this->initWithPreallocatedStorage(0, storage->get(), N);
400     }
401 
402     /**
403      * Copy another array, using preallocated storage if preAllocCount >=
404      * array.count(). Otherwise storage will only be used when array shrinks
405      * to fit.
406      */
407     template <int N>
SkTArray(const SkTArray & array,SkAlignedSTStorage<N,T> * storage)408     SkTArray(const SkTArray& array, SkAlignedSTStorage<N,T>* storage) {
409         this->initWithPreallocatedStorage(array.fCount, storage->get(), N);
410         this->copy(array.fItemArray);
411     }
412 
413     /**
414      * Move another array, using preallocated storage if preAllocCount >=
415      * array.count(). Otherwise storage will only be used when array shrinks
416      * to fit.
417      */
418     template <int N>
SkTArray(SkTArray && array,SkAlignedSTStorage<N,T> * storage)419     SkTArray(SkTArray&& array, SkAlignedSTStorage<N,T>* storage) {
420         this->initWithPreallocatedStorage(array.fCount, storage->get(), N);
421         array.move(fMemArray);
422         array.fCount = 0;
423     }
424 
425     /**
426      * Copy a C array, using preallocated storage if preAllocCount >=
427      * count. Otherwise storage will only be used when array shrinks
428      * to fit.
429      */
430     template <int N>
SkTArray(const T * array,int count,SkAlignedSTStorage<N,T> * storage)431     SkTArray(const T* array, int count, SkAlignedSTStorage<N,T>* storage) {
432         this->initWithPreallocatedStorage(count, storage->get(), N);
433         this->copy(array);
434     }
435 
436 private:
437     void init(int count = 0, int reserveCount = 0) {
438         SkASSERT(count >= 0);
439         SkASSERT(reserveCount >= 0);
440         fCount = count;
441         if (!count && !reserveCount) {
442             fAllocCount = 0;
443             fMemArray = nullptr;
444             fOwnMemory = false;
445             fReserved = false;
446         } else {
447             fAllocCount = SkTMax(count, SkTMax(kMinHeapAllocCount, reserveCount));
448             fMemArray = sk_malloc_throw(fAllocCount, sizeof(T));
449             fOwnMemory = true;
450             fReserved = reserveCount > 0;
451         }
452     }
453 
initWithPreallocatedStorage(int count,void * preallocStorage,int preallocCount)454     void initWithPreallocatedStorage(int count, void* preallocStorage, int preallocCount) {
455         SkASSERT(count >= 0);
456         SkASSERT(preallocCount > 0);
457         SkASSERT(preallocStorage);
458         fCount = count;
459         fMemArray = nullptr;
460         fReserved = false;
461         if (count > preallocCount) {
462             fAllocCount = SkTMax(count, kMinHeapAllocCount);
463             fMemArray = sk_malloc_throw(fAllocCount, sizeof(T));
464             fOwnMemory = true;
465         } else {
466             fAllocCount = preallocCount;
467             fMemArray = preallocStorage;
468             fOwnMemory = false;
469         }
470     }
471 
472     /** In the following move and copy methods, 'dst' is assumed to be uninitialized raw storage.
473      *  In the following move methods, 'src' is destroyed leaving behind uninitialized raw storage.
474      */
copy(const T * src)475     void copy(const T* src) {
476         // Some types may be trivially copyable, in which case we *could* use memcopy; but
477         // MEM_MOVE == true implies that the type is trivially movable, and not necessarily
478         // trivially copyable (think sk_sp<>).  So short of adding another template arg, we
479         // must be conservative and use copy construction.
480         for (int i = 0; i < fCount; ++i) {
481             new (fItemArray + i) T(src[i]);
482         }
483     }
484 
SK_WHEN(E,void)485     template <bool E = MEM_MOVE> SK_WHEN(E, void) move(int dst, int src) {
486         memcpy(&fItemArray[dst], &fItemArray[src], sizeof(T));
487     }
SK_WHEN(E,void)488     template <bool E = MEM_MOVE> SK_WHEN(E, void) move(void* dst) {
489         sk_careful_memcpy(dst, fMemArray, fCount * sizeof(T));
490     }
491 
move(int dst,int src)492     template <bool E = MEM_MOVE> SK_WHEN(!E, void) move(int dst, int src) {
493         new (&fItemArray[dst]) T(std::move(fItemArray[src]));
494         fItemArray[src].~T();
495     }
move(void * dst)496     template <bool E = MEM_MOVE> SK_WHEN(!E, void) move(void* dst) {
497         for (int i = 0; i < fCount; ++i) {
498             new (static_cast<char*>(dst) + sizeof(T) * i) T(std::move(fItemArray[i]));
499             fItemArray[i].~T();
500         }
501     }
502 
503     static constexpr int kMinHeapAllocCount = 8;
504 
505     // Helper function that makes space for n objects, adjusts the count, but does not initialize
506     // the new objects.
push_back_raw(int n)507     void* push_back_raw(int n) {
508         this->checkRealloc(n);
509         void* ptr = fItemArray + fCount;
510         fCount += n;
511         return ptr;
512     }
513 
checkRealloc(int delta)514     void checkRealloc(int delta) {
515         SkASSERT(fCount >= 0);
516         SkASSERT(fAllocCount >= 0);
517         SkASSERT(-delta <= fCount);
518 
519         int newCount = fCount + delta;
520 
521         // We allow fAllocCount to be in the range [newCount, 3*newCount]. We also never shrink
522         // when we're currently using preallocated memory, would allocate less than
523         // kMinHeapAllocCount, or a reserve count was specified that has yet to be exceeded.
524         bool mustGrow = newCount > fAllocCount;
525         bool shouldShrink = fAllocCount > 3 * newCount && fOwnMemory && !fReserved;
526         if (!mustGrow && !shouldShrink) {
527             return;
528         }
529 
530         // Whether we're growing or shrinking, we leave at least 50% extra space for future growth.
531         int newAllocCount = newCount + ((newCount + 1) >> 1);
532         // Align the new allocation count to kMinHeapAllocCount.
533         static_assert(SkIsPow2(kMinHeapAllocCount), "min alloc count not power of two.");
534         newAllocCount = (newAllocCount + (kMinHeapAllocCount - 1)) & ~(kMinHeapAllocCount - 1);
535         // At small sizes the old and new alloc count can both be kMinHeapAllocCount.
536         if (newAllocCount == fAllocCount) {
537             return;
538         }
539         fAllocCount = newAllocCount;
540         void* newMemArray = sk_malloc_throw(fAllocCount, sizeof(T));
541         this->move(newMemArray);
542         if (fOwnMemory) {
543             sk_free(fMemArray);
544 
545         }
546         fMemArray = newMemArray;
547         fOwnMemory = true;
548         fReserved = false;
549     }
550 
551     union {
552         T*       fItemArray;
553         void*    fMemArray;
554     };
555     int fCount;
556     int fAllocCount;
557     bool fOwnMemory : 1;
558     bool fReserved : 1;
559 };
560 
561 template<typename T, bool MEM_MOVE> constexpr int SkTArray<T, MEM_MOVE>::kMinHeapAllocCount;
562 
563 /**
564  * Subclass of SkTArray that contains a preallocated memory block for the array.
565  */
566 template <int N, typename T, bool MEM_MOVE= false>
567 class SkSTArray : public SkTArray<T, MEM_MOVE> {
568 private:
569     typedef SkTArray<T, MEM_MOVE> INHERITED;
570 
571 public:
SkSTArray()572     SkSTArray() : INHERITED(&fStorage) {
573     }
574 
SkSTArray(const SkSTArray & array)575     SkSTArray(const SkSTArray& array)
576         : INHERITED(array, &fStorage) {
577     }
578 
SkSTArray(SkSTArray && array)579     SkSTArray(SkSTArray&& array)
580         : INHERITED(std::move(array), &fStorage) {
581     }
582 
SkSTArray(const INHERITED & array)583     explicit SkSTArray(const INHERITED& array)
584         : INHERITED(array, &fStorage) {
585     }
586 
SkSTArray(INHERITED && array)587     explicit SkSTArray(INHERITED&& array)
588         : INHERITED(std::move(array), &fStorage) {
589     }
590 
SkSTArray(int reserveCount)591     explicit SkSTArray(int reserveCount)
592         : INHERITED(reserveCount) {
593     }
594 
SkSTArray(const T * array,int count)595     SkSTArray(const T* array, int count)
596         : INHERITED(array, count, &fStorage) {
597     }
598 
599     SkSTArray& operator=(const SkSTArray& array) {
600         INHERITED::operator=(array);
601         return *this;
602     }
603 
604     SkSTArray& operator=(SkSTArray&& array) {
605         INHERITED::operator=(std::move(array));
606         return *this;
607     }
608 
609     SkSTArray& operator=(const INHERITED& array) {
610         INHERITED::operator=(array);
611         return *this;
612     }
613 
614     SkSTArray& operator=(INHERITED&& array) {
615         INHERITED::operator=(std::move(array));
616         return *this;
617     }
618 
619 private:
620     SkAlignedSTStorage<N,T> fStorage;
621 };
622 
623 #endif
624