/* * Copyright 2014 Google, Inc * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkSmallAllocator_DEFINED #define SkSmallAllocator_DEFINED #include "SkTArray.h" #include "SkTypes.h" #include #include #include // max_align_t is needed to calculate the alignment for createWithIniterT when the T used is an // abstract type. The complication with max_align_t is that it is defined differently for // different builds. namespace { #if defined(SK_BUILD_FOR_WIN32) || defined(SK_BUILD_FOR_MAC) // Use std::max_align_t for compiles that follow the standard. #include using SystemAlignment = std::max_align_t; #else // Ubuntu compiles don't have std::max_align_t defined, but MSVC does not define max_align_t. #include using SystemAlignment = max_align_t; #endif } /* * Template class for allocating small objects without additional heap memory * allocations. * * kTotalBytes is the total number of bytes provided for storage for all * objects created by this allocator. If an object to be created is larger * than the storage (minus storage already used), it will be allocated on the * heap. This class's destructor will handle calling the destructor for each * object it allocated and freeing its memory. */ template class SkSmallAllocator : SkNoncopyable { public: ~SkSmallAllocator() { // Destruct in reverse order, in case an earlier object points to a // later object. while (fRecs.count() > 0) { this->deleteLast(); } } /* * Create a new object of type T. Its lifetime will be handled by this * SkSmallAllocator. */ template T* createT(Args&&... args) { void* buf = this->reserve(sizeof(T), DefaultDestructor); return new (buf) T(std::forward(args)...); } /* * Create a new object of size using initer to initialize the memory. The initer function has * the signature T* initer(void* storage). If initer is unable to initialize the memory it * should return nullptr where SkSmallAllocator will free the memory. */ template auto createWithIniter(size_t size, Initer initer) -> decltype(initer(nullptr)) { using ObjType = typename std::remove_pointer::type; SkASSERT(size >= sizeof(ObjType)); void* storage = this->reserve(size, DefaultDestructor); auto candidate = initer(storage); if (!candidate) { // Initializing didn't workout so free the memory. this->freeLast(); } return candidate; } /* * Free the last object allocated and call its destructor. This can be called multiple times * removing objects from the pool in reverse order. */ void deleteLast() { SkASSERT(fRecs.count() > 0); Rec& rec = fRecs.back(); rec.fDestructor(rec.fObj); this->freeLast(); } private: using Destructor = void(*)(void*); struct Rec { char* fObj; Destructor fDestructor; }; // Used to call the destructor for allocated objects. template static void DefaultDestructor(void* ptr) { static_cast(ptr)->~T(); } static constexpr size_t kAlignment = alignof(SystemAlignment); static constexpr size_t AlignSize(size_t size) { return (size + kAlignment - 1) & ~(kAlignment - 1); } // Reserve storageRequired from fStorage if possible otherwise allocate on the heap. void* reserve(size_t storageRequired, Destructor destructor) { // Make sure that all allocations stay aligned by rounding the storageRequired up to the // aligned value. char* objectStart = fStorageEnd; char* objectEnd = objectStart + AlignSize(storageRequired); Rec& rec = fRecs.push_back(); if (objectEnd > &fStorage[kTotalBytes]) { // Allocate on the heap. Ideally we want to avoid this situation. rec.fObj = new char [storageRequired]; } else { // There is space in fStorage. rec.fObj = objectStart; fStorageEnd = objectEnd; } rec.fDestructor = destructor; return rec.fObj; } void freeLast() { Rec& rec = fRecs.back(); if (std::less()(rec.fObj, fStorage) || !std::less()(rec.fObj, &fStorage[kTotalBytes])) { delete [] rec.fObj; } else { fStorageEnd = rec.fObj; } fRecs.pop_back(); } SkSTArray fRecs; char* fStorageEnd {fStorage}; // Since char have an alignment of 1, it should be forced onto an alignment the compiler // expects which is the alignment of std::max_align_t. alignas (kAlignment) char fStorage[kTotalBytes]; }; #endif // SkSmallAllocator_DEFINED