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*/
23template <typename T, bool MEM_MOVE = false> class SkTArray {
24public:
25    /**
26     * Creates an empty array with no initial storage
27     */
28    SkTArray() { this->init(); }
29
30    /**
31     * Creates an empty array that will preallocate space for reserveCount
32     * elements.
33     */
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     */
39    explicit SkTArray(const SkTArray& that) {
40        this->init(that.fCount);
41        this->copy(that.fItemArray);
42    }
43
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     */
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
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     */
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     */
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     */
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     */
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
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     */
166    int count() const { return fCount; }
167
168    /**
169     * Is the array empty.
170     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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. */
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
316    T* begin() {
317        return fItemArray;
318    }
319    const T* begin() const {
320        return fItemArray;
321    }
322    T* end() {
323        return fItemArray ? fItemArray + fCount : nullptr;
324    }
325    const T* end() const {
326        return fItemArray ? fItemArray + fCount : nullptr;
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     */
347    T& front() { SkASSERT(fCount > 0); return fItemArray[0];}
348
349    const T& front() const { SkASSERT(fCount > 0); return fItemArray[0];}
350
351    /**
352     * equivalent to operator[](count() - 1)
353     */
354    T& back() { SkASSERT(fCount); return fItemArray[fCount - 1];}
355
356    const T& back() const { SkASSERT(fCount > 0); return fItemArray[fCount - 1];}
357
358    /**
359     * equivalent to operator[](count()-1-i)
360     */
361    T& fromBack(int i) {
362        SkASSERT(i >= 0);
363        SkASSERT(i < fCount);
364        return fItemArray[fCount - i - 1];
365    }
366
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
392protected:
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>
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>
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>
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>
431    SkTArray(const T* array, int count, SkAlignedSTStorage<N,T>* storage) {
432        this->initWithPreallocatedStorage(count, storage->get(), N);
433        this->copy(array);
434    }
435
436private:
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 = true;
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
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     */
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
485    template <bool E = MEM_MOVE> SK_WHEN(E, void) move(int dst, int src) {
486        memcpy(&fItemArray[dst], &fItemArray[src], sizeof(T));
487    }
488    template <bool E = MEM_MOVE> SK_WHEN(E, void) move(void* dst) {
489        sk_careful_memcpy(dst, fMemArray, fCount * sizeof(T));
490    }
491
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    }
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.
507    void* push_back_raw(int n) {
508        this->checkRealloc(n);
509        void* ptr = fItemArray + fCount;
510        fCount += n;
511        return ptr;
512    }
513
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
561template<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 */
566template <int N, typename T, bool MEM_MOVE= false>
567class SkSTArray : public SkTArray<T, MEM_MOVE> {
568private:
569    typedef SkTArray<T, MEM_MOVE> INHERITED;
570
571public:
572    SkSTArray() : INHERITED(&fStorage) {
573    }
574
575    SkSTArray(const SkSTArray& array)
576        : INHERITED(array, &fStorage) {
577    }
578
579    SkSTArray(SkSTArray&& array)
580        : INHERITED(std::move(array), &fStorage) {
581    }
582
583    explicit SkSTArray(const INHERITED& array)
584        : INHERITED(array, &fStorage) {
585    }
586
587    explicit SkSTArray(INHERITED&& array)
588        : INHERITED(std::move(array), &fStorage) {
589    }
590
591    explicit SkSTArray(int reserveCount)
592        : INHERITED(reserveCount) {
593    }
594
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
619private:
620    SkAlignedSTStorage<N,T> fStorage;
621};
622
623#endif
624