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