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