1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2011 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #ifndef SkTArray_DEFINED
9*c8dee2aaSAndroid Build Coastguard Worker #define SkTArray_DEFINED
10*c8dee2aaSAndroid Build Coastguard Worker
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkASAN.h" // IWYU pragma: keep
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAlignedStorage.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAssert.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAttributes.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkContainers.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkDebug.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkMalloc.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkMath.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkSpan_impl.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTypeTraits.h" // IWYU pragma: keep
22*c8dee2aaSAndroid Build Coastguard Worker
23*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
24*c8dee2aaSAndroid Build Coastguard Worker #include <climits>
25*c8dee2aaSAndroid Build Coastguard Worker #include <cstddef>
26*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
27*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
28*c8dee2aaSAndroid Build Coastguard Worker #include <initializer_list>
29*c8dee2aaSAndroid Build Coastguard Worker #include <new>
30*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
31*c8dee2aaSAndroid Build Coastguard Worker
32*c8dee2aaSAndroid Build Coastguard Worker namespace skia_private {
33*c8dee2aaSAndroid Build Coastguard Worker /** TArray<T> implements a typical, mostly std::vector-like array.
34*c8dee2aaSAndroid Build Coastguard Worker Each T will be default-initialized on allocation, and ~T will be called on destruction.
35*c8dee2aaSAndroid Build Coastguard Worker
36*c8dee2aaSAndroid Build Coastguard Worker MEM_MOVE controls the behavior when a T needs to be moved (e.g. when the array is resized)
37*c8dee2aaSAndroid Build Coastguard Worker - true: T will be bit-copied via memcpy.
38*c8dee2aaSAndroid Build Coastguard Worker - false: T will be moved via move-constructors.
39*c8dee2aaSAndroid Build Coastguard Worker */
40*c8dee2aaSAndroid Build Coastguard Worker template <typename T, bool MEM_MOVE = sk_is_trivially_relocatable_v<T>> class TArray {
41*c8dee2aaSAndroid Build Coastguard Worker public:
42*c8dee2aaSAndroid Build Coastguard Worker using value_type = T;
43*c8dee2aaSAndroid Build Coastguard Worker
44*c8dee2aaSAndroid Build Coastguard Worker /**
45*c8dee2aaSAndroid Build Coastguard Worker * Creates an empty array with no initial storage
46*c8dee2aaSAndroid Build Coastguard Worker */
TArray()47*c8dee2aaSAndroid Build Coastguard Worker TArray() : fOwnMemory(true), fCapacity{0} {}
48*c8dee2aaSAndroid Build Coastguard Worker
49*c8dee2aaSAndroid Build Coastguard Worker /**
50*c8dee2aaSAndroid Build Coastguard Worker * Creates an empty array that will preallocate space for reserveCount elements.
51*c8dee2aaSAndroid Build Coastguard Worker */
TArray(int reserveCount)52*c8dee2aaSAndroid Build Coastguard Worker explicit TArray(int reserveCount) : TArray() { this->reserve_exact(reserveCount); }
53*c8dee2aaSAndroid Build Coastguard Worker
54*c8dee2aaSAndroid Build Coastguard Worker /**
55*c8dee2aaSAndroid Build Coastguard Worker * Copies one array to another. The new array will be heap allocated.
56*c8dee2aaSAndroid Build Coastguard Worker */
TArray(const TArray & that)57*c8dee2aaSAndroid Build Coastguard Worker TArray(const TArray& that) : TArray(that.fData, that.fSize) {}
58*c8dee2aaSAndroid Build Coastguard Worker
TArray(TArray && that)59*c8dee2aaSAndroid Build Coastguard Worker TArray(TArray&& that) {
60*c8dee2aaSAndroid Build Coastguard Worker if (that.fOwnMemory) {
61*c8dee2aaSAndroid Build Coastguard Worker this->setData(that);
62*c8dee2aaSAndroid Build Coastguard Worker that.setData({});
63*c8dee2aaSAndroid Build Coastguard Worker } else {
64*c8dee2aaSAndroid Build Coastguard Worker this->initData(that.fSize);
65*c8dee2aaSAndroid Build Coastguard Worker that.move(fData);
66*c8dee2aaSAndroid Build Coastguard Worker }
67*c8dee2aaSAndroid Build Coastguard Worker this->changeSize(that.fSize);
68*c8dee2aaSAndroid Build Coastguard Worker that.changeSize(0);
69*c8dee2aaSAndroid Build Coastguard Worker }
70*c8dee2aaSAndroid Build Coastguard Worker
71*c8dee2aaSAndroid Build Coastguard Worker /**
72*c8dee2aaSAndroid Build Coastguard Worker * Creates a TArray by copying contents of a standard C array. The new
73*c8dee2aaSAndroid Build Coastguard Worker * array will be heap allocated. Be careful not to use this constructor
74*c8dee2aaSAndroid Build Coastguard Worker * when you really want the (void*, int) version.
75*c8dee2aaSAndroid Build Coastguard Worker */
TArray(const T * array,int count)76*c8dee2aaSAndroid Build Coastguard Worker TArray(const T* array, int count) {
77*c8dee2aaSAndroid Build Coastguard Worker this->initData(count);
78*c8dee2aaSAndroid Build Coastguard Worker this->copy(array);
79*c8dee2aaSAndroid Build Coastguard Worker }
80*c8dee2aaSAndroid Build Coastguard Worker
81*c8dee2aaSAndroid Build Coastguard Worker /**
82*c8dee2aaSAndroid Build Coastguard Worker * Creates a TArray by copying contents from an SkSpan. The new array will be heap allocated.
83*c8dee2aaSAndroid Build Coastguard Worker */
TArray(SkSpan<const T> data)84*c8dee2aaSAndroid Build Coastguard Worker TArray(SkSpan<const T> data) : TArray(data.begin(), static_cast<int>(data.size())) {}
85*c8dee2aaSAndroid Build Coastguard Worker
86*c8dee2aaSAndroid Build Coastguard Worker /**
87*c8dee2aaSAndroid Build Coastguard Worker * Creates a TArray by copying contents of an initializer list.
88*c8dee2aaSAndroid Build Coastguard Worker */
TArray(std::initializer_list<T> data)89*c8dee2aaSAndroid Build Coastguard Worker TArray(std::initializer_list<T> data) : TArray(data.begin(), data.size()) {}
90*c8dee2aaSAndroid Build Coastguard Worker
91*c8dee2aaSAndroid Build Coastguard Worker TArray& operator=(const TArray& that) {
92*c8dee2aaSAndroid Build Coastguard Worker if (this == &that) {
93*c8dee2aaSAndroid Build Coastguard Worker return *this;
94*c8dee2aaSAndroid Build Coastguard Worker }
95*c8dee2aaSAndroid Build Coastguard Worker this->clear();
96*c8dee2aaSAndroid Build Coastguard Worker this->checkRealloc(that.size(), kExactFit);
97*c8dee2aaSAndroid Build Coastguard Worker this->changeSize(that.fSize);
98*c8dee2aaSAndroid Build Coastguard Worker this->copy(that.fData);
99*c8dee2aaSAndroid Build Coastguard Worker return *this;
100*c8dee2aaSAndroid Build Coastguard Worker }
101*c8dee2aaSAndroid Build Coastguard Worker
102*c8dee2aaSAndroid Build Coastguard Worker TArray& operator=(TArray&& that) {
103*c8dee2aaSAndroid Build Coastguard Worker if (this != &that) {
104*c8dee2aaSAndroid Build Coastguard Worker this->clear();
105*c8dee2aaSAndroid Build Coastguard Worker this->unpoison();
106*c8dee2aaSAndroid Build Coastguard Worker that.unpoison();
107*c8dee2aaSAndroid Build Coastguard Worker if (that.fOwnMemory) {
108*c8dee2aaSAndroid Build Coastguard Worker // The storage is on the heap, so move the data pointer.
109*c8dee2aaSAndroid Build Coastguard Worker if (fOwnMemory) {
110*c8dee2aaSAndroid Build Coastguard Worker sk_free(fData);
111*c8dee2aaSAndroid Build Coastguard Worker }
112*c8dee2aaSAndroid Build Coastguard Worker
113*c8dee2aaSAndroid Build Coastguard Worker fData = std::exchange(that.fData, nullptr);
114*c8dee2aaSAndroid Build Coastguard Worker
115*c8dee2aaSAndroid Build Coastguard Worker // Can't use exchange with bitfields.
116*c8dee2aaSAndroid Build Coastguard Worker fCapacity = that.fCapacity;
117*c8dee2aaSAndroid Build Coastguard Worker that.fCapacity = 0;
118*c8dee2aaSAndroid Build Coastguard Worker
119*c8dee2aaSAndroid Build Coastguard Worker fOwnMemory = true;
120*c8dee2aaSAndroid Build Coastguard Worker
121*c8dee2aaSAndroid Build Coastguard Worker this->changeSize(that.fSize);
122*c8dee2aaSAndroid Build Coastguard Worker } else {
123*c8dee2aaSAndroid Build Coastguard Worker // The data is stored inline in that, so move it element-by-element.
124*c8dee2aaSAndroid Build Coastguard Worker this->checkRealloc(that.size(), kExactFit);
125*c8dee2aaSAndroid Build Coastguard Worker this->changeSize(that.fSize);
126*c8dee2aaSAndroid Build Coastguard Worker that.move(fData);
127*c8dee2aaSAndroid Build Coastguard Worker }
128*c8dee2aaSAndroid Build Coastguard Worker that.changeSize(0);
129*c8dee2aaSAndroid Build Coastguard Worker }
130*c8dee2aaSAndroid Build Coastguard Worker return *this;
131*c8dee2aaSAndroid Build Coastguard Worker }
132*c8dee2aaSAndroid Build Coastguard Worker
~TArray()133*c8dee2aaSAndroid Build Coastguard Worker ~TArray() {
134*c8dee2aaSAndroid Build Coastguard Worker this->destroyAll();
135*c8dee2aaSAndroid Build Coastguard Worker this->unpoison();
136*c8dee2aaSAndroid Build Coastguard Worker if (fOwnMemory) {
137*c8dee2aaSAndroid Build Coastguard Worker sk_free(fData);
138*c8dee2aaSAndroid Build Coastguard Worker }
139*c8dee2aaSAndroid Build Coastguard Worker }
140*c8dee2aaSAndroid Build Coastguard Worker
141*c8dee2aaSAndroid Build Coastguard Worker /**
142*c8dee2aaSAndroid Build Coastguard Worker * Resets to size() = n newly constructed T objects and resets any reserve count.
143*c8dee2aaSAndroid Build Coastguard Worker */
reset(int n)144*c8dee2aaSAndroid Build Coastguard Worker void reset(int n) {
145*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(n >= 0);
146*c8dee2aaSAndroid Build Coastguard Worker this->clear();
147*c8dee2aaSAndroid Build Coastguard Worker this->checkRealloc(n, kExactFit);
148*c8dee2aaSAndroid Build Coastguard Worker this->changeSize(n);
149*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < this->size(); ++i) {
150*c8dee2aaSAndroid Build Coastguard Worker new (fData + i) T;
151*c8dee2aaSAndroid Build Coastguard Worker }
152*c8dee2aaSAndroid Build Coastguard Worker }
153*c8dee2aaSAndroid Build Coastguard Worker
154*c8dee2aaSAndroid Build Coastguard Worker /**
155*c8dee2aaSAndroid Build Coastguard Worker * Resets to a copy of a C array and resets any reserve count.
156*c8dee2aaSAndroid Build Coastguard Worker */
reset(const T * array,int count)157*c8dee2aaSAndroid Build Coastguard Worker void reset(const T* array, int count) {
158*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(count >= 0);
159*c8dee2aaSAndroid Build Coastguard Worker this->clear();
160*c8dee2aaSAndroid Build Coastguard Worker this->checkRealloc(count, kExactFit);
161*c8dee2aaSAndroid Build Coastguard Worker this->changeSize(count);
162*c8dee2aaSAndroid Build Coastguard Worker this->copy(array);
163*c8dee2aaSAndroid Build Coastguard Worker }
164*c8dee2aaSAndroid Build Coastguard Worker
165*c8dee2aaSAndroid Build Coastguard Worker /**
166*c8dee2aaSAndroid Build Coastguard Worker * Ensures there is enough reserved space for at least n elements. This is guaranteed at least
167*c8dee2aaSAndroid Build Coastguard Worker * until the array size grows above n and subsequently shrinks below n, any version of reset()
168*c8dee2aaSAndroid Build Coastguard Worker * is called, or reserve() is called again.
169*c8dee2aaSAndroid Build Coastguard Worker */
reserve(int n)170*c8dee2aaSAndroid Build Coastguard Worker void reserve(int n) {
171*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(n >= 0);
172*c8dee2aaSAndroid Build Coastguard Worker if (n > this->size()) {
173*c8dee2aaSAndroid Build Coastguard Worker this->checkRealloc(n - this->size(), kGrowing);
174*c8dee2aaSAndroid Build Coastguard Worker }
175*c8dee2aaSAndroid Build Coastguard Worker }
176*c8dee2aaSAndroid Build Coastguard Worker
177*c8dee2aaSAndroid Build Coastguard Worker /**
178*c8dee2aaSAndroid Build Coastguard Worker * Ensures there is enough reserved space for exactly n elements. The same capacity guarantees
179*c8dee2aaSAndroid Build Coastguard Worker * as above apply.
180*c8dee2aaSAndroid Build Coastguard Worker */
reserve_exact(int n)181*c8dee2aaSAndroid Build Coastguard Worker void reserve_exact(int n) {
182*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(n >= 0);
183*c8dee2aaSAndroid Build Coastguard Worker if (n > this->size()) {
184*c8dee2aaSAndroid Build Coastguard Worker this->checkRealloc(n - this->size(), kExactFit);
185*c8dee2aaSAndroid Build Coastguard Worker }
186*c8dee2aaSAndroid Build Coastguard Worker }
187*c8dee2aaSAndroid Build Coastguard Worker
removeShuffle(int n)188*c8dee2aaSAndroid Build Coastguard Worker void removeShuffle(int n) {
189*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(n < this->size());
190*c8dee2aaSAndroid Build Coastguard Worker int newCount = fSize - 1;
191*c8dee2aaSAndroid Build Coastguard Worker fData[n].~T();
192*c8dee2aaSAndroid Build Coastguard Worker if (n != newCount) {
193*c8dee2aaSAndroid Build Coastguard Worker this->move(n, newCount);
194*c8dee2aaSAndroid Build Coastguard Worker }
195*c8dee2aaSAndroid Build Coastguard Worker this->changeSize(newCount);
196*c8dee2aaSAndroid Build Coastguard Worker }
197*c8dee2aaSAndroid Build Coastguard Worker
198*c8dee2aaSAndroid Build Coastguard Worker // Is the array empty.
empty()199*c8dee2aaSAndroid Build Coastguard Worker bool empty() const { return fSize == 0; }
200*c8dee2aaSAndroid Build Coastguard Worker
201*c8dee2aaSAndroid Build Coastguard Worker /**
202*c8dee2aaSAndroid Build Coastguard Worker * Adds one new default-initialized T value and returns it by reference. Note that the reference
203*c8dee2aaSAndroid Build Coastguard Worker * only remains valid until the next call that adds or removes elements.
204*c8dee2aaSAndroid Build Coastguard Worker */
push_back()205*c8dee2aaSAndroid Build Coastguard Worker T& push_back() {
206*c8dee2aaSAndroid Build Coastguard Worker void* newT = this->push_back_raw(1);
207*c8dee2aaSAndroid Build Coastguard Worker return *new (newT) T;
208*c8dee2aaSAndroid Build Coastguard Worker }
209*c8dee2aaSAndroid Build Coastguard Worker
210*c8dee2aaSAndroid Build Coastguard Worker /**
211*c8dee2aaSAndroid Build Coastguard Worker * Adds one new T value which is copy-constructed, returning it by reference. As always,
212*c8dee2aaSAndroid Build Coastguard Worker * the reference only remains valid until the next call that adds or removes elements.
213*c8dee2aaSAndroid Build Coastguard Worker */
push_back(const T & t)214*c8dee2aaSAndroid Build Coastguard Worker T& push_back(const T& t) {
215*c8dee2aaSAndroid Build Coastguard Worker this->unpoison();
216*c8dee2aaSAndroid Build Coastguard Worker T* newT;
217*c8dee2aaSAndroid Build Coastguard Worker if (this->capacity() > fSize) SK_LIKELY {
218*c8dee2aaSAndroid Build Coastguard Worker // Copy over the element directly.
219*c8dee2aaSAndroid Build Coastguard Worker newT = new (fData + fSize) T(t);
220*c8dee2aaSAndroid Build Coastguard Worker } else {
221*c8dee2aaSAndroid Build Coastguard Worker newT = this->growAndConstructAtEnd(t);
222*c8dee2aaSAndroid Build Coastguard Worker }
223*c8dee2aaSAndroid Build Coastguard Worker
224*c8dee2aaSAndroid Build Coastguard Worker this->changeSize(fSize + 1);
225*c8dee2aaSAndroid Build Coastguard Worker return *newT;
226*c8dee2aaSAndroid Build Coastguard Worker }
227*c8dee2aaSAndroid Build Coastguard Worker
228*c8dee2aaSAndroid Build Coastguard Worker /**
229*c8dee2aaSAndroid Build Coastguard Worker * Adds one new T value which is copy-constructed, returning it by reference.
230*c8dee2aaSAndroid Build Coastguard Worker */
push_back(T && t)231*c8dee2aaSAndroid Build Coastguard Worker T& push_back(T&& t) {
232*c8dee2aaSAndroid Build Coastguard Worker this->unpoison();
233*c8dee2aaSAndroid Build Coastguard Worker T* newT;
234*c8dee2aaSAndroid Build Coastguard Worker if (this->capacity() > fSize) SK_LIKELY {
235*c8dee2aaSAndroid Build Coastguard Worker // Move over the element directly.
236*c8dee2aaSAndroid Build Coastguard Worker newT = new (fData + fSize) T(std::move(t));
237*c8dee2aaSAndroid Build Coastguard Worker } else {
238*c8dee2aaSAndroid Build Coastguard Worker newT = this->growAndConstructAtEnd(std::move(t));
239*c8dee2aaSAndroid Build Coastguard Worker }
240*c8dee2aaSAndroid Build Coastguard Worker
241*c8dee2aaSAndroid Build Coastguard Worker this->changeSize(fSize + 1);
242*c8dee2aaSAndroid Build Coastguard Worker return *newT;
243*c8dee2aaSAndroid Build Coastguard Worker }
244*c8dee2aaSAndroid Build Coastguard Worker
245*c8dee2aaSAndroid Build Coastguard Worker /**
246*c8dee2aaSAndroid Build Coastguard Worker * Constructs a new T at the back of this array, returning it by reference.
247*c8dee2aaSAndroid Build Coastguard Worker */
emplace_back(Args &&...args)248*c8dee2aaSAndroid Build Coastguard Worker template <typename... Args> T& emplace_back(Args&&... args) {
249*c8dee2aaSAndroid Build Coastguard Worker this->unpoison();
250*c8dee2aaSAndroid Build Coastguard Worker T* newT;
251*c8dee2aaSAndroid Build Coastguard Worker if (this->capacity() > fSize) SK_LIKELY {
252*c8dee2aaSAndroid Build Coastguard Worker // Emplace the new element in directly.
253*c8dee2aaSAndroid Build Coastguard Worker newT = new (fData + fSize) T(std::forward<Args>(args)...);
254*c8dee2aaSAndroid Build Coastguard Worker } else {
255*c8dee2aaSAndroid Build Coastguard Worker newT = this->growAndConstructAtEnd(std::forward<Args>(args)...);
256*c8dee2aaSAndroid Build Coastguard Worker }
257*c8dee2aaSAndroid Build Coastguard Worker
258*c8dee2aaSAndroid Build Coastguard Worker this->changeSize(fSize + 1);
259*c8dee2aaSAndroid Build Coastguard Worker return *newT;
260*c8dee2aaSAndroid Build Coastguard Worker }
261*c8dee2aaSAndroid Build Coastguard Worker
262*c8dee2aaSAndroid Build Coastguard Worker /**
263*c8dee2aaSAndroid Build Coastguard Worker * Allocates n more default-initialized T values, and returns the address of
264*c8dee2aaSAndroid Build Coastguard Worker * the start of that new range. Note: this address is only valid until the
265*c8dee2aaSAndroid Build Coastguard Worker * next API call made on the array that might add or remove elements.
266*c8dee2aaSAndroid Build Coastguard Worker */
push_back_n(int n)267*c8dee2aaSAndroid Build Coastguard Worker T* push_back_n(int n) {
268*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(n >= 0);
269*c8dee2aaSAndroid Build Coastguard Worker T* newTs = TCast(this->push_back_raw(n));
270*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < n; ++i) {
271*c8dee2aaSAndroid Build Coastguard Worker new (&newTs[i]) T;
272*c8dee2aaSAndroid Build Coastguard Worker }
273*c8dee2aaSAndroid Build Coastguard Worker return newTs;
274*c8dee2aaSAndroid Build Coastguard Worker }
275*c8dee2aaSAndroid Build Coastguard Worker
276*c8dee2aaSAndroid Build Coastguard Worker /**
277*c8dee2aaSAndroid Build Coastguard Worker * Version of above that uses a copy constructor to initialize all n items
278*c8dee2aaSAndroid Build Coastguard Worker * to the same T.
279*c8dee2aaSAndroid Build Coastguard Worker */
push_back_n(int n,const T & t)280*c8dee2aaSAndroid Build Coastguard Worker T* push_back_n(int n, const T& t) {
281*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(n >= 0);
282*c8dee2aaSAndroid Build Coastguard Worker T* newTs = TCast(this->push_back_raw(n));
283*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < n; ++i) {
284*c8dee2aaSAndroid Build Coastguard Worker new (&newTs[i]) T(t);
285*c8dee2aaSAndroid Build Coastguard Worker }
286*c8dee2aaSAndroid Build Coastguard Worker return static_cast<T*>(newTs);
287*c8dee2aaSAndroid Build Coastguard Worker }
288*c8dee2aaSAndroid Build Coastguard Worker
289*c8dee2aaSAndroid Build Coastguard Worker /**
290*c8dee2aaSAndroid Build Coastguard Worker * Version of above that uses a copy constructor to initialize the n items
291*c8dee2aaSAndroid Build Coastguard Worker * to separate T values.
292*c8dee2aaSAndroid Build Coastguard Worker */
push_back_n(int n,const T t[])293*c8dee2aaSAndroid Build Coastguard Worker T* push_back_n(int n, const T t[]) {
294*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(n >= 0);
295*c8dee2aaSAndroid Build Coastguard Worker this->checkRealloc(n, kGrowing);
296*c8dee2aaSAndroid Build Coastguard Worker T* end = this->end();
297*c8dee2aaSAndroid Build Coastguard Worker this->changeSize(fSize + n);
298*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < n; ++i) {
299*c8dee2aaSAndroid Build Coastguard Worker new (end + i) T(t[i]);
300*c8dee2aaSAndroid Build Coastguard Worker }
301*c8dee2aaSAndroid Build Coastguard Worker return end;
302*c8dee2aaSAndroid Build Coastguard Worker }
303*c8dee2aaSAndroid Build Coastguard Worker
304*c8dee2aaSAndroid Build Coastguard Worker /**
305*c8dee2aaSAndroid Build Coastguard Worker * Version of above that uses the move constructor to set n items.
306*c8dee2aaSAndroid Build Coastguard Worker */
move_back_n(int n,T * t)307*c8dee2aaSAndroid Build Coastguard Worker T* move_back_n(int n, T* t) {
308*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(n >= 0);
309*c8dee2aaSAndroid Build Coastguard Worker this->checkRealloc(n, kGrowing);
310*c8dee2aaSAndroid Build Coastguard Worker T* end = this->end();
311*c8dee2aaSAndroid Build Coastguard Worker this->changeSize(fSize + n);
312*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < n; ++i) {
313*c8dee2aaSAndroid Build Coastguard Worker new (end + i) T(std::move(t[i]));
314*c8dee2aaSAndroid Build Coastguard Worker }
315*c8dee2aaSAndroid Build Coastguard Worker return end;
316*c8dee2aaSAndroid Build Coastguard Worker }
317*c8dee2aaSAndroid Build Coastguard Worker
318*c8dee2aaSAndroid Build Coastguard Worker /**
319*c8dee2aaSAndroid Build Coastguard Worker * Removes the last element. Not safe to call when size() == 0.
320*c8dee2aaSAndroid Build Coastguard Worker */
pop_back()321*c8dee2aaSAndroid Build Coastguard Worker void pop_back() {
322*c8dee2aaSAndroid Build Coastguard Worker sk_collection_not_empty(this->empty());
323*c8dee2aaSAndroid Build Coastguard Worker fData[fSize - 1].~T();
324*c8dee2aaSAndroid Build Coastguard Worker this->changeSize(fSize - 1);
325*c8dee2aaSAndroid Build Coastguard Worker }
326*c8dee2aaSAndroid Build Coastguard Worker
327*c8dee2aaSAndroid Build Coastguard Worker /**
328*c8dee2aaSAndroid Build Coastguard Worker * Removes the last n elements. Not safe to call when size() < n.
329*c8dee2aaSAndroid Build Coastguard Worker */
pop_back_n(int n)330*c8dee2aaSAndroid Build Coastguard Worker void pop_back_n(int n) {
331*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(n >= 0);
332*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->size() >= n);
333*c8dee2aaSAndroid Build Coastguard Worker int i = fSize;
334*c8dee2aaSAndroid Build Coastguard Worker while (i-- > fSize - n) {
335*c8dee2aaSAndroid Build Coastguard Worker (*this)[i].~T();
336*c8dee2aaSAndroid Build Coastguard Worker }
337*c8dee2aaSAndroid Build Coastguard Worker this->changeSize(fSize - n);
338*c8dee2aaSAndroid Build Coastguard Worker }
339*c8dee2aaSAndroid Build Coastguard Worker
340*c8dee2aaSAndroid Build Coastguard Worker /**
341*c8dee2aaSAndroid Build Coastguard Worker * Pushes or pops from the back to resize. Pushes will be default initialized.
342*c8dee2aaSAndroid Build Coastguard Worker */
resize_back(int newCount)343*c8dee2aaSAndroid Build Coastguard Worker void resize_back(int newCount) {
344*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(newCount >= 0);
345*c8dee2aaSAndroid Build Coastguard Worker if (newCount > this->size()) {
346*c8dee2aaSAndroid Build Coastguard Worker if (this->empty()) {
347*c8dee2aaSAndroid Build Coastguard Worker // When the container is completely empty, grow to exactly the requested size.
348*c8dee2aaSAndroid Build Coastguard Worker this->checkRealloc(newCount, kExactFit);
349*c8dee2aaSAndroid Build Coastguard Worker }
350*c8dee2aaSAndroid Build Coastguard Worker this->push_back_n(newCount - fSize);
351*c8dee2aaSAndroid Build Coastguard Worker } else if (newCount < this->size()) {
352*c8dee2aaSAndroid Build Coastguard Worker this->pop_back_n(fSize - newCount);
353*c8dee2aaSAndroid Build Coastguard Worker }
354*c8dee2aaSAndroid Build Coastguard Worker }
355*c8dee2aaSAndroid Build Coastguard Worker
356*c8dee2aaSAndroid Build Coastguard Worker /** Swaps the contents of this array with that array. Does a pointer swap if possible,
357*c8dee2aaSAndroid Build Coastguard Worker otherwise copies the T values. */
swap(TArray & that)358*c8dee2aaSAndroid Build Coastguard Worker void swap(TArray& that) {
359*c8dee2aaSAndroid Build Coastguard Worker using std::swap;
360*c8dee2aaSAndroid Build Coastguard Worker if (this == &that) {
361*c8dee2aaSAndroid Build Coastguard Worker return;
362*c8dee2aaSAndroid Build Coastguard Worker }
363*c8dee2aaSAndroid Build Coastguard Worker if (fOwnMemory && that.fOwnMemory) {
364*c8dee2aaSAndroid Build Coastguard Worker swap(fData, that.fData);
365*c8dee2aaSAndroid Build Coastguard Worker swap(fSize, that.fSize);
366*c8dee2aaSAndroid Build Coastguard Worker
367*c8dee2aaSAndroid Build Coastguard Worker // Can't use swap because fCapacity is a bit field.
368*c8dee2aaSAndroid Build Coastguard Worker auto allocCount = fCapacity;
369*c8dee2aaSAndroid Build Coastguard Worker fCapacity = that.fCapacity;
370*c8dee2aaSAndroid Build Coastguard Worker that.fCapacity = allocCount;
371*c8dee2aaSAndroid Build Coastguard Worker } else {
372*c8dee2aaSAndroid Build Coastguard Worker // This could be more optimal...
373*c8dee2aaSAndroid Build Coastguard Worker TArray copy(std::move(that));
374*c8dee2aaSAndroid Build Coastguard Worker that = std::move(*this);
375*c8dee2aaSAndroid Build Coastguard Worker *this = std::move(copy);
376*c8dee2aaSAndroid Build Coastguard Worker }
377*c8dee2aaSAndroid Build Coastguard Worker }
378*c8dee2aaSAndroid Build Coastguard Worker
379*c8dee2aaSAndroid Build Coastguard Worker /**
380*c8dee2aaSAndroid Build Coastguard Worker * Moves all elements of `that` to the end of this array, leaving `that` empty.
381*c8dee2aaSAndroid Build Coastguard Worker * This is a no-op if `that` is empty or equal to this array.
382*c8dee2aaSAndroid Build Coastguard Worker */
move_back(TArray & that)383*c8dee2aaSAndroid Build Coastguard Worker void move_back(TArray& that) {
384*c8dee2aaSAndroid Build Coastguard Worker if (that.empty() || &that == this) {
385*c8dee2aaSAndroid Build Coastguard Worker return;
386*c8dee2aaSAndroid Build Coastguard Worker }
387*c8dee2aaSAndroid Build Coastguard Worker void* dst = this->push_back_raw(that.size());
388*c8dee2aaSAndroid Build Coastguard Worker // After move() returns, the contents of `dst` will have either been in-place initialized
389*c8dee2aaSAndroid Build Coastguard Worker // using a the move constructor (per-item from `that`'s elements), or will have been
390*c8dee2aaSAndroid Build Coastguard Worker // mem-copied into when MEM_MOVE is true (now valid objects).
391*c8dee2aaSAndroid Build Coastguard Worker that.move(dst);
392*c8dee2aaSAndroid Build Coastguard Worker // All items in `that` have either been destroyed (when MEM_MOVE is false) or should be
393*c8dee2aaSAndroid Build Coastguard Worker // considered invalid (when MEM_MOVE is true). Reset fSize to 0 directly to skip any further
394*c8dee2aaSAndroid Build Coastguard Worker // per-item destruction.
395*c8dee2aaSAndroid Build Coastguard Worker that.changeSize(0);
396*c8dee2aaSAndroid Build Coastguard Worker }
397*c8dee2aaSAndroid Build Coastguard Worker
begin()398*c8dee2aaSAndroid Build Coastguard Worker T* begin() {
399*c8dee2aaSAndroid Build Coastguard Worker return fData;
400*c8dee2aaSAndroid Build Coastguard Worker }
begin()401*c8dee2aaSAndroid Build Coastguard Worker const T* begin() const {
402*c8dee2aaSAndroid Build Coastguard Worker return fData;
403*c8dee2aaSAndroid Build Coastguard Worker }
404*c8dee2aaSAndroid Build Coastguard Worker
405*c8dee2aaSAndroid Build Coastguard Worker // It's safe to use fItemArray + fSize because if fItemArray is nullptr then adding 0 is
406*c8dee2aaSAndroid Build Coastguard Worker // valid and returns nullptr. See [expr.add] in the C++ standard.
end()407*c8dee2aaSAndroid Build Coastguard Worker T* end() {
408*c8dee2aaSAndroid Build Coastguard Worker if (fData == nullptr) {
409*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fSize == 0);
410*c8dee2aaSAndroid Build Coastguard Worker }
411*c8dee2aaSAndroid Build Coastguard Worker return fData + fSize;
412*c8dee2aaSAndroid Build Coastguard Worker }
end()413*c8dee2aaSAndroid Build Coastguard Worker const T* end() const {
414*c8dee2aaSAndroid Build Coastguard Worker if (fData == nullptr) {
415*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fSize == 0);
416*c8dee2aaSAndroid Build Coastguard Worker }
417*c8dee2aaSAndroid Build Coastguard Worker return fData + fSize;
418*c8dee2aaSAndroid Build Coastguard Worker }
data()419*c8dee2aaSAndroid Build Coastguard Worker T* data() { return fData; }
data()420*c8dee2aaSAndroid Build Coastguard Worker const T* data() const { return fData; }
size()421*c8dee2aaSAndroid Build Coastguard Worker int size() const { return fSize; }
size_bytes()422*c8dee2aaSAndroid Build Coastguard Worker size_t size_bytes() const { return Bytes(fSize); }
resize(size_t count)423*c8dee2aaSAndroid Build Coastguard Worker void resize(size_t count) { this->resize_back((int)count); }
424*c8dee2aaSAndroid Build Coastguard Worker
clear()425*c8dee2aaSAndroid Build Coastguard Worker void clear() {
426*c8dee2aaSAndroid Build Coastguard Worker this->destroyAll();
427*c8dee2aaSAndroid Build Coastguard Worker this->changeSize(0);
428*c8dee2aaSAndroid Build Coastguard Worker }
429*c8dee2aaSAndroid Build Coastguard Worker
shrink_to_fit()430*c8dee2aaSAndroid Build Coastguard Worker void shrink_to_fit() {
431*c8dee2aaSAndroid Build Coastguard Worker if (!fOwnMemory || fSize == fCapacity) {
432*c8dee2aaSAndroid Build Coastguard Worker return;
433*c8dee2aaSAndroid Build Coastguard Worker }
434*c8dee2aaSAndroid Build Coastguard Worker this->unpoison();
435*c8dee2aaSAndroid Build Coastguard Worker if (fSize == 0) {
436*c8dee2aaSAndroid Build Coastguard Worker sk_free(fData);
437*c8dee2aaSAndroid Build Coastguard Worker fData = nullptr;
438*c8dee2aaSAndroid Build Coastguard Worker fCapacity = 0;
439*c8dee2aaSAndroid Build Coastguard Worker } else {
440*c8dee2aaSAndroid Build Coastguard Worker SkSpan<std::byte> allocation = Allocate(fSize);
441*c8dee2aaSAndroid Build Coastguard Worker this->move(TCast(allocation.data()));
442*c8dee2aaSAndroid Build Coastguard Worker if (fOwnMemory) {
443*c8dee2aaSAndroid Build Coastguard Worker sk_free(fData);
444*c8dee2aaSAndroid Build Coastguard Worker }
445*c8dee2aaSAndroid Build Coastguard Worker // Poison is applied in `setDataFromBytes`.
446*c8dee2aaSAndroid Build Coastguard Worker this->setDataFromBytes(allocation);
447*c8dee2aaSAndroid Build Coastguard Worker }
448*c8dee2aaSAndroid Build Coastguard Worker }
449*c8dee2aaSAndroid Build Coastguard Worker
450*c8dee2aaSAndroid Build Coastguard Worker /**
451*c8dee2aaSAndroid Build Coastguard Worker * Get the i^th element.
452*c8dee2aaSAndroid Build Coastguard Worker */
453*c8dee2aaSAndroid Build Coastguard Worker T& operator[] (int i) {
454*c8dee2aaSAndroid Build Coastguard Worker return fData[sk_collection_check_bounds(i, this->size())];
455*c8dee2aaSAndroid Build Coastguard Worker }
456*c8dee2aaSAndroid Build Coastguard Worker
457*c8dee2aaSAndroid Build Coastguard Worker const T& operator[] (int i) const {
458*c8dee2aaSAndroid Build Coastguard Worker return fData[sk_collection_check_bounds(i, this->size())];
459*c8dee2aaSAndroid Build Coastguard Worker }
460*c8dee2aaSAndroid Build Coastguard Worker
at(int i)461*c8dee2aaSAndroid Build Coastguard Worker T& at(int i) { return (*this)[i]; }
at(int i)462*c8dee2aaSAndroid Build Coastguard Worker const T& at(int i) const { return (*this)[i]; }
463*c8dee2aaSAndroid Build Coastguard Worker
464*c8dee2aaSAndroid Build Coastguard Worker /**
465*c8dee2aaSAndroid Build Coastguard Worker * equivalent to operator[](0)
466*c8dee2aaSAndroid Build Coastguard Worker */
front()467*c8dee2aaSAndroid Build Coastguard Worker T& front() {
468*c8dee2aaSAndroid Build Coastguard Worker sk_collection_not_empty(this->empty());
469*c8dee2aaSAndroid Build Coastguard Worker return fData[0];
470*c8dee2aaSAndroid Build Coastguard Worker }
471*c8dee2aaSAndroid Build Coastguard Worker
front()472*c8dee2aaSAndroid Build Coastguard Worker const T& front() const {
473*c8dee2aaSAndroid Build Coastguard Worker sk_collection_not_empty(this->empty());
474*c8dee2aaSAndroid Build Coastguard Worker return fData[0];
475*c8dee2aaSAndroid Build Coastguard Worker }
476*c8dee2aaSAndroid Build Coastguard Worker
477*c8dee2aaSAndroid Build Coastguard Worker /**
478*c8dee2aaSAndroid Build Coastguard Worker * equivalent to operator[](size() - 1)
479*c8dee2aaSAndroid Build Coastguard Worker */
back()480*c8dee2aaSAndroid Build Coastguard Worker T& back() {
481*c8dee2aaSAndroid Build Coastguard Worker sk_collection_not_empty(this->empty());
482*c8dee2aaSAndroid Build Coastguard Worker return fData[fSize - 1];
483*c8dee2aaSAndroid Build Coastguard Worker }
484*c8dee2aaSAndroid Build Coastguard Worker
back()485*c8dee2aaSAndroid Build Coastguard Worker const T& back() const {
486*c8dee2aaSAndroid Build Coastguard Worker sk_collection_not_empty(this->empty());
487*c8dee2aaSAndroid Build Coastguard Worker return fData[fSize - 1];
488*c8dee2aaSAndroid Build Coastguard Worker }
489*c8dee2aaSAndroid Build Coastguard Worker
490*c8dee2aaSAndroid Build Coastguard Worker /**
491*c8dee2aaSAndroid Build Coastguard Worker * equivalent to operator[](size()-1-i)
492*c8dee2aaSAndroid Build Coastguard Worker */
fromBack(int i)493*c8dee2aaSAndroid Build Coastguard Worker T& fromBack(int i) {
494*c8dee2aaSAndroid Build Coastguard Worker return (*this)[fSize - i - 1];
495*c8dee2aaSAndroid Build Coastguard Worker }
496*c8dee2aaSAndroid Build Coastguard Worker
fromBack(int i)497*c8dee2aaSAndroid Build Coastguard Worker const T& fromBack(int i) const {
498*c8dee2aaSAndroid Build Coastguard Worker return (*this)[fSize - i - 1];
499*c8dee2aaSAndroid Build Coastguard Worker }
500*c8dee2aaSAndroid Build Coastguard Worker
501*c8dee2aaSAndroid Build Coastguard Worker bool operator==(const TArray<T, MEM_MOVE>& right) const {
502*c8dee2aaSAndroid Build Coastguard Worker int leftCount = this->size();
503*c8dee2aaSAndroid Build Coastguard Worker if (leftCount != right.size()) {
504*c8dee2aaSAndroid Build Coastguard Worker return false;
505*c8dee2aaSAndroid Build Coastguard Worker }
506*c8dee2aaSAndroid Build Coastguard Worker for (int index = 0; index < leftCount; ++index) {
507*c8dee2aaSAndroid Build Coastguard Worker if (fData[index] != right.fData[index]) {
508*c8dee2aaSAndroid Build Coastguard Worker return false;
509*c8dee2aaSAndroid Build Coastguard Worker }
510*c8dee2aaSAndroid Build Coastguard Worker }
511*c8dee2aaSAndroid Build Coastguard Worker return true;
512*c8dee2aaSAndroid Build Coastguard Worker }
513*c8dee2aaSAndroid Build Coastguard Worker
514*c8dee2aaSAndroid Build Coastguard Worker bool operator!=(const TArray<T, MEM_MOVE>& right) const {
515*c8dee2aaSAndroid Build Coastguard Worker return !(*this == right);
516*c8dee2aaSAndroid Build Coastguard Worker }
517*c8dee2aaSAndroid Build Coastguard Worker
capacity()518*c8dee2aaSAndroid Build Coastguard Worker int capacity() const {
519*c8dee2aaSAndroid Build Coastguard Worker return fCapacity;
520*c8dee2aaSAndroid Build Coastguard Worker }
521*c8dee2aaSAndroid Build Coastguard Worker
522*c8dee2aaSAndroid Build Coastguard Worker protected:
523*c8dee2aaSAndroid Build Coastguard Worker // Creates an empty array that will use the passed storage block until it is insufficiently
524*c8dee2aaSAndroid Build Coastguard Worker // large to hold the entire array.
525*c8dee2aaSAndroid Build Coastguard Worker template <int InitialCapacity>
526*c8dee2aaSAndroid Build Coastguard Worker TArray(SkAlignedSTStorage<InitialCapacity, T>* storage, int size = 0) {
527*c8dee2aaSAndroid Build Coastguard Worker static_assert(InitialCapacity >= 0);
528*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(size >= 0);
529*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(storage->get() != nullptr);
530*c8dee2aaSAndroid Build Coastguard Worker if (size > InitialCapacity) {
531*c8dee2aaSAndroid Build Coastguard Worker this->initData(size);
532*c8dee2aaSAndroid Build Coastguard Worker } else {
533*c8dee2aaSAndroid Build Coastguard Worker this->setDataFromBytes(*storage);
534*c8dee2aaSAndroid Build Coastguard Worker this->changeSize(size);
535*c8dee2aaSAndroid Build Coastguard Worker
536*c8dee2aaSAndroid Build Coastguard Worker // setDataFromBytes always sets fOwnMemory to true, but we are actually using static
537*c8dee2aaSAndroid Build Coastguard Worker // storage here, which shouldn't ever be freed.
538*c8dee2aaSAndroid Build Coastguard Worker fOwnMemory = false;
539*c8dee2aaSAndroid Build Coastguard Worker }
540*c8dee2aaSAndroid Build Coastguard Worker }
541*c8dee2aaSAndroid Build Coastguard Worker
542*c8dee2aaSAndroid Build Coastguard Worker // Copy a C array, using pre-allocated storage if preAllocCount >= count. Otherwise, storage
543*c8dee2aaSAndroid Build Coastguard Worker // will only be used when array shrinks to fit.
544*c8dee2aaSAndroid Build Coastguard Worker template <int InitialCapacity>
TArray(const T * array,int size,SkAlignedSTStorage<InitialCapacity,T> * storage)545*c8dee2aaSAndroid Build Coastguard Worker TArray(const T* array, int size, SkAlignedSTStorage<InitialCapacity, T>* storage)
546*c8dee2aaSAndroid Build Coastguard Worker : TArray{storage, size} {
547*c8dee2aaSAndroid Build Coastguard Worker this->copy(array);
548*c8dee2aaSAndroid Build Coastguard Worker }
549*c8dee2aaSAndroid Build Coastguard Worker template <int InitialCapacity>
TArray(SkSpan<const T> data,SkAlignedSTStorage<InitialCapacity,T> * storage)550*c8dee2aaSAndroid Build Coastguard Worker TArray(SkSpan<const T> data, SkAlignedSTStorage<InitialCapacity, T>* storage)
551*c8dee2aaSAndroid Build Coastguard Worker : TArray{storage, static_cast<int>(data.size())} {
552*c8dee2aaSAndroid Build Coastguard Worker this->copy(data.begin());
553*c8dee2aaSAndroid Build Coastguard Worker }
554*c8dee2aaSAndroid Build Coastguard Worker
555*c8dee2aaSAndroid Build Coastguard Worker private:
556*c8dee2aaSAndroid Build Coastguard Worker // Growth factors for checkRealloc.
557*c8dee2aaSAndroid Build Coastguard Worker static constexpr double kExactFit = 1.0;
558*c8dee2aaSAndroid Build Coastguard Worker static constexpr double kGrowing = 1.5;
559*c8dee2aaSAndroid Build Coastguard Worker
560*c8dee2aaSAndroid Build Coastguard Worker static constexpr int kMinHeapAllocCount = 8;
561*c8dee2aaSAndroid Build Coastguard Worker static_assert(SkIsPow2(kMinHeapAllocCount), "min alloc count not power of two.");
562*c8dee2aaSAndroid Build Coastguard Worker
563*c8dee2aaSAndroid Build Coastguard Worker // Note for 32-bit machines kMaxCapacity will be <= SIZE_MAX. For 64-bit machines it will
564*c8dee2aaSAndroid Build Coastguard Worker // just be INT_MAX if the sizeof(T) < 2^32.
565*c8dee2aaSAndroid Build Coastguard Worker static constexpr int kMaxCapacity = SkToInt(std::min(SIZE_MAX / sizeof(T), (size_t)INT_MAX));
566*c8dee2aaSAndroid Build Coastguard Worker
setDataFromBytes(SkSpan<std::byte> allocation)567*c8dee2aaSAndroid Build Coastguard Worker void setDataFromBytes(SkSpan<std::byte> allocation) {
568*c8dee2aaSAndroid Build Coastguard Worker T* data = TCast(allocation.data());
569*c8dee2aaSAndroid Build Coastguard Worker // We have gotten extra bytes back from the allocation limit, pin to kMaxCapacity. It
570*c8dee2aaSAndroid Build Coastguard Worker // would seem like the SkContainerAllocator should handle the divide, but it would have
571*c8dee2aaSAndroid Build Coastguard Worker // to a full divide instruction. If done here the size is known at compile, and usually
572*c8dee2aaSAndroid Build Coastguard Worker // can be implemented by a right shift. The full divide takes ~50X longer than the shift.
573*c8dee2aaSAndroid Build Coastguard Worker size_t size = std::min(allocation.size() / sizeof(T), SkToSizeT(kMaxCapacity));
574*c8dee2aaSAndroid Build Coastguard Worker this->setData(SkSpan<T>(data, size));
575*c8dee2aaSAndroid Build Coastguard Worker }
576*c8dee2aaSAndroid Build Coastguard Worker
setData(SkSpan<T> array)577*c8dee2aaSAndroid Build Coastguard Worker void setData(SkSpan<T> array) {
578*c8dee2aaSAndroid Build Coastguard Worker this->unpoison();
579*c8dee2aaSAndroid Build Coastguard Worker
580*c8dee2aaSAndroid Build Coastguard Worker fData = array.data();
581*c8dee2aaSAndroid Build Coastguard Worker fCapacity = SkToU32(array.size());
582*c8dee2aaSAndroid Build Coastguard Worker fOwnMemory = true;
583*c8dee2aaSAndroid Build Coastguard Worker
584*c8dee2aaSAndroid Build Coastguard Worker this->poison();
585*c8dee2aaSAndroid Build Coastguard Worker }
586*c8dee2aaSAndroid Build Coastguard Worker
unpoison()587*c8dee2aaSAndroid Build Coastguard Worker void unpoison() {
588*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_SANITIZE_ADDRESS
589*c8dee2aaSAndroid Build Coastguard Worker if (fData && fPoisoned) {
590*c8dee2aaSAndroid Build Coastguard Worker // SkDebugf("UNPOISONING %p : 0 -> %zu\n", fData, Bytes(fCapacity));
591*c8dee2aaSAndroid Build Coastguard Worker sk_asan_unpoison_memory_region(this->begin(), Bytes(fCapacity));
592*c8dee2aaSAndroid Build Coastguard Worker fPoisoned = false;
593*c8dee2aaSAndroid Build Coastguard Worker }
594*c8dee2aaSAndroid Build Coastguard Worker #endif
595*c8dee2aaSAndroid Build Coastguard Worker }
596*c8dee2aaSAndroid Build Coastguard Worker
poison()597*c8dee2aaSAndroid Build Coastguard Worker void poison() {
598*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_SANITIZE_ADDRESS
599*c8dee2aaSAndroid Build Coastguard Worker if (fData && fCapacity > fSize) {
600*c8dee2aaSAndroid Build Coastguard Worker // SkDebugf(" POISONING %p : %zu -> %zu\n", fData, Bytes(fSize), Bytes(fCapacity));
601*c8dee2aaSAndroid Build Coastguard Worker sk_asan_poison_memory_region(this->end(), Bytes(fCapacity - fSize));
602*c8dee2aaSAndroid Build Coastguard Worker fPoisoned = true;
603*c8dee2aaSAndroid Build Coastguard Worker }
604*c8dee2aaSAndroid Build Coastguard Worker #endif
605*c8dee2aaSAndroid Build Coastguard Worker }
606*c8dee2aaSAndroid Build Coastguard Worker
changeSize(int n)607*c8dee2aaSAndroid Build Coastguard Worker void changeSize(int n) {
608*c8dee2aaSAndroid Build Coastguard Worker this->unpoison();
609*c8dee2aaSAndroid Build Coastguard Worker fSize = n;
610*c8dee2aaSAndroid Build Coastguard Worker this->poison();
611*c8dee2aaSAndroid Build Coastguard Worker }
612*c8dee2aaSAndroid Build Coastguard Worker
613*c8dee2aaSAndroid Build Coastguard Worker // We disable Control-Flow Integrity sanitization (go/cfi) when casting item-array buffers.
614*c8dee2aaSAndroid Build Coastguard Worker // CFI flags this code as dangerous because we are casting `buffer` to a T* while the buffer's
615*c8dee2aaSAndroid Build Coastguard Worker // contents might still be uninitialized memory. When T has a vtable, this is especially risky
616*c8dee2aaSAndroid Build Coastguard Worker // because we could hypothetically access a virtual method on fItemArray and jump to an
617*c8dee2aaSAndroid Build Coastguard Worker // unpredictable location in memory. Of course, TArray won't actually use fItemArray in this
618*c8dee2aaSAndroid Build Coastguard Worker // way, and we don't want to construct a T before the user requests one. There's no real risk
619*c8dee2aaSAndroid Build Coastguard Worker // here, so disable CFI when doing these casts.
620*c8dee2aaSAndroid Build Coastguard Worker SK_NO_SANITIZE("cfi")
TCast(void * buffer)621*c8dee2aaSAndroid Build Coastguard Worker static T* TCast(void* buffer) {
622*c8dee2aaSAndroid Build Coastguard Worker return (T*)buffer;
623*c8dee2aaSAndroid Build Coastguard Worker }
624*c8dee2aaSAndroid Build Coastguard Worker
Bytes(int n)625*c8dee2aaSAndroid Build Coastguard Worker static size_t Bytes(int n) {
626*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(n <= kMaxCapacity);
627*c8dee2aaSAndroid Build Coastguard Worker return SkToSizeT(n) * sizeof(T);
628*c8dee2aaSAndroid Build Coastguard Worker }
629*c8dee2aaSAndroid Build Coastguard Worker
630*c8dee2aaSAndroid Build Coastguard Worker static SkSpan<std::byte> Allocate(int capacity, double growthFactor = 1.0) {
631*c8dee2aaSAndroid Build Coastguard Worker return SkContainerAllocator{sizeof(T), kMaxCapacity}.allocate(capacity, growthFactor);
632*c8dee2aaSAndroid Build Coastguard Worker }
633*c8dee2aaSAndroid Build Coastguard Worker
initData(int count)634*c8dee2aaSAndroid Build Coastguard Worker void initData(int count) {
635*c8dee2aaSAndroid Build Coastguard Worker this->setDataFromBytes(Allocate(count));
636*c8dee2aaSAndroid Build Coastguard Worker this->changeSize(count);
637*c8dee2aaSAndroid Build Coastguard Worker }
638*c8dee2aaSAndroid Build Coastguard Worker
destroyAll()639*c8dee2aaSAndroid Build Coastguard Worker void destroyAll() {
640*c8dee2aaSAndroid Build Coastguard Worker if (!this->empty()) {
641*c8dee2aaSAndroid Build Coastguard Worker T* cursor = this->begin();
642*c8dee2aaSAndroid Build Coastguard Worker T* const end = this->end();
643*c8dee2aaSAndroid Build Coastguard Worker do {
644*c8dee2aaSAndroid Build Coastguard Worker cursor->~T();
645*c8dee2aaSAndroid Build Coastguard Worker cursor++;
646*c8dee2aaSAndroid Build Coastguard Worker } while (cursor < end);
647*c8dee2aaSAndroid Build Coastguard Worker }
648*c8dee2aaSAndroid Build Coastguard Worker }
649*c8dee2aaSAndroid Build Coastguard Worker
650*c8dee2aaSAndroid Build Coastguard Worker /** In the following move and copy methods, 'dst' is assumed to be uninitialized raw storage.
651*c8dee2aaSAndroid Build Coastguard Worker * In the following move methods, 'src' is destroyed leaving behind uninitialized raw storage.
652*c8dee2aaSAndroid Build Coastguard Worker */
copy(const T * src)653*c8dee2aaSAndroid Build Coastguard Worker void copy(const T* src) {
654*c8dee2aaSAndroid Build Coastguard Worker if constexpr (std::is_trivially_copyable_v<T>) {
655*c8dee2aaSAndroid Build Coastguard Worker if (!this->empty() && src != nullptr) {
656*c8dee2aaSAndroid Build Coastguard Worker sk_careful_memcpy(fData, src, this->size_bytes());
657*c8dee2aaSAndroid Build Coastguard Worker }
658*c8dee2aaSAndroid Build Coastguard Worker } else {
659*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < this->size(); ++i) {
660*c8dee2aaSAndroid Build Coastguard Worker new (fData + i) T(src[i]);
661*c8dee2aaSAndroid Build Coastguard Worker }
662*c8dee2aaSAndroid Build Coastguard Worker }
663*c8dee2aaSAndroid Build Coastguard Worker }
664*c8dee2aaSAndroid Build Coastguard Worker
move(int dst,int src)665*c8dee2aaSAndroid Build Coastguard Worker void move(int dst, int src) {
666*c8dee2aaSAndroid Build Coastguard Worker if constexpr (MEM_MOVE) {
667*c8dee2aaSAndroid Build Coastguard Worker memcpy(static_cast<void*>(&fData[dst]),
668*c8dee2aaSAndroid Build Coastguard Worker static_cast<const void*>(&fData[src]),
669*c8dee2aaSAndroid Build Coastguard Worker sizeof(T));
670*c8dee2aaSAndroid Build Coastguard Worker } else {
671*c8dee2aaSAndroid Build Coastguard Worker new (&fData[dst]) T(std::move(fData[src]));
672*c8dee2aaSAndroid Build Coastguard Worker fData[src].~T();
673*c8dee2aaSAndroid Build Coastguard Worker }
674*c8dee2aaSAndroid Build Coastguard Worker }
675*c8dee2aaSAndroid Build Coastguard Worker
move(void * dst)676*c8dee2aaSAndroid Build Coastguard Worker void move(void* dst) {
677*c8dee2aaSAndroid Build Coastguard Worker if constexpr (MEM_MOVE) {
678*c8dee2aaSAndroid Build Coastguard Worker sk_careful_memcpy(dst, fData, Bytes(fSize));
679*c8dee2aaSAndroid Build Coastguard Worker } else {
680*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < this->size(); ++i) {
681*c8dee2aaSAndroid Build Coastguard Worker new (static_cast<char*>(dst) + Bytes(i)) T(std::move(fData[i]));
682*c8dee2aaSAndroid Build Coastguard Worker fData[i].~T();
683*c8dee2aaSAndroid Build Coastguard Worker }
684*c8dee2aaSAndroid Build Coastguard Worker }
685*c8dee2aaSAndroid Build Coastguard Worker }
686*c8dee2aaSAndroid Build Coastguard Worker
687*c8dee2aaSAndroid Build Coastguard Worker // Helper function that makes space for n objects, adjusts the count, but does not initialize
688*c8dee2aaSAndroid Build Coastguard Worker // the new objects.
push_back_raw(int n)689*c8dee2aaSAndroid Build Coastguard Worker void* push_back_raw(int n) {
690*c8dee2aaSAndroid Build Coastguard Worker this->checkRealloc(n, kGrowing);
691*c8dee2aaSAndroid Build Coastguard Worker void* ptr = fData + fSize;
692*c8dee2aaSAndroid Build Coastguard Worker this->changeSize(fSize + n);
693*c8dee2aaSAndroid Build Coastguard Worker return ptr;
694*c8dee2aaSAndroid Build Coastguard Worker }
695*c8dee2aaSAndroid Build Coastguard Worker
696*c8dee2aaSAndroid Build Coastguard Worker template <typename... Args>
growAndConstructAtEnd(Args &&...args)697*c8dee2aaSAndroid Build Coastguard Worker SK_ALWAYS_INLINE T* growAndConstructAtEnd(Args&&... args) {
698*c8dee2aaSAndroid Build Coastguard Worker SkSpan<std::byte> buffer = this->preallocateNewData(/*delta=*/1, kGrowing);
699*c8dee2aaSAndroid Build Coastguard Worker T* newT = new (TCast(buffer.data()) + fSize) T(std::forward<Args>(args)...);
700*c8dee2aaSAndroid Build Coastguard Worker this->installDataAndUpdateCapacity(buffer);
701*c8dee2aaSAndroid Build Coastguard Worker
702*c8dee2aaSAndroid Build Coastguard Worker return newT;
703*c8dee2aaSAndroid Build Coastguard Worker }
704*c8dee2aaSAndroid Build Coastguard Worker
checkRealloc(int delta,double growthFactor)705*c8dee2aaSAndroid Build Coastguard Worker void checkRealloc(int delta, double growthFactor) {
706*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(delta >= 0);
707*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fSize >= 0);
708*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fCapacity >= 0);
709*c8dee2aaSAndroid Build Coastguard Worker
710*c8dee2aaSAndroid Build Coastguard Worker // Check if there are enough remaining allocated elements to satisfy the request.
711*c8dee2aaSAndroid Build Coastguard Worker if (this->capacity() - fSize < delta) {
712*c8dee2aaSAndroid Build Coastguard Worker // Looks like we need to reallocate.
713*c8dee2aaSAndroid Build Coastguard Worker this->installDataAndUpdateCapacity(this->preallocateNewData(delta, growthFactor));
714*c8dee2aaSAndroid Build Coastguard Worker }
715*c8dee2aaSAndroid Build Coastguard Worker }
716*c8dee2aaSAndroid Build Coastguard Worker
preallocateNewData(int delta,double growthFactor)717*c8dee2aaSAndroid Build Coastguard Worker SkSpan<std::byte> preallocateNewData(int delta, double growthFactor) {
718*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(delta >= 0);
719*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fSize >= 0);
720*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fCapacity >= 0);
721*c8dee2aaSAndroid Build Coastguard Worker
722*c8dee2aaSAndroid Build Coastguard Worker // Don't overflow fSize or size_t later in the memory allocation. Overflowing memory
723*c8dee2aaSAndroid Build Coastguard Worker // allocation really only applies to fSizes on 32-bit machines; on 64-bit machines this
724*c8dee2aaSAndroid Build Coastguard Worker // will probably never produce a check. Since kMaxCapacity is bounded above by INT_MAX,
725*c8dee2aaSAndroid Build Coastguard Worker // this also checks the bounds of fSize.
726*c8dee2aaSAndroid Build Coastguard Worker if (delta > kMaxCapacity - fSize) {
727*c8dee2aaSAndroid Build Coastguard Worker sk_report_container_overflow_and_die();
728*c8dee2aaSAndroid Build Coastguard Worker }
729*c8dee2aaSAndroid Build Coastguard Worker const int newCount = fSize + delta;
730*c8dee2aaSAndroid Build Coastguard Worker
731*c8dee2aaSAndroid Build Coastguard Worker return Allocate(newCount, growthFactor);
732*c8dee2aaSAndroid Build Coastguard Worker }
733*c8dee2aaSAndroid Build Coastguard Worker
installDataAndUpdateCapacity(SkSpan<std::byte> allocation)734*c8dee2aaSAndroid Build Coastguard Worker void installDataAndUpdateCapacity(SkSpan<std::byte> allocation) {
735*c8dee2aaSAndroid Build Coastguard Worker this->move(TCast(allocation.data()));
736*c8dee2aaSAndroid Build Coastguard Worker if (fOwnMemory) {
737*c8dee2aaSAndroid Build Coastguard Worker sk_free(fData);
738*c8dee2aaSAndroid Build Coastguard Worker }
739*c8dee2aaSAndroid Build Coastguard Worker this->setDataFromBytes(allocation);
740*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fData != nullptr);
741*c8dee2aaSAndroid Build Coastguard Worker }
742*c8dee2aaSAndroid Build Coastguard Worker
743*c8dee2aaSAndroid Build Coastguard Worker T* fData{nullptr};
744*c8dee2aaSAndroid Build Coastguard Worker int fSize{0};
745*c8dee2aaSAndroid Build Coastguard Worker uint32_t fOwnMemory : 1;
746*c8dee2aaSAndroid Build Coastguard Worker uint32_t fCapacity : 31;
747*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_SANITIZE_ADDRESS
748*c8dee2aaSAndroid Build Coastguard Worker bool fPoisoned = false;
749*c8dee2aaSAndroid Build Coastguard Worker #endif
750*c8dee2aaSAndroid Build Coastguard Worker };
751*c8dee2aaSAndroid Build Coastguard Worker
swap(TArray<T,M> & a,TArray<T,M> & b)752*c8dee2aaSAndroid Build Coastguard Worker template <typename T, bool M> static inline void swap(TArray<T, M>& a, TArray<T, M>& b) {
753*c8dee2aaSAndroid Build Coastguard Worker a.swap(b);
754*c8dee2aaSAndroid Build Coastguard Worker }
755*c8dee2aaSAndroid Build Coastguard Worker
756*c8dee2aaSAndroid Build Coastguard Worker // Subclass of TArray that contains a pre-allocated memory block for the array.
757*c8dee2aaSAndroid Build Coastguard Worker template <int Nreq, typename T, bool MEM_MOVE = sk_is_trivially_relocatable_v<T>>
758*c8dee2aaSAndroid Build Coastguard Worker class STArray : private SkAlignedSTStorage<SkContainerAllocator::RoundUp<T>(Nreq), T>,
759*c8dee2aaSAndroid Build Coastguard Worker public TArray<T, MEM_MOVE> {
760*c8dee2aaSAndroid Build Coastguard Worker // We round up the requested array size to the next capacity multiple.
761*c8dee2aaSAndroid Build Coastguard Worker // This space would likely otherwise go to waste.
762*c8dee2aaSAndroid Build Coastguard Worker static constexpr int N = SkContainerAllocator::RoundUp<T>(Nreq);
763*c8dee2aaSAndroid Build Coastguard Worker static_assert(Nreq > 0);
764*c8dee2aaSAndroid Build Coastguard Worker static_assert(N >= Nreq);
765*c8dee2aaSAndroid Build Coastguard Worker
766*c8dee2aaSAndroid Build Coastguard Worker using Storage = SkAlignedSTStorage<N,T>;
767*c8dee2aaSAndroid Build Coastguard Worker
768*c8dee2aaSAndroid Build Coastguard Worker public:
STArray()769*c8dee2aaSAndroid Build Coastguard Worker STArray()
770*c8dee2aaSAndroid Build Coastguard Worker : Storage{}
771*c8dee2aaSAndroid Build Coastguard Worker , TArray<T, MEM_MOVE>(this) {} // Must use () to avoid confusion with initializer_list
772*c8dee2aaSAndroid Build Coastguard Worker // when T=bool because * are convertable to bool.
773*c8dee2aaSAndroid Build Coastguard Worker
STArray(const T * array,int count)774*c8dee2aaSAndroid Build Coastguard Worker STArray(const T* array, int count)
775*c8dee2aaSAndroid Build Coastguard Worker : Storage{}
776*c8dee2aaSAndroid Build Coastguard Worker , TArray<T, MEM_MOVE>{array, count, this} {}
777*c8dee2aaSAndroid Build Coastguard Worker
STArray(SkSpan<const T> data)778*c8dee2aaSAndroid Build Coastguard Worker STArray(SkSpan<const T> data)
779*c8dee2aaSAndroid Build Coastguard Worker : Storage{}
780*c8dee2aaSAndroid Build Coastguard Worker , TArray<T, MEM_MOVE>{data, this} {}
781*c8dee2aaSAndroid Build Coastguard Worker
STArray(std::initializer_list<T> data)782*c8dee2aaSAndroid Build Coastguard Worker STArray(std::initializer_list<T> data)
783*c8dee2aaSAndroid Build Coastguard Worker : STArray{data.begin(), SkToInt(data.size())} {}
784*c8dee2aaSAndroid Build Coastguard Worker
STArray(int reserveCount)785*c8dee2aaSAndroid Build Coastguard Worker explicit STArray(int reserveCount)
786*c8dee2aaSAndroid Build Coastguard Worker : STArray() { this->reserve_exact(reserveCount); }
787*c8dee2aaSAndroid Build Coastguard Worker
STArray(const STArray & that)788*c8dee2aaSAndroid Build Coastguard Worker STArray(const STArray& that)
789*c8dee2aaSAndroid Build Coastguard Worker : STArray() { *this = that; }
790*c8dee2aaSAndroid Build Coastguard Worker
STArray(const TArray<T,MEM_MOVE> & that)791*c8dee2aaSAndroid Build Coastguard Worker explicit STArray(const TArray<T, MEM_MOVE>& that)
792*c8dee2aaSAndroid Build Coastguard Worker : STArray() { *this = that; }
793*c8dee2aaSAndroid Build Coastguard Worker
STArray(STArray && that)794*c8dee2aaSAndroid Build Coastguard Worker STArray(STArray&& that)
795*c8dee2aaSAndroid Build Coastguard Worker : STArray() { *this = std::move(that); }
796*c8dee2aaSAndroid Build Coastguard Worker
STArray(TArray<T,MEM_MOVE> && that)797*c8dee2aaSAndroid Build Coastguard Worker explicit STArray(TArray<T, MEM_MOVE>&& that)
798*c8dee2aaSAndroid Build Coastguard Worker : STArray() { *this = std::move(that); }
799*c8dee2aaSAndroid Build Coastguard Worker
800*c8dee2aaSAndroid Build Coastguard Worker STArray& operator=(const STArray& that) {
801*c8dee2aaSAndroid Build Coastguard Worker TArray<T, MEM_MOVE>::operator=(that);
802*c8dee2aaSAndroid Build Coastguard Worker return *this;
803*c8dee2aaSAndroid Build Coastguard Worker }
804*c8dee2aaSAndroid Build Coastguard Worker
805*c8dee2aaSAndroid Build Coastguard Worker STArray& operator=(const TArray<T, MEM_MOVE>& that) {
806*c8dee2aaSAndroid Build Coastguard Worker TArray<T, MEM_MOVE>::operator=(that);
807*c8dee2aaSAndroid Build Coastguard Worker return *this;
808*c8dee2aaSAndroid Build Coastguard Worker }
809*c8dee2aaSAndroid Build Coastguard Worker
810*c8dee2aaSAndroid Build Coastguard Worker STArray& operator=(STArray&& that) {
811*c8dee2aaSAndroid Build Coastguard Worker TArray<T, MEM_MOVE>::operator=(std::move(that));
812*c8dee2aaSAndroid Build Coastguard Worker return *this;
813*c8dee2aaSAndroid Build Coastguard Worker }
814*c8dee2aaSAndroid Build Coastguard Worker
815*c8dee2aaSAndroid Build Coastguard Worker STArray& operator=(TArray<T, MEM_MOVE>&& that) {
816*c8dee2aaSAndroid Build Coastguard Worker TArray<T, MEM_MOVE>::operator=(std::move(that));
817*c8dee2aaSAndroid Build Coastguard Worker return *this;
818*c8dee2aaSAndroid Build Coastguard Worker }
819*c8dee2aaSAndroid Build Coastguard Worker
820*c8dee2aaSAndroid Build Coastguard Worker // Force the use of TArray for data() and size().
821*c8dee2aaSAndroid Build Coastguard Worker using TArray<T, MEM_MOVE>::data;
822*c8dee2aaSAndroid Build Coastguard Worker using TArray<T, MEM_MOVE>::size;
823*c8dee2aaSAndroid Build Coastguard Worker };
824*c8dee2aaSAndroid Build Coastguard Worker } // namespace skia_private
825*c8dee2aaSAndroid Build Coastguard Worker #endif // SkTArray_DEFINED
826