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