xref: /aosp_15_r20/external/skia/src/base/SkTDArray.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2018 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 #include "include/private/base/SkTDArray.h"
9 
10 #include "include/private/base/SkMalloc.h"
11 #include "include/private/base/SkTFitsIn.h"
12 #include "include/private/base/SkTo.h"
13 
14 #include <climits>
15 #include <cstddef>
16 #include <cstdint>
17 #include <cstring>
18 #include <new>
19 #include <utility>
20 
SkTDStorage(int sizeOfT)21 SkTDStorage::SkTDStorage(int sizeOfT) : fSizeOfT{sizeOfT} {}
22 
SkTDStorage(const void * src,int size,int sizeOfT)23 SkTDStorage::SkTDStorage(const void* src, int size, int sizeOfT)
24         : fSizeOfT{sizeOfT}
25         , fCapacity{size}
26         , fSize{size} {
27     if (size > 0) {
28         SkASSERT(src != nullptr);
29         size_t storageSize = this->bytes(size);
30         fStorage = static_cast<std::byte*>(sk_malloc_throw(storageSize));
31         memcpy(fStorage, src, storageSize);
32     }
33 }
34 
SkTDStorage(const SkTDStorage & that)35 SkTDStorage::SkTDStorage(const SkTDStorage& that)
36         : SkTDStorage{that.fStorage, that.fSize, that.fSizeOfT} {}
37 
operator =(const SkTDStorage & that)38 SkTDStorage& SkTDStorage::operator=(const SkTDStorage& that) {
39     if (this != &that) {
40         if (that.fSize <= fCapacity) {
41             fSize = that.fSize;
42             if (fSize > 0) {
43                 memcpy(fStorage, that.data(), that.size_bytes());
44             }
45         } else {
46             *this = SkTDStorage{that.data(), that.size(), that.fSizeOfT};
47         }
48     }
49     return *this;
50 }
51 
SkTDStorage(SkTDStorage && that)52 SkTDStorage::SkTDStorage(SkTDStorage&& that)
53         : fSizeOfT{that.fSizeOfT}
54         , fStorage(std::exchange(that.fStorage, nullptr))
55         , fCapacity{that.fCapacity}
56         , fSize{that.fSize} {}
57 
operator =(SkTDStorage && that)58 SkTDStorage& SkTDStorage::operator=(SkTDStorage&& that) {
59     if (this != &that) {
60         this->~SkTDStorage();
61         new (this) SkTDStorage{std::move(that)};
62     }
63     return *this;
64 }
65 
~SkTDStorage()66 SkTDStorage::~SkTDStorage() {
67     sk_free(fStorage);
68 }
69 
reset()70 void SkTDStorage::reset() {
71     const int sizeOfT = fSizeOfT;
72     this->~SkTDStorage();
73     new (this) SkTDStorage{sizeOfT};
74 }
75 
swap(SkTDStorage & that)76 void SkTDStorage::swap(SkTDStorage& that) {
77     SkASSERT(fSizeOfT == that.fSizeOfT);
78     using std::swap;
79     swap(fStorage, that.fStorage);
80     swap(fCapacity, that.fCapacity);
81     swap(fSize, that.fSize);
82 }
83 
resize(int newSize)84 void SkTDStorage::resize(int newSize) {
85     SkASSERT(newSize >= 0);
86     if (newSize > fCapacity) {
87         this->reserve(newSize);
88     }
89     fSize = newSize;
90 }
91 
reserve(int newCapacity)92 void SkTDStorage::reserve(int newCapacity) {
93     SkASSERT(newCapacity >= 0);
94     if (newCapacity > fCapacity) {
95         // Establish the maximum number of elements that includes a valid count for end. In the
96         // largest case end() = &fArray[INT_MAX] which is 1 after the last indexable element.
97         static constexpr int kMaxCount = INT_MAX;
98 
99         // Assume that the array will max out.
100         int expandedReserve = kMaxCount;
101         if (kMaxCount - newCapacity > 4) {
102             // Add 1/4 more than we need. Add 4 to ensure this grows by at least 1. Pin to
103             // kMaxCount if no room for 1/4 growth.
104             int growth = 4 + ((newCapacity + 4) >> 2);
105             // Read this line as: if (count + growth < kMaxCount) { ... }
106             // It's rewritten to avoid signed integer overflow.
107             if (kMaxCount - newCapacity > growth) {
108                 expandedReserve = newCapacity + growth;
109             }
110         }
111 
112 
113         // With a T size of 1, the above allocator produces the progression of 7, 15, ... Since,
114         // the sizeof max_align_t is often 16, there is no reason to allocate anything less than
115         // 16 bytes. This eliminates a realloc when pushing back bytes to an SkTDArray.
116         if (fSizeOfT == 1) {
117             // Round up to the multiple of 16.
118             expandedReserve = (expandedReserve + 15) & ~15;
119         }
120 
121         fCapacity = expandedReserve;
122         size_t newStorageSize = this->bytes(fCapacity);
123         fStorage = static_cast<std::byte*>(sk_realloc_throw(fStorage, newStorageSize));
124     }
125 }
126 
shrink_to_fit()127 void SkTDStorage::shrink_to_fit() {
128     if (fCapacity != fSize) {
129         fCapacity = fSize;
130         // Because calling realloc with size of 0 is implementation defined, force to a good state
131         // by freeing fStorage.
132         if (fCapacity > 0) {
133             fStorage = static_cast<std::byte*>(sk_realloc_throw(fStorage, this->bytes(fCapacity)));
134         } else {
135             sk_free(fStorage);
136             fStorage = nullptr;
137         }
138     }
139 }
140 
erase(int index,int count)141 void SkTDStorage::erase(int index, int count) {
142     SkASSERT(count >= 0);
143     SkASSERT(fSize >= count);
144     SkASSERT(0 <= index && index <= fSize);
145 
146     if (count > 0) {
147         // Check that the resulting size fits in an int. This will abort if not.
148         const int newCount = this->calculateSizeOrDie(-count);
149         this->moveTail(index, index + count, fSize);
150         this->resize(newCount);
151     }
152 }
153 
removeShuffle(int index)154 void SkTDStorage::removeShuffle(int index) {
155     SkASSERT(fSize > 0);
156     SkASSERT(0 <= index && index < fSize);
157     // Check that the new count is valid.
158     const int newCount = this->calculateSizeOrDie(-1);
159     this->moveTail(index, fSize - 1, fSize);
160     this->resize(newCount);
161 }
162 
prepend()163 void* SkTDStorage::prepend() {
164     return this->insert(/*index=*/0);
165 }
166 
append()167 void SkTDStorage::append() {
168     if (fSize < fCapacity) {
169         fSize++;
170     } else {
171         this->insert(fSize);
172     }
173 }
174 
append(int count)175 void SkTDStorage::append(int count) {
176     SkASSERT(count >= 0);
177     // Read as: if (fSize + count <= fCapacity) {...}. This is a UB safe way to avoid the add.
178     if (fCapacity - fSize >= count) {
179         fSize += count;
180     } else {
181         this->insert(fSize, count, nullptr);
182     }
183 }
184 
append(const void * src,int count)185 void* SkTDStorage::append(const void* src, int count) {
186     return this->insert(fSize, count, src);
187 }
188 
insert(int index)189 void* SkTDStorage::insert(int index) {
190     return this->insert(index, /*count=*/1, nullptr);
191 }
192 
insert(int index,int count,const void * src)193 void* SkTDStorage::insert(int index, int count, const void* src) {
194     SkASSERT(0 <= index && index <= fSize);
195     SkASSERT(count >= 0);
196 
197     if (count > 0) {
198         const int oldCount = fSize;
199         const int newCount = this->calculateSizeOrDie(count);
200         this->resize(newCount);
201         this->moveTail(index + count, index, oldCount);
202 
203         if (src != nullptr) {
204             this->copySrc(index, src, count);
205         }
206     }
207 
208     return this->address(index);
209 }
210 
operator ==(const SkTDStorage & a,const SkTDStorage & b)211 bool operator==(const SkTDStorage& a, const SkTDStorage& b) {
212     return a.size() == b.size() && (a.empty() || !memcmp(a.data(), b.data(), a.bytes(a.size())));
213 }
214 
calculateSizeOrDie(int delta)215 int SkTDStorage::calculateSizeOrDie(int delta) {
216     // Check that count will not go negative.
217     SkASSERT_RELEASE(-fSize <= delta);
218 
219     // We take care to avoid overflow here.
220     // Because count and delta are both signed 32-bit ints, the sum of count and delta is at
221     // most 4294967294, which fits fine in uint32_t. Proof follows in assert.
222     static_assert(UINT32_MAX >= (uint32_t)INT_MAX + (uint32_t)INT_MAX);
223     uint32_t testCount = (uint32_t)fSize + (uint32_t)delta;
224     SkASSERT_RELEASE(SkTFitsIn<int>(testCount));
225     return SkToInt(testCount);
226 }
227 
moveTail(int to,int tailStart,int tailEnd)228 void SkTDStorage::moveTail(int to, int tailStart, int tailEnd) {
229     SkASSERT(0 <= to && to <= fSize);
230     SkASSERT(0 <= tailStart && tailStart <= tailEnd && tailEnd <= fSize);
231     if (to != tailStart && tailStart != tailEnd) {
232         this->copySrc(to, this->address(tailStart), tailEnd - tailStart);
233     }
234 }
235 
copySrc(int dstIndex,const void * src,int count)236 void SkTDStorage::copySrc(int dstIndex, const void* src, int count) {
237     SkASSERT(count > 0);
238     memmove(this->address(dstIndex), src, this->bytes(count));
239 }
240