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 const uint64_t size64 = sk_64_mul(count, sizeof(T));
179 const size_t size = static_cast<size_t>(size64);
180 if (size != size64) {
181 sk_out_of_memory();
182 }
183 fArray = (T*) sk_malloc_throw(size);
184 } else if (count > 0) {
185 fArray = (T*) fStorage;
186 } else {
187 fArray = NULL;
188 }
189
190 fCount = count;
191 }
192
193 iter = fArray;
194 T* stop = fArray + count;
195 while (iter < stop) {
196 new (iter++) T;
197 }
198 }
199
200 /** Return the number of T elements in the array
201 */
count()202 int count() const { return fCount; }
203
204 /** Return the array of T elements. Will be NULL if count == 0
205 */
get()206 T* get() const { return fArray; }
207
begin()208 T* begin() { return fArray; }
209
begin()210 const T* begin() const { return fArray; }
211
end()212 T* end() { return fArray + fCount; }
213
end()214 const T* end() const { return fArray + fCount; }
215
216 /** Return the nth element in the array
217 */
218 T& operator[](int index) const {
219 SkASSERT(index < fCount);
220 return fArray[index];
221 }
222
223 private:
224 #if defined(GOOGLE3)
225 // Stack frame size is limited for GOOGLE3. 4k is less than the actual max, but some functions
226 // have multiple large stack allocations.
227 static const int kMaxBytes = 4 * 1024;
228 static const int kCount = kCountRequested * sizeof(T) > kMaxBytes
229 ? kMaxBytes / sizeof(T)
230 : kCountRequested;
231 #else
232 static const int kCount = kCountRequested;
233 #endif
234
235 int fCount;
236 T* fArray;
237 // since we come right after fArray, fStorage should be properly aligned
238 char fStorage[kCount * sizeof(T)];
239 };
240
241 /** Manages an array of T elements, freeing the array in the destructor.
242 * Does NOT call any constructors/destructors on T (T must be POD).
243 */
244 template <typename T> class SkAutoTMalloc : SkNoncopyable {
245 public:
246 /** Takes ownership of the ptr. The ptr must be a value which can be passed to sk_free. */
247 explicit SkAutoTMalloc(T* ptr = NULL) {
248 fPtr = ptr;
249 }
250
251 /** Allocates space for 'count' Ts. */
SkAutoTMalloc(size_t count)252 explicit SkAutoTMalloc(size_t count) {
253 fPtr = count ? (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW) : nullptr;
254 }
255
SkAutoTMalloc(SkAutoTMalloc<T> && that)256 SkAutoTMalloc(SkAutoTMalloc<T>&& that) : fPtr(that.release()) {}
257
~SkAutoTMalloc()258 ~SkAutoTMalloc() {
259 sk_free(fPtr);
260 }
261
262 /** Resize the memory area pointed to by the current ptr preserving contents. */
realloc(size_t count)263 void realloc(size_t count) {
264 if (count) {
265 fPtr = reinterpret_cast<T*>(sk_realloc_throw(fPtr, count * sizeof(T)));
266 } else {
267 this->reset(0);
268 }
269 }
270
271 /** Resize the memory area pointed to by the current ptr without preserving contents. */
272 T* reset(size_t count = 0) {
273 sk_free(fPtr);
274 fPtr = count ? (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW) : nullptr;
275 return fPtr;
276 }
277
get()278 T* get() const { return fPtr; }
279
280 operator T*() {
281 return fPtr;
282 }
283
284 operator const T*() const {
285 return fPtr;
286 }
287
288 T& operator[](int index) {
289 return fPtr[index];
290 }
291
292 const T& operator[](int index) const {
293 return fPtr[index];
294 }
295
296 SkAutoTMalloc& operator=(SkAutoTMalloc<T>&& that) {
297 sk_free(fPtr);
298 fPtr = that.release();
299 return *this;
300 }
301
302 /**
303 * Transfer ownership of the ptr to the caller, setting the internal
304 * pointer to NULL. Note that this differs from get(), which also returns
305 * the pointer, but it does not transfer ownership.
306 */
release()307 T* release() {
308 T* ptr = fPtr;
309 fPtr = NULL;
310 return ptr;
311 }
312
313 private:
314 T* fPtr;
315 };
316
317 template <size_t kCountRequested, typename T> class SkAutoSTMalloc : SkNoncopyable {
318 public:
SkAutoSTMalloc()319 SkAutoSTMalloc() : fPtr(fTStorage) {}
320
SkAutoSTMalloc(size_t count)321 SkAutoSTMalloc(size_t count) {
322 if (count > kCount) {
323 fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP);
324 } else if (count) {
325 fPtr = fTStorage;
326 } else {
327 fPtr = nullptr;
328 }
329 }
330
~SkAutoSTMalloc()331 ~SkAutoSTMalloc() {
332 if (fPtr != fTStorage) {
333 sk_free(fPtr);
334 }
335 }
336
337 // doesn't preserve contents
reset(size_t count)338 T* reset(size_t count) {
339 if (fPtr != fTStorage) {
340 sk_free(fPtr);
341 }
342 if (count > kCount) {
343 fPtr = (T*)sk_malloc_throw(count * sizeof(T));
344 } else if (count) {
345 fPtr = fTStorage;
346 } else {
347 fPtr = nullptr;
348 }
349 return fPtr;
350 }
351
get()352 T* get() const { return fPtr; }
353
354 operator T*() {
355 return fPtr;
356 }
357
358 operator const T*() const {
359 return fPtr;
360 }
361
362 T& operator[](int index) {
363 return fPtr[index];
364 }
365
366 const T& operator[](int index) const {
367 return fPtr[index];
368 }
369
370 // Reallocs the array, can be used to shrink the allocation. Makes no attempt to be intelligent
realloc(size_t count)371 void realloc(size_t count) {
372 if (count > kCount) {
373 if (fPtr == fTStorage) {
374 fPtr = (T*)sk_malloc_throw(count * sizeof(T));
375 memcpy(fPtr, fTStorage, kCount * sizeof(T));
376 } else {
377 fPtr = (T*)sk_realloc_throw(fPtr, count * sizeof(T));
378 }
379 } else if (count) {
380 if (fPtr != fTStorage) {
381 fPtr = (T*)sk_realloc_throw(fPtr, count * sizeof(T));
382 }
383 } else {
384 this->reset(0);
385 }
386 }
387
388 private:
389 // Since we use uint32_t storage, we might be able to get more elements for free.
390 static const size_t kCountWithPadding = SkAlign4(kCountRequested*sizeof(T)) / sizeof(T);
391 #if defined(GOOGLE3)
392 // Stack frame size is limited for GOOGLE3. 4k is less than the actual max, but some functions
393 // have multiple large stack allocations.
394 static const size_t kMaxBytes = 4 * 1024;
395 static const size_t kCount = kCountRequested * sizeof(T) > kMaxBytes
396 ? kMaxBytes / sizeof(T)
397 : kCountWithPadding;
398 #else
399 static const size_t kCount = kCountWithPadding;
400 #endif
401
402 T* fPtr;
403 union {
404 uint32_t fStorage32[SkAlign4(kCount*sizeof(T)) >> 2];
405 T fTStorage[1]; // do NOT want to invoke T::T()
406 };
407 };
408
409 //////////////////////////////////////////////////////////////////////////////////////////////////
410
411 /**
412 * Pass the object and the storage that was offered during SkInPlaceNewCheck, and this will
413 * safely destroy (and free if it was dynamically allocated) the object.
414 */
SkInPlaceDeleteCheck(T * obj,void * storage)415 template <typename T> void SkInPlaceDeleteCheck(T* obj, void* storage) {
416 if (storage == obj) {
417 obj->~T();
418 } else {
419 delete obj;
420 }
421 }
422
423 /**
424 * Allocates T, using storage if it is large enough, and allocating on the heap (via new) if
425 * storage is not large enough.
426 *
427 * obj = SkInPlaceNewCheck<Type>(storage, size);
428 * ...
429 * SkInPlaceDeleteCheck(obj, storage);
430 */
SkInPlaceNewCheck(void * storage,size_t size)431 template <typename T> T* SkInPlaceNewCheck(void* storage, size_t size) {
432 return (sizeof(T) <= size) ? new (storage) T : new T;
433 }
434
435 template <typename T, typename A1, typename A2, typename A3>
SkInPlaceNewCheck(void * storage,size_t size,const A1 & a1,const A2 & a2,const A3 & a3)436 T* SkInPlaceNewCheck(void* storage, size_t size, const A1& a1, const A2& a2, const A3& a3) {
437 return (sizeof(T) <= size) ? new (storage) T(a1, a2, a3) : new T(a1, a2, a3);
438 }
439
440 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)441 T* SkInPlaceNewCheck(void* storage, size_t size,
442 const A1& a1, const A2& a2, const A3& a3, const A4& a4) {
443 return (sizeof(T) <= size) ? new (storage) T(a1, a2, a3, a4) : new T(a1, a2, a3, a4);
444 }
445
446 /**
447 * Reserves memory that is aligned on double and pointer boundaries.
448 * Hopefully this is sufficient for all practical purposes.
449 */
450 template <size_t N> class SkAlignedSStorage : SkNoncopyable {
451 public:
size()452 size_t size() const { return N; }
get()453 void* get() { return fData; }
get()454 const void* get() const { return fData; }
455
456 private:
457 union {
458 void* fPtr;
459 double fDouble;
460 char fData[N];
461 };
462 };
463
464 /**
465 * Reserves memory that is aligned on double and pointer boundaries.
466 * Hopefully this is sufficient for all practical purposes. Otherwise,
467 * we have to do some arcane trickery to determine alignment of non-POD
468 * types. Lifetime of the memory is the lifetime of the object.
469 */
470 template <int N, typename T> class SkAlignedSTStorage : SkNoncopyable {
471 public:
472 /**
473 * Returns void* because this object does not initialize the
474 * memory. Use placement new for types that require a cons.
475 */
get()476 void* get() { return fStorage.get(); }
get()477 const void* get() const { return fStorage.get(); }
478 private:
479 SkAlignedSStorage<sizeof(T)*N> fStorage;
480 };
481
482 using SkAutoFree = std::unique_ptr<void, SkFunctionWrapper<void, void, sk_free>>;
483
484 #endif
485