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