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 /** \class SkAutoTCallIProc
70
71 Call a function when this goes out of scope. The template uses two
72 parameters, the object, and a function that is to be called in the destructor.
73 If release() is called, the object reference is set to null. If the object
74 reference is null when the destructor is called, we do not call the
75 function.
76 */
77 template <typename T, int (*P)(T*)> class SkAutoTCallIProc
78 : public std::unique_ptr<T, SkFunctionWrapper<int, T, P>> {
79 public:
SkAutoTCallIProc(T * obj)80 SkAutoTCallIProc(T* obj): std::unique_ptr<T, SkFunctionWrapper<int, T, P>>(obj) {}
81
82 operator T*() const { return this->get(); }
83 };
84
85 /** Allocate an array of T elements, and free the array in the destructor
86 */
87 template <typename T> class SkAutoTArray : SkNoncopyable {
88 public:
SkAutoTArray()89 SkAutoTArray() {
90 fArray = NULL;
91 SkDEBUGCODE(fCount = 0;)
92 }
93 /** Allocate count number of T elements
94 */
SkAutoTArray(int count)95 explicit SkAutoTArray(int count) {
96 SkASSERT(count >= 0);
97 fArray = NULL;
98 if (count) {
99 fArray = new T[count];
100 }
101 SkDEBUGCODE(fCount = count;)
102 }
103
104 /** Reallocates given a new count. Reallocation occurs even if new count equals old count.
105 */
reset(int count)106 void reset(int count) {
107 delete[] fArray;
108 SkASSERT(count >= 0);
109 fArray = NULL;
110 if (count) {
111 fArray = new T[count];
112 }
113 SkDEBUGCODE(fCount = count;)
114 }
115
~SkAutoTArray()116 ~SkAutoTArray() { delete[] fArray; }
117
118 /** Return the array of T elements. Will be NULL if count == 0
119 */
get()120 T* get() const { return fArray; }
121
122 /** Return the nth element in the array
123 */
124 T& operator[](int index) const {
125 SkASSERT((unsigned)index < (unsigned)fCount);
126 return fArray[index];
127 }
128
swap(SkAutoTArray & other)129 void swap(SkAutoTArray& other) {
130 SkTSwap(fArray, other.fArray);
131 SkDEBUGCODE(SkTSwap(fCount, other.fCount));
132 }
133
134 private:
135 T* fArray;
136 SkDEBUGCODE(int fCount;)
137 };
138
139 /** Wraps SkAutoTArray, with room for kCountRequested elements preallocated.
140 */
141 template <int kCountRequested, typename T> class SkAutoSTArray : SkNoncopyable {
142 public:
143 /** Initialize with no objects */
SkAutoSTArray()144 SkAutoSTArray() {
145 fArray = NULL;
146 fCount = 0;
147 }
148
149 /** Allocate count number of T elements
150 */
SkAutoSTArray(int count)151 SkAutoSTArray(int count) {
152 fArray = NULL;
153 fCount = 0;
154 this->reset(count);
155 }
156
~SkAutoSTArray()157 ~SkAutoSTArray() {
158 this->reset(0);
159 }
160
161 /** Destroys previous objects in the array and default constructs count number of objects */
reset(int count)162 void reset(int count) {
163 T* start = fArray;
164 T* iter = start + fCount;
165 while (iter > start) {
166 (--iter)->~T();
167 }
168
169 SkASSERT(count >= 0);
170 if (fCount != count) {
171 if (fCount > kCount) {
172 // 'fArray' was allocated last time so free it now
173 SkASSERT((T*) fStorage != fArray);
174 sk_free(fArray);
175 }
176
177 if (count > kCount) {
178 fArray = (T*) sk_malloc_throw(count, sizeof(T));
179 } else if (count > 0) {
180 fArray = (T*) fStorage;
181 } else {
182 fArray = NULL;
183 }
184
185 fCount = count;
186 }
187
188 iter = fArray;
189 T* stop = fArray + count;
190 while (iter < stop) {
191 new (iter++) T;
192 }
193 }
194
195 /** Return the number of T elements in the array
196 */
count()197 int count() const { return fCount; }
198
199 /** Return the array of T elements. Will be NULL if count == 0
200 */
get()201 T* get() const { return fArray; }
202
begin()203 T* begin() { return fArray; }
204
begin()205 const T* begin() const { return fArray; }
206
end()207 T* end() { return fArray + fCount; }
208
end()209 const T* end() const { return fArray + fCount; }
210
211 /** Return the nth element in the array
212 */
213 T& operator[](int index) const {
214 SkASSERT(index < fCount);
215 return fArray[index];
216 }
217
218 private:
219 #if defined(GOOGLE3)
220 // Stack frame size is limited for GOOGLE3. 4k is less than the actual max, but some functions
221 // have multiple large stack allocations.
222 static const int kMaxBytes = 4 * 1024;
223 static const int kCount = kCountRequested * sizeof(T) > kMaxBytes
224 ? kMaxBytes / sizeof(T)
225 : kCountRequested;
226 #else
227 static const int kCount = kCountRequested;
228 #endif
229
230 int fCount;
231 T* fArray;
232 // since we come right after fArray, fStorage should be properly aligned
233 char fStorage[kCount * sizeof(T)];
234 };
235
236 /** Manages an array of T elements, freeing the array in the destructor.
237 * Does NOT call any constructors/destructors on T (T must be POD).
238 */
239 template <typename T> class SkAutoTMalloc : SkNoncopyable {
240 public:
241 /** Takes ownership of the ptr. The ptr must be a value which can be passed to sk_free. */
242 explicit SkAutoTMalloc(T* ptr = NULL) {
243 fPtr = ptr;
244 }
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<T> && that)251 SkAutoTMalloc(SkAutoTMalloc<T>&& that) : fPtr(that.release()) {}
252
~SkAutoTMalloc()253 ~SkAutoTMalloc() {
254 sk_free(fPtr);
255 }
256
257 /** Resize the memory area pointed to by the current ptr preserving contents. */
realloc(size_t count)258 void realloc(size_t count) {
259 if (count) {
260 fPtr = reinterpret_cast<T*>(sk_realloc_throw(fPtr, count * sizeof(T)));
261 } else {
262 this->reset(0);
263 }
264 }
265
266 /** Resize the memory area pointed to by the current ptr without preserving contents. */
267 T* reset(size_t count = 0) {
268 sk_free(fPtr);
269 fPtr = count ? (T*)sk_malloc_throw(count, sizeof(T)) : nullptr;
270 return fPtr;
271 }
272
get()273 T* get() const { return fPtr; }
274
275 operator T*() {
276 return fPtr;
277 }
278
279 operator const T*() const {
280 return fPtr;
281 }
282
283 T& operator[](int index) {
284 return fPtr[index];
285 }
286
287 const T& operator[](int index) const {
288 return fPtr[index];
289 }
290
291 SkAutoTMalloc& operator=(SkAutoTMalloc<T>&& that) {
292 if (this != &that) {
293 sk_free(fPtr);
294 fPtr = that.release();
295 }
296 return *this;
297 }
298
299 /**
300 * Transfer ownership of the ptr to the caller, setting the internal
301 * pointer to NULL. Note that this differs from get(), which also returns
302 * the pointer, but it does not transfer ownership.
303 */
release()304 T* release() {
305 T* ptr = fPtr;
306 fPtr = NULL;
307 return ptr;
308 }
309
310 private:
311 T* fPtr;
312 };
313
314 template <size_t kCountRequested, typename T> class SkAutoSTMalloc : SkNoncopyable {
315 public:
SkAutoSTMalloc()316 SkAutoSTMalloc() : fPtr(fTStorage) {}
317
SkAutoSTMalloc(size_t count)318 SkAutoSTMalloc(size_t count) {
319 if (count > kCount) {
320 fPtr = (T*)sk_malloc_throw(count, sizeof(T));
321 } else if (count) {
322 fPtr = fTStorage;
323 } else {
324 fPtr = nullptr;
325 }
326 }
327
~SkAutoSTMalloc()328 ~SkAutoSTMalloc() {
329 if (fPtr != fTStorage) {
330 sk_free(fPtr);
331 }
332 }
333
334 // doesn't preserve contents
reset(size_t count)335 T* reset(size_t count) {
336 if (fPtr != fTStorage) {
337 sk_free(fPtr);
338 }
339 if (count > kCount) {
340 fPtr = (T*)sk_malloc_throw(count, sizeof(T));
341 } else if (count) {
342 fPtr = fTStorage;
343 } else {
344 fPtr = nullptr;
345 }
346 return fPtr;
347 }
348
get()349 T* get() const { return fPtr; }
350
351 operator T*() {
352 return fPtr;
353 }
354
355 operator const T*() const {
356 return fPtr;
357 }
358
359 T& operator[](int index) {
360 return fPtr[index];
361 }
362
363 const T& operator[](int index) const {
364 return fPtr[index];
365 }
366
367 // Reallocs the array, can be used to shrink the allocation. Makes no attempt to be intelligent
realloc(size_t count)368 void realloc(size_t count) {
369 if (count > kCount) {
370 if (fPtr == fTStorage) {
371 fPtr = (T*)sk_malloc_throw(count, sizeof(T));
372 memcpy(fPtr, fTStorage, kCount * sizeof(T));
373 } else {
374 fPtr = (T*)sk_realloc_throw(fPtr, count, sizeof(T));
375 }
376 } else if (count) {
377 if (fPtr != fTStorage) {
378 fPtr = (T*)sk_realloc_throw(fPtr, count, sizeof(T));
379 }
380 } else {
381 this->reset(0);
382 }
383 }
384
385 private:
386 // Since we use uint32_t storage, we might be able to get more elements for free.
387 static const size_t kCountWithPadding = SkAlign4(kCountRequested*sizeof(T)) / sizeof(T);
388 #if defined(GOOGLE3)
389 // Stack frame size is limited for GOOGLE3. 4k is less than the actual max, but some functions
390 // have multiple large stack allocations.
391 static const size_t kMaxBytes = 4 * 1024;
392 static const size_t kCount = kCountRequested * sizeof(T) > kMaxBytes
393 ? kMaxBytes / sizeof(T)
394 : kCountWithPadding;
395 #else
396 static const size_t kCount = kCountWithPadding;
397 #endif
398
399 T* fPtr;
400 union {
401 uint32_t fStorage32[SkAlign4(kCount*sizeof(T)) >> 2];
402 T fTStorage[1]; // do NOT want to invoke T::T()
403 };
404 };
405
406 //////////////////////////////////////////////////////////////////////////////////////////////////
407
408 /**
409 * Pass the object and the storage that was offered during SkInPlaceNewCheck, and this will
410 * safely destroy (and free if it was dynamically allocated) the object.
411 */
SkInPlaceDeleteCheck(T * obj,void * storage)412 template <typename T> void SkInPlaceDeleteCheck(T* obj, void* storage) {
413 if (storage == obj) {
414 obj->~T();
415 } else {
416 delete obj;
417 }
418 }
419
420 /**
421 * Allocates T, using storage if it is large enough, and allocating on the heap (via new) if
422 * storage is not large enough.
423 *
424 * obj = SkInPlaceNewCheck<Type>(storage, size);
425 * ...
426 * SkInPlaceDeleteCheck(obj, storage);
427 */
SkInPlaceNewCheck(void * storage,size_t size)428 template <typename T> T* SkInPlaceNewCheck(void* storage, size_t size) {
429 return (sizeof(T) <= size) ? new (storage) T : new T;
430 }
431
432 template <typename T, typename A1, typename A2, typename A3>
SkInPlaceNewCheck(void * storage,size_t size,const A1 & a1,const A2 & a2,const A3 & a3)433 T* SkInPlaceNewCheck(void* storage, size_t size, const A1& a1, const A2& a2, const A3& a3) {
434 return (sizeof(T) <= size) ? new (storage) T(a1, a2, a3) : new T(a1, a2, a3);
435 }
436
437 template <typename T, typename A1, typename A2, typename A3, typename A4>
SkInPlaceNewCheck(void * storage,size_t size,const A1 & a1,const A2 & a2,const A3 & a3,const A4 & a4)438 T* SkInPlaceNewCheck(void* storage, size_t size,
439 const A1& a1, const A2& a2, const A3& a3, const A4& a4) {
440 return (sizeof(T) <= size) ? new (storage) T(a1, a2, a3, a4) : new T(a1, a2, a3, a4);
441 }
442
443 /**
444 * Reserves memory that is aligned on double and pointer boundaries.
445 * Hopefully this is sufficient for all practical purposes.
446 */
447 template <size_t N> class SkAlignedSStorage : SkNoncopyable {
448 public:
size()449 size_t size() const { return N; }
get()450 void* get() { return fData; }
get()451 const void* get() const { return fData; }
452
453 private:
454 union {
455 void* fPtr;
456 double fDouble;
457 char fData[N];
458 };
459 };
460
461 /**
462 * Reserves memory that is aligned on double and pointer boundaries.
463 * Hopefully this is sufficient for all practical purposes. Otherwise,
464 * we have to do some arcane trickery to determine alignment of non-POD
465 * types. Lifetime of the memory is the lifetime of the object.
466 */
467 template <int N, typename T> class SkAlignedSTStorage : SkNoncopyable {
468 public:
469 /**
470 * Returns void* because this object does not initialize the
471 * memory. Use placement new for types that require a cons.
472 */
get()473 void* get() { return fStorage.get(); }
get()474 const void* get() const { return fStorage.get(); }
475 private:
476 SkAlignedSStorage<sizeof(T)*N> fStorage;
477 };
478
479 using SkAutoFree = std::unique_ptr<void, SkFunctionWrapper<void, void, sk_free>>;
480
481 #endif
482