michael@0: /* michael@0: * Copyright 2014 Google, Inc michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #ifndef SkSmallAllocator_DEFINED michael@0: #define SkSmallAllocator_DEFINED michael@0: michael@0: #include "SkTDArray.h" michael@0: #include "SkTypes.h" michael@0: michael@0: // Used by SkSmallAllocator to call the destructor for objects it has michael@0: // allocated. michael@0: template void destroyT(void* ptr) { michael@0: static_cast(ptr)->~T(); michael@0: } michael@0: michael@0: /* michael@0: * Template class for allocating small objects without additional heap memory michael@0: * allocations. kMaxObjects is a hard limit on the number of objects that can michael@0: * be allocated using this class. After that, attempts to create more objects michael@0: * with this class will assert and return NULL. michael@0: * kTotalBytes is the total number of bytes provided for storage for all michael@0: * objects created by this allocator. If an object to be created is larger michael@0: * than the storage (minus storage already used), it will be allocated on the michael@0: * heap. This class's destructor will handle calling the destructor for each michael@0: * object it allocated and freeing its memory. michael@0: */ michael@0: template michael@0: class SkSmallAllocator : public SkNoncopyable { michael@0: public: michael@0: SkSmallAllocator() michael@0: : fStorageUsed(0) michael@0: , fNumObjects(0) michael@0: {} michael@0: michael@0: ~SkSmallAllocator() { michael@0: // Destruct in reverse order, in case an earlier object points to a michael@0: // later object. michael@0: while (fNumObjects > 0) { michael@0: fNumObjects--; michael@0: Rec* rec = &fRecs[fNumObjects]; michael@0: rec->fKillProc(rec->fObj); michael@0: // Safe to do if fObj is in fStorage, since fHeapStorage will michael@0: // point to NULL. michael@0: sk_free(rec->fHeapStorage); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Create a new object of type T. Its lifetime will be handled by this michael@0: * SkSmallAllocator. michael@0: * Each version behaves the same but takes a different number of michael@0: * arguments. michael@0: * Note: If kMaxObjects have been created by this SkSmallAllocator, NULL michael@0: * will be returned. michael@0: */ michael@0: template michael@0: T* createT() { michael@0: void* buf = this->reserveT(); michael@0: if (NULL == buf) { michael@0: return NULL; michael@0: } michael@0: SkNEW_PLACEMENT(buf, T); michael@0: return static_cast(buf); michael@0: } michael@0: michael@0: template T* createT(const A1& a1) { michael@0: void* buf = this->reserveT(); michael@0: if (NULL == buf) { michael@0: return NULL; michael@0: } michael@0: SkNEW_PLACEMENT_ARGS(buf, T, (a1)); michael@0: return static_cast(buf); michael@0: } michael@0: michael@0: template michael@0: T* createT(const A1& a1, const A2& a2) { michael@0: void* buf = this->reserveT(); michael@0: if (NULL == buf) { michael@0: return NULL; michael@0: } michael@0: SkNEW_PLACEMENT_ARGS(buf, T, (a1, a2)); michael@0: return static_cast(buf); michael@0: } michael@0: michael@0: template michael@0: T* createT(const A1& a1, const A2& a2, const A3& a3) { michael@0: void* buf = this->reserveT(); michael@0: if (NULL == buf) { michael@0: return NULL; michael@0: } michael@0: SkNEW_PLACEMENT_ARGS(buf, T, (a1, a2, a3)); michael@0: return static_cast(buf); michael@0: } michael@0: michael@0: /* michael@0: * Reserve a specified amount of space (must be enough space for one T). michael@0: * The space will be in fStorage if there is room, or on the heap otherwise. michael@0: * Either way, this class will call ~T() in its destructor and free the heap michael@0: * allocation if necessary. michael@0: * Unlike createT(), this method will not call the constructor of T. michael@0: */ michael@0: template void* reserveT(size_t storageRequired = sizeof(T)) { michael@0: SkASSERT(fNumObjects < kMaxObjects); michael@0: SkASSERT(storageRequired >= sizeof(T)); michael@0: if (kMaxObjects == fNumObjects) { michael@0: return NULL; michael@0: } michael@0: const size_t storageRemaining = SkAlign4(kTotalBytes) - fStorageUsed; michael@0: storageRequired = SkAlign4(storageRequired); michael@0: Rec* rec = &fRecs[fNumObjects]; michael@0: if (storageRequired > storageRemaining) { michael@0: // Allocate on the heap. Ideally we want to avoid this situation, michael@0: // but we're not sure we can catch all callers, so handle it but michael@0: // assert false in debug mode. michael@0: SkASSERT(false); michael@0: rec->fHeapStorage = sk_malloc_throw(storageRequired); michael@0: rec->fObj = static_cast(rec->fHeapStorage); michael@0: } else { michael@0: // There is space in fStorage. michael@0: rec->fHeapStorage = NULL; michael@0: SkASSERT(SkIsAlign4(fStorageUsed)); michael@0: rec->fObj = static_cast(fStorage + (fStorageUsed / 4)); michael@0: fStorageUsed += storageRequired; michael@0: } michael@0: rec->fKillProc = destroyT; michael@0: fNumObjects++; michael@0: return rec->fObj; michael@0: } michael@0: michael@0: private: michael@0: struct Rec { michael@0: void* fObj; michael@0: void* fHeapStorage; michael@0: void (*fKillProc)(void*); michael@0: }; michael@0: michael@0: // Number of bytes used so far. michael@0: size_t fStorageUsed; michael@0: // Pad the storage size to be 4-byte aligned. michael@0: uint32_t fStorage[SkAlign4(kTotalBytes) >> 2]; michael@0: uint32_t fNumObjects; michael@0: Rec fRecs[kMaxObjects]; michael@0: }; michael@0: michael@0: #endif // SkSmallAllocator_DEFINED