SkSmallAllocator.h revision 001f4ed2fb62ecdc98ce2884d925de11b7516d23
1/* 2 * Copyright 2014 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 SkSmallAllocator_DEFINED 9#define SkSmallAllocator_DEFINED 10 11#include "SkTDArray.h" 12#include "SkTypes.h" 13 14// Used by SkSmallAllocator to call the destructor for objects it has 15// allocated. 16template<typename T> void destroyT(void* ptr) { 17 static_cast<T*>(ptr)->~T(); 18} 19 20/* 21 * Template class for allocating small objects without additional heap memory 22 * allocations. kMaxObjects is a hard limit on the number of objects that can 23 * be allocated using this class. After that, attempts to create more objects 24 * with this class will assert and return NULL. 25 * kTotalBytes is the total number of bytes provided for storage for all 26 * objects created by this allocator. If an object to be created is larger 27 * than the storage (minus storage already used), it will be allocated on the 28 * heap. This class's destructor will handle calling the destructor for each 29 * object it allocated and freeing its memory. 30 */ 31template<uint32_t kMaxObjects, size_t kTotalBytes> 32class SkSmallAllocator : SkNoncopyable { 33public: 34 SkSmallAllocator() 35 : fStorageUsed(0) 36 , fNumObjects(0) 37 {} 38 39 ~SkSmallAllocator() { 40 // Destruct in reverse order, in case an earlier object points to a 41 // later object. 42 while (fNumObjects > 0) { 43 fNumObjects--; 44 Rec* rec = &fRecs[fNumObjects]; 45 rec->fKillProc(rec->fObj); 46 // Safe to do if fObj is in fStorage, since fHeapStorage will 47 // point to NULL. 48 sk_free(rec->fHeapStorage); 49 } 50 } 51 52 /* 53 * Create a new object of type T. Its lifetime will be handled by this 54 * SkSmallAllocator. 55 * Each version behaves the same but takes a different number of 56 * arguments. 57 * Note: If kMaxObjects have been created by this SkSmallAllocator, NULL 58 * will be returned. 59 */ 60 template<typename T> 61 T* createT() { 62 void* buf = this->reserveT<T>(); 63 if (NULL == buf) { 64 return NULL; 65 } 66 SkNEW_PLACEMENT(buf, T); 67 return static_cast<T*>(buf); 68 } 69 70 template<typename T, typename A1> T* createT(const A1& a1) { 71 void* buf = this->reserveT<T>(); 72 if (NULL == buf) { 73 return NULL; 74 } 75 SkNEW_PLACEMENT_ARGS(buf, T, (a1)); 76 return static_cast<T*>(buf); 77 } 78 79 template<typename T, typename A1, typename A2> 80 T* createT(const A1& a1, const A2& a2) { 81 void* buf = this->reserveT<T>(); 82 if (NULL == buf) { 83 return NULL; 84 } 85 SkNEW_PLACEMENT_ARGS(buf, T, (a1, a2)); 86 return static_cast<T*>(buf); 87 } 88 89 template<typename T, typename A1, typename A2, typename A3> 90 T* createT(const A1& a1, const A2& a2, const A3& a3) { 91 void* buf = this->reserveT<T>(); 92 if (NULL == buf) { 93 return NULL; 94 } 95 SkNEW_PLACEMENT_ARGS(buf, T, (a1, a2, a3)); 96 return static_cast<T*>(buf); 97 } 98 99 /* 100 * Reserve a specified amount of space (must be enough space for one T). 101 * The space will be in fStorage if there is room, or on the heap otherwise. 102 * Either way, this class will call ~T() in its destructor and free the heap 103 * allocation if necessary. 104 * Unlike createT(), this method will not call the constructor of T. 105 */ 106 template<typename T> void* reserveT(size_t storageRequired = sizeof(T)) { 107 SkASSERT(fNumObjects < kMaxObjects); 108 SkASSERT(storageRequired >= sizeof(T)); 109 if (kMaxObjects == fNumObjects) { 110 return NULL; 111 } 112 const size_t storageRemaining = SkAlign4(kTotalBytes) - fStorageUsed; 113 storageRequired = SkAlign4(storageRequired); 114 Rec* rec = &fRecs[fNumObjects]; 115 if (storageRequired > storageRemaining) { 116 // Allocate on the heap. Ideally we want to avoid this situation, 117 // but we're not sure we can catch all callers, so handle it but 118 // assert false in debug mode. 119 SkASSERT(false); 120 rec->fStorageSize = 0; 121 rec->fHeapStorage = sk_malloc_throw(storageRequired); 122 rec->fObj = static_cast<void*>(rec->fHeapStorage); 123 } else { 124 // There is space in fStorage. 125 rec->fStorageSize = storageRequired; 126 rec->fHeapStorage = NULL; 127 SkASSERT(SkIsAlign4(fStorageUsed)); 128 rec->fObj = static_cast<void*>(fStorage + (fStorageUsed / 4)); 129 fStorageUsed += storageRequired; 130 } 131 rec->fKillProc = destroyT<T>; 132 fNumObjects++; 133 return rec->fObj; 134 } 135 136 /* 137 * Free the memory reserved last without calling the destructor. 138 * Can be used in a nested way, i.e. after reserving A and B, calling 139 * freeLast once will free B and calling it again will free A. 140 */ 141 void freeLast() { 142 SkASSERT(fNumObjects > 0); 143 Rec* rec = &fRecs[fNumObjects - 1]; 144 sk_free(rec->fHeapStorage); 145 fStorageUsed -= rec->fStorageSize; 146 147 fNumObjects--; 148 } 149 150private: 151 struct Rec { 152 size_t fStorageSize; // 0 if allocated on heap 153 void* fObj; 154 void* fHeapStorage; 155 void (*fKillProc)(void*); 156 }; 157 158 // Number of bytes used so far. 159 size_t fStorageUsed; 160 // Pad the storage size to be 4-byte aligned. 161 uint32_t fStorage[SkAlign4(kTotalBytes) >> 2]; 162 uint32_t fNumObjects; 163 Rec fRecs[kMaxObjects]; 164}; 165 166#endif // SkSmallAllocator_DEFINED 167