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