Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright 2016 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #ifndef SkAutoMalloc_DEFINED
      9 #define SkAutoMalloc_DEFINED
     10 
     11 #include "SkTypes.h"
     12 #include "SkMalloc.h"
     13 
     14 #include <memory>
     15 
     16 /**
     17  *  Manage an allocated block of heap memory. This object is the sole manager of
     18  *  the lifetime of the block, so the caller must not call sk_free() or delete
     19  *  on the block, unless release() was called.
     20  */
     21 class SkAutoMalloc : SkNoncopyable {
     22 public:
     23     explicit SkAutoMalloc(size_t size = 0)
     24         : fPtr(size ? sk_malloc_throw(size) : nullptr), fSize(size) {}
     25 
     26     /**
     27      *  Passed to reset to specify what happens if the requested size is smaller
     28      *  than the current size (and the current block was dynamically allocated).
     29      */
     30     enum OnShrink {
     31         /**
     32          *  If the requested size is smaller than the current size, and the
     33          *  current block is dynamically allocated, free the old block and
     34          *  malloc a new block of the smaller size.
     35          */
     36         kAlloc_OnShrink,
     37 
     38         /**
     39          *  If the requested size is smaller than the current size, and the
     40          *  current block is dynamically allocated, just return the old
     41          *  block.
     42          */
     43         kReuse_OnShrink
     44     };
     45 
     46     /**
     47      *  Reallocates the block to a new size. The ptr may or may not change.
     48      */
     49     void* reset(size_t size = 0, OnShrink shrink = kAlloc_OnShrink) {
     50         if (size != fSize && (size > fSize || kReuse_OnShrink != shrink)) {
     51             fPtr.reset(size ? sk_malloc_throw(size) : nullptr);
     52             fSize = size;
     53         }
     54         return fPtr.get();
     55     }
     56 
     57     /**
     58      *  Return the allocated block.
     59      */
     60     void* get() { return fPtr.get(); }
     61     const void* get() const { return fPtr.get(); }
     62 
     63    /** Transfer ownership of the current ptr to the caller, setting the
     64        internal reference to null. Note the caller is reponsible for calling
     65        sk_free on the returned address.
     66     */
     67     void* release() {
     68         fSize = 0;
     69         return fPtr.release();
     70     }
     71 
     72 private:
     73     struct WrapFree {
     74         void operator()(void* p) { sk_free(p); }
     75     };
     76     std::unique_ptr<void, WrapFree> fPtr;
     77     size_t fSize;  // can be larger than the requested size (see kReuse)
     78 };
     79 #define SkAutoMalloc(...) SK_REQUIRE_LOCAL_VAR(SkAutoMalloc)
     80 
     81 /**
     82  *  Manage an allocated block of memory. If the requested size is <= kSizeRequested (or slightly
     83  *  more), then the allocation will come from the stack rather than the heap. This object is the
     84  *  sole manager of the lifetime of the block, so the caller must not call sk_free() or delete on
     85  *  the block.
     86  */
     87 template <size_t kSizeRequested> class SkAutoSMalloc : SkNoncopyable {
     88 public:
     89     /**
     90      *  Creates initially empty storage. get() returns a ptr, but it is to a zero-byte allocation.
     91      *  Must call reset(size) to return an allocated block.
     92      */
     93     SkAutoSMalloc() {
     94         fPtr = fStorage;
     95         fSize = kSize;
     96     }
     97 
     98     /**
     99      *  Allocate a block of the specified size. If size <= kSizeRequested (or slightly more), then
    100      *  the allocation will come from the stack, otherwise it will be dynamically allocated.
    101      */
    102     explicit SkAutoSMalloc(size_t size) {
    103         fPtr = fStorage;
    104         fSize = kSize;
    105         this->reset(size);
    106     }
    107 
    108     /**
    109      *  Free the allocated block (if any). If the block was small enough to have been allocated on
    110      *  the stack, then this does nothing.
    111      */
    112     ~SkAutoSMalloc() {
    113         if (fPtr != (void*)fStorage) {
    114             sk_free(fPtr);
    115         }
    116     }
    117 
    118     /**
    119      *  Return the allocated block. May return non-null even if the block is of zero size. Since
    120      *  this may be on the stack or dynamically allocated, the caller must not call sk_free() on it,
    121      *  but must rely on SkAutoSMalloc to manage it.
    122      */
    123     void* get() const { return fPtr; }
    124 
    125     /**
    126      *  Return a new block of the requested size, freeing (as necessary) any previously allocated
    127      *  block. As with the constructor, if size <= kSizeRequested (or slightly more) then the return
    128      *  block may be allocated locally, rather than from the heap.
    129      */
    130     void* reset(size_t size,
    131                 SkAutoMalloc::OnShrink shrink = SkAutoMalloc::kAlloc_OnShrink,
    132                 bool* didChangeAlloc = nullptr) {
    133         size = (size < kSize) ? kSize : size;
    134         bool alloc = size != fSize && (SkAutoMalloc::kAlloc_OnShrink == shrink || size > fSize);
    135         if (didChangeAlloc) {
    136             *didChangeAlloc = alloc;
    137         }
    138         if (alloc) {
    139             if (fPtr != (void*)fStorage) {
    140                 sk_free(fPtr);
    141             }
    142 
    143             if (size == kSize) {
    144                 SkASSERT(fPtr != fStorage); // otherwise we lied when setting didChangeAlloc.
    145                 fPtr = fStorage;
    146             } else {
    147                 fPtr = sk_malloc_throw(size);
    148             }
    149 
    150             fSize = size;
    151         }
    152         SkASSERT(fSize >= size && fSize >= kSize);
    153         SkASSERT((fPtr == fStorage) || fSize > kSize);
    154         return fPtr;
    155     }
    156 
    157 private:
    158     // Align up to 32 bits.
    159     static const size_t kSizeAlign4 = SkAlign4(kSizeRequested);
    160 #if defined(SK_BUILD_FOR_GOOGLE3)
    161     // Stack frame size is limited for SK_BUILD_FOR_GOOGLE3. 4k is less than the actual max, but some functions
    162     // have multiple large stack allocations.
    163     static const size_t kMaxBytes = 4 * 1024;
    164     static const size_t kSize = kSizeRequested > kMaxBytes ? kMaxBytes : kSizeAlign4;
    165 #else
    166     static const size_t kSize = kSizeAlign4;
    167 #endif
    168 
    169     void*       fPtr;
    170     size_t      fSize;  // can be larger than the requested size (see kReuse)
    171     uint32_t    fStorage[kSize >> 2];
    172 };
    173 // Can't guard the constructor because it's a template class.
    174 
    175 #endif
    176