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