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