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