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 "SkTypes.h"
15 #include <limits.h>
16 #include <new>
17
18 /** \file SkTemplates.h
19
20 This file contains light-weight template classes for type-safe and exception-safe
21 resource management.
22 */
23
24 /**
25 * Marks a local variable as known to be unused (to avoid warnings).
26 * Note that this does *not* prevent the local variable from being optimized away.
27 */
sk_ignore_unused_variable(const T &)28 template<typename T> inline void sk_ignore_unused_variable(const T&) { }
29
30 /**
31 * SkTIsConst<T>::value is true if the type T is const.
32 * The type T is constrained not to be an array or reference type.
33 */
34 template <typename T> struct SkTIsConst {
35 static T* t;
36 static uint16_t test(const volatile void*);
37 static uint32_t test(volatile void *);
38 static const bool value = (sizeof(uint16_t) == sizeof(test(t)));
39 };
40
41 ///@{
42 /** SkTConstType<T, CONST>::type will be 'const T' if CONST is true, 'T' otherwise. */
43 template <typename T, bool CONST> struct SkTConstType {
44 typedef T type;
45 };
46 template <typename T> struct SkTConstType<T, true> {
47 typedef const T type;
48 };
49 ///@}
50
51 /**
52 * Returns a pointer to a D which comes immediately after S[count].
53 */
54 template <typename D, typename S> static D* SkTAfter(S* ptr, size_t count = 1) {
55 return reinterpret_cast<D*>(ptr + count);
56 }
57
58 /**
59 * Returns a pointer to a D which comes byteOffset bytes after S.
60 */
61 template <typename D, typename S> static D* SkTAddOffset(S* ptr, size_t byteOffset) {
62 // The intermediate char* has the same const-ness as D as this produces better error messages.
63 // This relies on the fact that reinterpret_cast can add constness, but cannot remove it.
64 return reinterpret_cast<D*>(
65 reinterpret_cast<typename SkTConstType<char, SkTIsConst<D>::value>::type*>(ptr) + byteOffset
66 );
67 }
68
69 /** \class SkAutoTCallVProc
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 detach() 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, void (*P)(T*)> class SkAutoTCallVProc : SkNoncopyable {
78 public:
79 SkAutoTCallVProc(T* obj): fObj(obj) {}
80 ~SkAutoTCallVProc() { if (fObj) P(fObj); }
81
82 operator T*() const { return fObj; }
83 T* operator->() const { SkASSERT(fObj); return fObj; }
84
85 T* detach() { T* obj = fObj; fObj = NULL; return obj; }
86 void reset(T* obj = NULL) {
87 if (fObj != obj) {
88 if (fObj) {
89 P(fObj);
90 }
91 fObj = obj;
92 }
93 }
94 private:
95 T* fObj;
96 };
97
98 /** \class SkAutoTCallIProc
99
100 Call a function when this goes out of scope. The template uses two
101 parameters, the object, and a function that is to be called in the destructor.
102 If detach() is called, the object reference is set to null. If the object
103 reference is null when the destructor is called, we do not call the
104 function.
105 */
106 template <typename T, int (*P)(T*)> class SkAutoTCallIProc : SkNoncopyable {
107 public:
108 SkAutoTCallIProc(T* obj): fObj(obj) {}
109 ~SkAutoTCallIProc() { if (fObj) P(fObj); }
110
111 operator T*() const { return fObj; }
112 T* operator->() const { SkASSERT(fObj); return fObj; }
113
114 T* detach() { T* obj = fObj; fObj = NULL; return obj; }
115 private:
116 T* fObj;
117 };
118
119 /** \class SkAutoTDelete
120 An SkAutoTDelete<T> is like a T*, except that the destructor of SkAutoTDelete<T>
121 automatically deletes the pointer it holds (if any). That is, SkAutoTDelete<T>
122 owns the T object that it points to. Like a T*, an SkAutoTDelete<T> may hold
123 either NULL or a pointer to a T object. Also like T*, SkAutoTDelete<T> is
124 thread-compatible, and once you dereference it, you get the threadsafety
125 guarantees of T.
126
127 The size of a SkAutoTDelete is small: sizeof(SkAutoTDelete<T>) == sizeof(T*)
128 */
129 template <typename T> class SkAutoTDelete : SkNoncopyable {
130 public:
131 SkAutoTDelete(T* obj = NULL) : fObj(obj) {}
132 ~SkAutoTDelete() { SkDELETE(fObj); }
133
134 T* get() const { return fObj; }
135 operator T*() const { return fObj; }
136 T& operator*() const { SkASSERT(fObj); return *fObj; }
137 T* operator->() const { SkASSERT(fObj); return fObj; }
138
139 void reset(T* obj) {
140 if (fObj != obj) {
141 SkDELETE(fObj);
142 fObj = obj;
143 }
144 }
145
146 /**
147 * Delete the owned object, setting the internal pointer to NULL.
148 */
149 void free() {
150 SkDELETE(fObj);
151 fObj = NULL;
152 }
153
154 /**
155 * Transfer ownership of the object to the caller, setting the internal
156 * pointer to NULL. Note that this differs from get(), which also returns
157 * the pointer, but it does not transfer ownership.
158 */
159 T* detach() {
160 T* obj = fObj;
161 fObj = NULL;
162 return obj;
163 }
164
165 void swap(SkAutoTDelete* that) {
166 SkTSwap(fObj, that->fObj);
167 }
168
169 private:
170 T* fObj;
171 };
172
173 // Calls ~T() in the destructor.
174 template <typename T> class SkAutoTDestroy : SkNoncopyable {
175 public:
176 SkAutoTDestroy(T* obj = NULL) : fObj(obj) {}
177 ~SkAutoTDestroy() {
178 if (fObj) {
179 fObj->~T();
180 }
181 }
182
183 T* get() const { return fObj; }
184 T& operator*() const { SkASSERT(fObj); return *fObj; }
185 T* operator->() const { SkASSERT(fObj); return fObj; }
186
187 private:
188 T* fObj;
189 };
190
191 template <typename T> class SkAutoTDeleteArray : SkNoncopyable {
192 public:
193 SkAutoTDeleteArray(T array[]) : fArray(array) {}
194 ~SkAutoTDeleteArray() { SkDELETE_ARRAY(fArray); }
195
196 T* get() const { return fArray; }
197 void free() { SkDELETE_ARRAY(fArray); fArray = NULL; }
198 T* detach() { T* array = fArray; fArray = NULL; return array; }
199
200 void reset(T array[]) {
201 if (fArray != array) {
202 SkDELETE_ARRAY(fArray);
203 fArray = array;
204 }
205 }
206
207 private:
208 T* fArray;
209 };
210
211 /** Allocate an array of T elements, and free the array in the destructor
212 */
213 template <typename T> class SkAutoTArray : SkNoncopyable {
214 public:
215 SkAutoTArray() {
216 fArray = NULL;
217 SkDEBUGCODE(fCount = 0;)
218 }
219 /** Allocate count number of T elements
220 */
221 explicit SkAutoTArray(int count) {
222 SkASSERT(count >= 0);
223 fArray = NULL;
224 if (count) {
225 fArray = SkNEW_ARRAY(T, count);
226 }
227 SkDEBUGCODE(fCount = count;)
228 }
229
230 /** Reallocates given a new count. Reallocation occurs even if new count equals old count.
231 */
232 void reset(int count) {
233 SkDELETE_ARRAY(fArray);
234 SkASSERT(count >= 0);
235 fArray = NULL;
236 if (count) {
237 fArray = SkNEW_ARRAY(T, count);
238 }
239 SkDEBUGCODE(fCount = count;)
240 }
241
242 ~SkAutoTArray() {
243 SkDELETE_ARRAY(fArray);
244 }
245
246 /** Return the array of T elements. Will be NULL if count == 0
247 */
248 T* get() const { return fArray; }
249
250 /** Return the nth element in the array
251 */
252 T& operator[](int index) const {
253 SkASSERT((unsigned)index < (unsigned)fCount);
254 return fArray[index];
255 }
256
257 void swap(SkAutoTArray& other) {
258 SkTSwap(fArray, other.fArray);
259 SkDEBUGCODE(SkTSwap(fCount, other.fCount));
260 }
261
262 private:
263 T* fArray;
264 SkDEBUGCODE(int fCount;)
265 };
266
267 /** Wraps SkAutoTArray, with room for up to N elements preallocated
268 */
269 template <int N, typename T> class SkAutoSTArray : SkNoncopyable {
270 public:
271 /** Initialize with no objects */
272 SkAutoSTArray() {
273 fArray = NULL;
274 fCount = 0;
275 }
276
277 /** Allocate count number of T elements
278 */
279 SkAutoSTArray(int count) {
280 fArray = NULL;
281 fCount = 0;
282 this->reset(count);
283 }
284
285 ~SkAutoSTArray() {
286 this->reset(0);
287 }
288
289 /** Destroys previous objects in the array and default constructs count number of objects */
290 void reset(int count) {
291 T* start = fArray;
292 T* iter = start + fCount;
293 while (iter > start) {
294 (--iter)->~T();
295 }
296
297 if (fCount != count) {
298 if (fCount > N) {
299 // 'fArray' was allocated last time so free it now
300 SkASSERT((T*) fStorage != fArray);
301 sk_free(fArray);
302 }
303
304 if (count > N) {
305 const uint64_t size64 = sk_64_mul(count, sizeof(T));
306 const size_t size = static_cast<size_t>(size64);
307 if (size != size64) {
308 sk_out_of_memory();
309 }
310 fArray = (T*) sk_malloc_throw(size);
311 } else if (count > 0) {
312 fArray = (T*) fStorage;
313 } else {
314 fArray = NULL;
315 }
316
317 fCount = count;
318 }
319
320 iter = fArray;
321 T* stop = fArray + count;
322 while (iter < stop) {
323 SkNEW_PLACEMENT(iter++, T);
324 }
325 }
326
327 /** Return the number of T elements in the array
328 */
329 int count() const { return fCount; }
330
331 /** Return the array of T elements. Will be NULL if count == 0
332 */
333 T* get() const { return fArray; }
334
335 /** Return the nth element in the array
336 */
337 T& operator[](int index) const {
338 SkASSERT(index < fCount);
339 return fArray[index];
340 }
341
342 private:
343 int fCount;
344 T* fArray;
345 // since we come right after fArray, fStorage should be properly aligned
346 char fStorage[N * sizeof(T)];
347 };
348
349 /** Manages an array of T elements, freeing the array in the destructor.
350 * Does NOT call any constructors/destructors on T (T must be POD).
351 */
352 template <typename T> class SkAutoTMalloc : SkNoncopyable {
353 public:
354 /** Takes ownership of the ptr. The ptr must be a value which can be passed to sk_free. */
355 explicit SkAutoTMalloc(T* ptr = NULL) {
356 fPtr = ptr;
357 }
358
359 /** Allocates space for 'count' Ts. */
360 explicit SkAutoTMalloc(size_t count) {
361 fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW);
362 }
363
364 ~SkAutoTMalloc() {
365 sk_free(fPtr);
366 }
367
368 /** Resize the memory area pointed to by the current ptr preserving contents. */
369 void realloc(size_t count) {
370 fPtr = reinterpret_cast<T*>(sk_realloc_throw(fPtr, count * sizeof(T)));
371 }
372
373 /** Resize the memory area pointed to by the current ptr without preserving contents. */
374 void reset(size_t count) {
375 sk_free(fPtr);
376 fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW);
377 }
378
379 T* get() const { return fPtr; }
380
381 operator T*() {
382 return fPtr;
383 }
384
385 operator const T*() const {
386 return fPtr;
387 }
388
389 T& operator[](int index) {
390 return fPtr[index];
391 }
392
393 const T& operator[](int index) const {
394 return fPtr[index];
395 }
396
397 /**
398 * Transfer ownership of the ptr to the caller, setting the internal
399 * pointer to NULL. Note that this differs from get(), which also returns
400 * the pointer, but it does not transfer ownership.
401 */
402 T* detach() {
403 T* ptr = fPtr;
404 fPtr = NULL;
405 return ptr;
406 }
407
408 private:
409 T* fPtr;
410 };
411
412 template <size_t N, typename T> class SkAutoSTMalloc : SkNoncopyable {
413 public:
414 SkAutoSTMalloc() : fPtr(fTStorage) {}
415
416 SkAutoSTMalloc(size_t count) {
417 if (count > N) {
418 fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP);
419 } else {
420 fPtr = fTStorage;
421 }
422 }
423
424 ~SkAutoSTMalloc() {
425 if (fPtr != fTStorage) {
426 sk_free(fPtr);
427 }
428 }
429
430 // doesn't preserve contents
431 T* reset(size_t count) {
432 if (fPtr != fTStorage) {
433 sk_free(fPtr);
434 }
435 if (count > N) {
436 fPtr = (T*)sk_malloc_throw(count * sizeof(T));
437 } else {
438 fPtr = fTStorage;
439 }
440 return fPtr;
441 }
442
443 T* get() const { return fPtr; }
444
445 operator T*() {
446 return fPtr;
447 }
448
449 operator const T*() const {
450 return fPtr;
451 }
452
453 T& operator[](int index) {
454 return fPtr[index];
455 }
456
457 const T& operator[](int index) const {
458 return fPtr[index];
459 }
460
461 // Reallocs the array, can be used to shrink the allocation. Makes no attempt to be intelligent
462 void realloc(size_t count) {
463 if (count > N) {
464 if (fPtr == fTStorage) {
465 fPtr = (T*)sk_malloc_throw(count * sizeof(T));
466 memcpy(fPtr, fTStorage, N * sizeof(T));
467 } else {
468 fPtr = (T*)sk_realloc_throw(fPtr, count * sizeof(T));
469 }
470 } else if (fPtr != fTStorage) {
471 fPtr = (T*)sk_realloc_throw(fPtr, count * sizeof(T));
472 }
473 }
474
475 private:
476 T* fPtr;
477 union {
478 uint32_t fStorage32[(N*sizeof(T) + 3) >> 2];
479 T fTStorage[1]; // do NOT want to invoke T::T()
480 };
481 };
482
483 /**
484 * Reserves memory that is aligned on double and pointer boundaries.
485 * Hopefully this is sufficient for all practical purposes.
486 */
487 template <size_t N> class SkAlignedSStorage : SkNoncopyable {
488 public:
489 void* get() { return fData; }
490 const void* get() const { return fData; }
491 private:
492 union {
493 void* fPtr;
494 double fDouble;
495 char fData[N];
496 };
497 };
498
499 /**
500 * Reserves memory that is aligned on double and pointer boundaries.
501 * Hopefully this is sufficient for all practical purposes. Otherwise,
502 * we have to do some arcane trickery to determine alignment of non-POD
503 * types. Lifetime of the memory is the lifetime of the object.
504 */
505 template <int N, typename T> class SkAlignedSTStorage : SkNoncopyable {
506 public:
507 /**
508 * Returns void* because this object does not initialize the
509 * memory. Use placement new for types that require a cons.
510 */
511 void* get() { return fStorage.get(); }
512 private:
513 SkAlignedSStorage<sizeof(T)*N> fStorage;
514 };
515
516 #endif
517