1 /*
2 * Copyright 2006 The Android Open Source Project
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 SkTemplates_DEFINED
9 #define SkTemplates_DEFINED
10
11 #include "include/private/base/SkAlign.h"
12 #include "include/private/base/SkAssert.h"
13 #include "include/private/base/SkDebug.h"
14 #include "include/private/base/SkMalloc.h"
15 #include "include/private/base/SkTLogic.h"
16 #include "include/private/base/SkTo.h"
17
18 #include <array>
19 #include <cstddef>
20 #include <cstdint>
21 #include <cstring>
22 #include <memory>
23 #include <type_traits>
24 #include <utility>
25
26
27 /** \file SkTemplates.h
28
29 This file contains light-weight template classes for type-safe and exception-safe
30 resource management.
31 */
32
33 /**
34 * Marks a local variable as known to be unused (to avoid warnings).
35 * Note that this does *not* prevent the local variable from being optimized away.
36 */
sk_ignore_unused_variable(const T &)37 template<typename T> inline void sk_ignore_unused_variable(const T&) { }
38
39 /**
40 * This is a general purpose absolute-value function.
41 * See SkAbs32 in (SkSafe32.h) for a 32-bit int specific version that asserts.
42 */
SkTAbs(T value)43 template <typename T> static inline T SkTAbs(T value) {
44 if (value < 0) {
45 value = -value;
46 }
47 return value;
48 }
49
50 /**
51 * Returns a pointer to a D which comes immediately after S[count].
52 */
53 template <typename D, typename S> inline D* SkTAfter(S* ptr, size_t count = 1) {
54 return reinterpret_cast<D*>(ptr + count);
55 }
56
57 /**
58 * Returns a pointer to a D which comes byteOffset bytes after S.
59 */
SkTAddOffset(S * ptr,ptrdiff_t byteOffset)60 template <typename D, typename S> inline D* SkTAddOffset(S* ptr, ptrdiff_t byteOffset) {
61 // The intermediate char* has the same cv-ness as D as this produces better error messages.
62 // This relies on the fact that reinterpret_cast can add constness, but cannot remove it.
63 return reinterpret_cast<D*>(reinterpret_cast<sknonstd::same_cv_t<char, D>*>(ptr) + byteOffset);
64 }
65
66 template <typename T, T* P> struct SkOverloadedFunctionObject {
67 template <typename... Args>
68 auto operator()(Args&&... args) const -> decltype(P(std::forward<Args>(args)...)) {
69 return P(std::forward<Args>(args)...);
70 }
71 };
72
73 template <auto F> using SkFunctionObject =
74 SkOverloadedFunctionObject<std::remove_pointer_t<decltype(F)>, F>;
75
76 /** \class SkAutoTCallVProc
77
78 Call a function when this goes out of scope. The template uses two
79 parameters, the object, and a function that is to be called in the destructor.
80 If release() is called, the object reference is set to null. If the object
81 reference is null when the destructor is called, we do not call the
82 function.
83 */
84 template <typename T, void (*P)(T*)> class SkAutoTCallVProc
85 : public std::unique_ptr<T, SkFunctionObject<P>> {
86 using inherited = std::unique_ptr<T, SkFunctionObject<P>>;
87 public:
88 using inherited::inherited;
89 SkAutoTCallVProc(const SkAutoTCallVProc&) = delete;
SkAutoTCallVProc(SkAutoTCallVProc && that)90 SkAutoTCallVProc(SkAutoTCallVProc&& that) : inherited(std::move(that)) {}
91
92 operator T*() const { return this->get(); }
93 };
94
95
96 namespace skia_private {
97 /** Allocate an array of T elements, and free the array in the destructor
98 */
99 template <typename T> class AutoTArray {
100 public:
AutoTArray()101 AutoTArray() {}
102 // Allocate size number of T elements
AutoTArray(size_t size)103 explicit AutoTArray(size_t size) {
104 fSize = check_size_bytes_too_big<T>(size);
105 fData.reset(size > 0 ? new T[size] : nullptr);
106 }
107
108 // TODO: remove when all uses are gone.
AutoTArray(int size)109 explicit AutoTArray(int size) : AutoTArray(SkToSizeT(size)) {}
110
AutoTArray(AutoTArray && other)111 AutoTArray(AutoTArray&& other) : fData(std::move(other.fData)) {
112 fSize = std::exchange(other.fSize, 0);
113 }
114 AutoTArray& operator=(AutoTArray&& other) {
115 if (this != &other) {
116 fData = std::move(other.fData);
117 fSize = std::exchange(other.fSize, 0);
118 }
119 return *this;
120 }
121
122 // Reallocates given a new count. Reallocation occurs even if new count equals old count.
123 void reset(size_t count = 0) {
124 *this = AutoTArray(count);
125 }
126
get()127 T* get() const { return fData.get(); }
128
129 T& operator[](size_t index) const {
130 return fData[sk_collection_check_bounds(index, fSize)];
131 }
132
data()133 const T* data() const { return fData.get(); }
data()134 T* data() { return fData.get(); }
135
size()136 size_t size() const { return fSize; }
empty()137 bool empty() const { return fSize == 0; }
size_bytes()138 size_t size_bytes() const { return sizeof(T) * fSize; }
139
begin()140 T* begin() {
141 return fData;
142 }
begin()143 const T* begin() const {
144 return fData;
145 }
146
147 // It's safe to use fItemArray + fSize because if fItemArray is nullptr then adding 0 is
148 // valid and returns nullptr. See [expr.add] in the C++ standard.
end()149 T* end() {
150 if (fData == nullptr) {
151 SkASSERT(fSize == 0);
152 }
153 return fData + fSize;
154 }
end()155 const T* end() const {
156 if (fData == nullptr) {
157 SkASSERT(fSize == 0);
158 }
159 return fData + fSize;
160 }
161
162 private:
163 std::unique_ptr<T[]> fData;
164 size_t fSize = 0;
165 };
166
167 /** Wraps AutoTArray, with room for kCountRequested elements preallocated.
168 */
169 template <int kCountRequested, typename T> class AutoSTArray {
170 public:
171 AutoSTArray(AutoSTArray&&) = delete;
172 AutoSTArray(const AutoSTArray&) = delete;
173 AutoSTArray& operator=(AutoSTArray&&) = delete;
174 AutoSTArray& operator=(const AutoSTArray&) = delete;
175
176 /** Initialize with no objects */
AutoSTArray()177 AutoSTArray() {
178 fArray = nullptr;
179 fCount = 0;
180 }
181
182 /** Allocate count number of T elements
183 */
AutoSTArray(int count)184 AutoSTArray(int count) {
185 fArray = nullptr;
186 fCount = 0;
187 this->reset(count);
188 }
189
~AutoSTArray()190 ~AutoSTArray() {
191 this->reset(0);
192 }
193
194 /** Destroys previous objects in the array and default constructs count number of objects */
reset(int count)195 void reset(int count) {
196 T* start = fArray;
197 T* iter = start + fCount;
198 while (iter > start) {
199 (--iter)->~T();
200 }
201
202 SkASSERT(count >= 0);
203 if (fCount != count) {
204 if (fCount > kCount) {
205 // 'fArray' was allocated last time so free it now
206 SkASSERT((T*) fStorage != fArray);
207 sk_free(fArray);
208 }
209
210 if (count > kCount) {
211 fArray = (T*) sk_malloc_throw(count, sizeof(T));
212 } else if (count > 0) {
213 fArray = (T*) fStorage;
214 } else {
215 fArray = nullptr;
216 }
217
218 fCount = count;
219 }
220
221 iter = fArray;
222 T* stop = fArray + count;
223 while (iter < stop) {
224 new (iter++) T;
225 }
226 }
227
228 /** Return the number of T elements in the array
229 */
count()230 int count() const { return fCount; }
231
232 /** Return the array of T elements. Will be NULL if count == 0
233 */
get()234 T* get() const { return fArray; }
235
begin()236 T* begin() { return fArray; }
237
begin()238 const T* begin() const { return fArray; }
239
end()240 T* end() { return fArray + fCount; }
241
end()242 const T* end() const { return fArray + fCount; }
243
244 /** Return the nth element in the array
245 */
246 T& operator[](int index) const {
247 return fArray[sk_collection_check_bounds(index, fCount)];
248 }
249
250 /** Aliases matching other types, like std::vector. */
data()251 const T* data() const { return fArray; }
data()252 T* data() { return fArray; }
size()253 size_t size() const { return fCount; }
254
255 private:
256 #if defined(SK_BUILD_FOR_GOOGLE3)
257 // Stack frame size is limited for SK_BUILD_FOR_GOOGLE3. 4k is less than the actual max,
258 // but some functions have multiple large stack allocations.
259 static const int kMaxBytes = 4 * 1024;
260 static const int kCount = kCountRequested * sizeof(T) > kMaxBytes
261 ? kMaxBytes / sizeof(T)
262 : kCountRequested;
263 #else
264 static const int kCount = kCountRequested;
265 #endif
266
267 int fCount;
268 T* fArray;
269 alignas(T) char fStorage[kCount * sizeof(T)];
270 };
271
272 /** Manages an array of T elements, freeing the array in the destructor.
273 * Does NOT call any constructors/destructors on T (T must be POD).
274 */
275 template <typename T,
276 typename = std::enable_if_t<std::is_trivially_default_constructible<T>::value &&
277 std::is_trivially_destructible<T>::value>>
278 class AutoTMalloc {
279 public:
280 /** Takes ownership of the ptr. The ptr must be a value which can be passed to sk_free. */
fPtr(ptr)281 explicit AutoTMalloc(T* ptr = nullptr) : fPtr(ptr) {}
282
283 /** Allocates space for 'count' Ts. */
AutoTMalloc(size_t count)284 explicit AutoTMalloc(size_t count)
285 : fPtr(count ? (T*)sk_malloc_throw(count, sizeof(T)) : nullptr) {}
286
287 AutoTMalloc(AutoTMalloc&&) = default;
288 AutoTMalloc& operator=(AutoTMalloc&&) = default;
289
290 /** Resize the memory area pointed to by the current ptr preserving contents. */
realloc(size_t count)291 void realloc(size_t count) {
292 fPtr.reset(count ? (T*)sk_realloc_throw(fPtr.release(), count * sizeof(T)) : nullptr);
293 }
294
295 /** Resize the memory area pointed to by the current ptr without preserving contents. */
296 T* reset(size_t count = 0) {
297 fPtr.reset(count ? (T*)sk_malloc_throw(count, sizeof(T)) : nullptr);
298 return this->get();
299 }
300
get()301 T* get() const { return fPtr.get(); }
302
303 operator T*() { return fPtr.get(); }
304
305 operator const T*() const { return fPtr.get(); }
306
307 T& operator[](int index) { return fPtr.get()[index]; }
308
309 const T& operator[](int index) const { return fPtr.get()[index]; }
310
311 /** Aliases matching other types, like std::vector. */
data()312 const T* data() const { return fPtr.get(); }
data()313 T* data() { return fPtr.get(); }
314
315 /**
316 * Transfer ownership of the ptr to the caller, setting the internal
317 * pointer to NULL. Note that this differs from get(), which also returns
318 * the pointer, but it does not transfer ownership.
319 */
release()320 T* release() { return fPtr.release(); }
321
322 private:
323 std::unique_ptr<T, SkOverloadedFunctionObject<void(void*), sk_free>> fPtr;
324 };
325
326 template <size_t kCountRequested,
327 typename T,
328 typename = std::enable_if_t<std::is_trivially_default_constructible<T>::value &&
329 std::is_trivially_destructible<T>::value>>
330 class AutoSTMalloc {
331 public:
AutoSTMalloc()332 AutoSTMalloc() : fPtr(fTStorage) {}
333
AutoSTMalloc(size_t count)334 AutoSTMalloc(size_t count) {
335 if (count > kCount) {
336 fPtr = (T*)sk_malloc_throw(count, sizeof(T));
337 } else if (count) {
338 fPtr = fTStorage;
339 } else {
340 fPtr = nullptr;
341 }
342 }
343
344 AutoSTMalloc(AutoSTMalloc&&) = delete;
345 AutoSTMalloc(const AutoSTMalloc&) = delete;
346 AutoSTMalloc& operator=(AutoSTMalloc&&) = delete;
347 AutoSTMalloc& operator=(const AutoSTMalloc&) = delete;
348
~AutoSTMalloc()349 ~AutoSTMalloc() {
350 if (fPtr != fTStorage) {
351 sk_free(fPtr);
352 }
353 }
354
355 // doesn't preserve contents
reset(size_t count)356 T* reset(size_t count) {
357 if (fPtr != fTStorage) {
358 sk_free(fPtr);
359 }
360 if (count > kCount) {
361 fPtr = (T*)sk_malloc_throw(count, sizeof(T));
362 } else if (count) {
363 fPtr = fTStorage;
364 } else {
365 fPtr = nullptr;
366 }
367 return fPtr;
368 }
369
get()370 T* get() const { return fPtr; }
371
372 operator T*() {
373 return fPtr;
374 }
375
376 operator const T*() const {
377 return fPtr;
378 }
379
380 T& operator[](int index) {
381 return fPtr[index];
382 }
383
384 const T& operator[](int index) const {
385 return fPtr[index];
386 }
387
388 /** Aliases matching other types, like std::vector. */
data()389 const T* data() const { return fPtr; }
data()390 T* data() { return fPtr; }
391
392 // Reallocs the array, can be used to shrink the allocation. Makes no attempt to be intelligent
realloc(size_t count)393 void realloc(size_t count) {
394 if (count > kCount) {
395 if (fPtr == fTStorage) {
396 fPtr = (T*)sk_malloc_throw(count, sizeof(T));
397 memcpy((void*)fPtr, fTStorage, kCount * sizeof(T));
398 } else {
399 fPtr = (T*)sk_realloc_throw(fPtr, count, sizeof(T));
400 }
401 } else if (count) {
402 if (fPtr != fTStorage) {
403 fPtr = (T*)sk_realloc_throw(fPtr, count, sizeof(T));
404 }
405 } else {
406 this->reset(0);
407 }
408 }
409
410 private:
411 // Since we use uint32_t storage, we might be able to get more elements for free.
412 static const size_t kCountWithPadding = SkAlign4(kCountRequested*sizeof(T)) / sizeof(T);
413 #if defined(SK_BUILD_FOR_GOOGLE3)
414 // Stack frame size is limited for SK_BUILD_FOR_GOOGLE3. 4k is less than the actual max, but some functions
415 // have multiple large stack allocations.
416 static const size_t kMaxBytes = 4 * 1024;
417 static const size_t kCount = kCountRequested * sizeof(T) > kMaxBytes
418 ? kMaxBytes / sizeof(T)
419 : kCountWithPadding;
420 #else
421 static const size_t kCount = kCountWithPadding;
422 #endif
423
424 T* fPtr;
425 union {
426 uint32_t fStorage32[SkAlign4(kCount*sizeof(T)) >> 2];
427 T fTStorage[1]; // do NOT want to invoke T::T()
428 };
429 };
430
431 using UniqueVoidPtr = std::unique_ptr<void, SkOverloadedFunctionObject<void(void*), sk_free>>;
432
433 } // namespace skia_private
434
435 template<typename C, std::size_t... Is>
436 constexpr auto SkMakeArrayFromIndexSequence(C c, std::index_sequence<Is...> is)
437 -> std::array<decltype(c(std::declval<typename decltype(is)::value_type>())), sizeof...(Is)> {
438 return {{ c(Is)... }};
439 }
440
441 template<size_t N, typename C> constexpr auto SkMakeArray(C c)
442 -> std::array<decltype(c(std::declval<typename std::index_sequence<N>::value_type>())), N> {
443 return SkMakeArrayFromIndexSequence(c, std::make_index_sequence<N>{});
444 }
445
446 #endif
447