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