1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/core/SkSmallAllocator.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,149 @@ 1.4 +/* 1.5 + * Copyright 2014 Google, Inc 1.6 + * 1.7 + * Use of this source code is governed by a BSD-style license that can be 1.8 + * found in the LICENSE file. 1.9 + */ 1.10 + 1.11 +#ifndef SkSmallAllocator_DEFINED 1.12 +#define SkSmallAllocator_DEFINED 1.13 + 1.14 +#include "SkTDArray.h" 1.15 +#include "SkTypes.h" 1.16 + 1.17 +// Used by SkSmallAllocator to call the destructor for objects it has 1.18 +// allocated. 1.19 +template<typename T> void destroyT(void* ptr) { 1.20 + static_cast<T*>(ptr)->~T(); 1.21 +} 1.22 + 1.23 +/* 1.24 + * Template class for allocating small objects without additional heap memory 1.25 + * allocations. kMaxObjects is a hard limit on the number of objects that can 1.26 + * be allocated using this class. After that, attempts to create more objects 1.27 + * with this class will assert and return NULL. 1.28 + * kTotalBytes is the total number of bytes provided for storage for all 1.29 + * objects created by this allocator. If an object to be created is larger 1.30 + * than the storage (minus storage already used), it will be allocated on the 1.31 + * heap. This class's destructor will handle calling the destructor for each 1.32 + * object it allocated and freeing its memory. 1.33 + */ 1.34 +template<uint32_t kMaxObjects, size_t kTotalBytes> 1.35 +class SkSmallAllocator : public SkNoncopyable { 1.36 +public: 1.37 + SkSmallAllocator() 1.38 + : fStorageUsed(0) 1.39 + , fNumObjects(0) 1.40 + {} 1.41 + 1.42 + ~SkSmallAllocator() { 1.43 + // Destruct in reverse order, in case an earlier object points to a 1.44 + // later object. 1.45 + while (fNumObjects > 0) { 1.46 + fNumObjects--; 1.47 + Rec* rec = &fRecs[fNumObjects]; 1.48 + rec->fKillProc(rec->fObj); 1.49 + // Safe to do if fObj is in fStorage, since fHeapStorage will 1.50 + // point to NULL. 1.51 + sk_free(rec->fHeapStorage); 1.52 + } 1.53 + } 1.54 + 1.55 + /* 1.56 + * Create a new object of type T. Its lifetime will be handled by this 1.57 + * SkSmallAllocator. 1.58 + * Each version behaves the same but takes a different number of 1.59 + * arguments. 1.60 + * Note: If kMaxObjects have been created by this SkSmallAllocator, NULL 1.61 + * will be returned. 1.62 + */ 1.63 + template<typename T> 1.64 + T* createT() { 1.65 + void* buf = this->reserveT<T>(); 1.66 + if (NULL == buf) { 1.67 + return NULL; 1.68 + } 1.69 + SkNEW_PLACEMENT(buf, T); 1.70 + return static_cast<T*>(buf); 1.71 + } 1.72 + 1.73 + template<typename T, typename A1> T* createT(const A1& a1) { 1.74 + void* buf = this->reserveT<T>(); 1.75 + if (NULL == buf) { 1.76 + return NULL; 1.77 + } 1.78 + SkNEW_PLACEMENT_ARGS(buf, T, (a1)); 1.79 + return static_cast<T*>(buf); 1.80 + } 1.81 + 1.82 + template<typename T, typename A1, typename A2> 1.83 + T* createT(const A1& a1, const A2& a2) { 1.84 + void* buf = this->reserveT<T>(); 1.85 + if (NULL == buf) { 1.86 + return NULL; 1.87 + } 1.88 + SkNEW_PLACEMENT_ARGS(buf, T, (a1, a2)); 1.89 + return static_cast<T*>(buf); 1.90 + } 1.91 + 1.92 + template<typename T, typename A1, typename A2, typename A3> 1.93 + T* createT(const A1& a1, const A2& a2, const A3& a3) { 1.94 + void* buf = this->reserveT<T>(); 1.95 + if (NULL == buf) { 1.96 + return NULL; 1.97 + } 1.98 + SkNEW_PLACEMENT_ARGS(buf, T, (a1, a2, a3)); 1.99 + return static_cast<T*>(buf); 1.100 + } 1.101 + 1.102 + /* 1.103 + * Reserve a specified amount of space (must be enough space for one T). 1.104 + * The space will be in fStorage if there is room, or on the heap otherwise. 1.105 + * Either way, this class will call ~T() in its destructor and free the heap 1.106 + * allocation if necessary. 1.107 + * Unlike createT(), this method will not call the constructor of T. 1.108 + */ 1.109 + template<typename T> void* reserveT(size_t storageRequired = sizeof(T)) { 1.110 + SkASSERT(fNumObjects < kMaxObjects); 1.111 + SkASSERT(storageRequired >= sizeof(T)); 1.112 + if (kMaxObjects == fNumObjects) { 1.113 + return NULL; 1.114 + } 1.115 + const size_t storageRemaining = SkAlign4(kTotalBytes) - fStorageUsed; 1.116 + storageRequired = SkAlign4(storageRequired); 1.117 + Rec* rec = &fRecs[fNumObjects]; 1.118 + if (storageRequired > storageRemaining) { 1.119 + // Allocate on the heap. Ideally we want to avoid this situation, 1.120 + // but we're not sure we can catch all callers, so handle it but 1.121 + // assert false in debug mode. 1.122 + SkASSERT(false); 1.123 + rec->fHeapStorage = sk_malloc_throw(storageRequired); 1.124 + rec->fObj = static_cast<void*>(rec->fHeapStorage); 1.125 + } else { 1.126 + // There is space in fStorage. 1.127 + rec->fHeapStorage = NULL; 1.128 + SkASSERT(SkIsAlign4(fStorageUsed)); 1.129 + rec->fObj = static_cast<void*>(fStorage + (fStorageUsed / 4)); 1.130 + fStorageUsed += storageRequired; 1.131 + } 1.132 + rec->fKillProc = destroyT<T>; 1.133 + fNumObjects++; 1.134 + return rec->fObj; 1.135 + } 1.136 + 1.137 +private: 1.138 + struct Rec { 1.139 + void* fObj; 1.140 + void* fHeapStorage; 1.141 + void (*fKillProc)(void*); 1.142 + }; 1.143 + 1.144 + // Number of bytes used so far. 1.145 + size_t fStorageUsed; 1.146 + // Pad the storage size to be 4-byte aligned. 1.147 + uint32_t fStorage[SkAlign4(kTotalBytes) >> 2]; 1.148 + uint32_t fNumObjects; 1.149 + Rec fRecs[kMaxObjects]; 1.150 +}; 1.151 + 1.152 +#endif // SkSmallAllocator_DEFINED