| Index: src/core/SkSmallAllocator.h
 | 
| diff --git a/src/core/SkSmallAllocator.h b/src/core/SkSmallAllocator.h
 | 
| index 13b1505821ab588f99a8a28fe21fc0ddd9a16cb9..efe0bc7b4026de198d3b3c258188e482f2395bcd 100644
 | 
| --- a/src/core/SkSmallAllocator.h
 | 
| +++ b/src/core/SkSmallAllocator.h
 | 
| @@ -8,132 +8,145 @@
 | 
|  #ifndef SkSmallAllocator_DEFINED
 | 
|  #define SkSmallAllocator_DEFINED
 | 
|  
 | 
| -#include "SkTDArray.h"
 | 
| +#include "SkTArray.h"
 | 
|  #include "SkTypes.h"
 | 
|  
 | 
| -#include <new>
 | 
| +#include <functional>
 | 
| +#include <type_traits>
 | 
|  #include <utility>
 | 
|  
 | 
| +
 | 
| +// 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 <cstddef>
 | 
| +    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 <stddef.h>
 | 
| +    using SystemAlignment = max_align_t;
 | 
| +#endif
 | 
| +}
 | 
| +
 | 
|  /*
 | 
|   *  Template class for allocating small objects without additional heap memory
 | 
| - *  allocations. kMaxObjects is a hard limit on the number of objects that can
 | 
| - *  be allocated using this class. After that, attempts to create more objects
 | 
| - *  with this class will assert and return nullptr.
 | 
| + *  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.
 | 
| - *
 | 
| - *  Current the class always aligns each allocation to 16-bytes to be safe, but future
 | 
| - *  may reduce this to only the alignment that is required per alloc.
 | 
|   */
 | 
| -template<uint32_t kMaxObjects, size_t kTotalBytes>
 | 
| +template<uint32_t kExpectedObjects, size_t kTotalBytes>
 | 
|  class SkSmallAllocator : SkNoncopyable {
 | 
|  public:
 | 
| -    SkSmallAllocator()
 | 
| -    : fStorageUsed(0)
 | 
| -    , fNumObjects(0)
 | 
| -    {}
 | 
| -
 | 
|      ~SkSmallAllocator() {
 | 
|          // Destruct in reverse order, in case an earlier object points to a
 | 
|          // later object.
 | 
| -        while (fNumObjects > 0) {
 | 
| -            fNumObjects--;
 | 
| -            Rec* rec = &fRecs[fNumObjects];
 | 
| -            rec->fKillProc(rec->fObj);
 | 
| -            // Safe to do if fObj is in fStorage, since fHeapStorage will
 | 
| -            // point to nullptr.
 | 
| -            sk_free(rec->fHeapStorage);
 | 
| +        while (fRecs.count() > 0) {
 | 
| +            this->deleteLast();
 | 
|          }
 | 
|      }
 | 
|  
 | 
|      /*
 | 
|       *  Create a new object of type T. Its lifetime will be handled by this
 | 
|       *  SkSmallAllocator.
 | 
| -     *  Note: If kMaxObjects have been created by this SkSmallAllocator, nullptr
 | 
| -     *  will be returned.
 | 
|       */
 | 
|      template<typename T, typename... Args>
 | 
|      T* createT(Args&&... args) {
 | 
| -        void* buf = this->reserveT<T>();
 | 
| -        if (nullptr == buf) {
 | 
| -            return nullptr;
 | 
| -        }
 | 
| +        void* buf = this->reserve(sizeof(T), DefaultDestructor<T>);
 | 
|          return new (buf) T(std::forward<Args>(args)...);
 | 
|      }
 | 
|  
 | 
|      /*
 | 
| -     *  Reserve a specified amount of space (must be enough space for one T).
 | 
| -     *  The space will be in fStorage if there is room, or on the heap otherwise.
 | 
| -     *  Either way, this class will call ~T() in its destructor and free the heap
 | 
| -     *  allocation if necessary.
 | 
| -     *  Unlike createT(), this method will not call the constructor of T.
 | 
| +     * 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<typename T> void* reserveT(size_t storageRequired = sizeof(T)) {
 | 
| -        SkASSERT(fNumObjects < kMaxObjects);
 | 
| -        SkASSERT(storageRequired >= sizeof(T));
 | 
| -        if (kMaxObjects == fNumObjects) {
 | 
| -            return nullptr;
 | 
| -        }
 | 
| -        const size_t storageRemaining = sizeof(fStorage) - fStorageUsed;
 | 
| -        Rec* rec = &fRecs[fNumObjects];
 | 
| -        if (storageRequired > storageRemaining) {
 | 
| -            // Allocate on the heap. Ideally we want to avoid this situation.
 | 
| +    template <typename Initer>
 | 
| +    auto createWithIniter(size_t size, Initer initer) -> decltype(initer(nullptr)) {
 | 
| +        using ObjType = typename std::remove_pointer<decltype(initer(nullptr))>::type;
 | 
| +        SkASSERT(size >= sizeof(ObjType));
 | 
|  
 | 
| -            // With the gm composeshader_bitmap2, storage required is 4476
 | 
| -            // and storage remaining is 3392. Increasing the base storage
 | 
| -            // causes google 3 tests to fail.
 | 
| -
 | 
| -            rec->fStorageSize = 0;
 | 
| -            rec->fHeapStorage = sk_malloc_throw(storageRequired);
 | 
| -            rec->fObj = static_cast<void*>(rec->fHeapStorage);
 | 
| -        } else {
 | 
| -            // There is space in fStorage.
 | 
| -            rec->fStorageSize = storageRequired;
 | 
| -            rec->fHeapStorage = nullptr;
 | 
| -            rec->fObj = static_cast<void*>(fStorage + fStorageUsed);
 | 
| -            fStorageUsed += storageRequired;
 | 
| +        void* storage = this->reserve(size, DefaultDestructor<ObjType>);
 | 
| +        auto candidate = initer(storage);
 | 
| +        if (!candidate) {
 | 
| +            // Initializing didn't workout so free the memory.
 | 
| +            this->freeLast();
 | 
|          }
 | 
| -        rec->fKillProc = DestroyT<T>;
 | 
| -        fNumObjects++;
 | 
| -        return rec->fObj;
 | 
| +
 | 
| +        return candidate;
 | 
|      }
 | 
|  
 | 
|      /*
 | 
| -     *  Free the memory reserved last without calling the destructor.
 | 
| -     *  Can be used in a nested way, i.e. after reserving A and B, calling
 | 
| -     *  freeLast once will free B and calling it again will free A.
 | 
| +     * Free the last object allocated and call its destructor. This can be called multiple times
 | 
| +     * removing objects from the pool in reverse order.
 | 
|       */
 | 
| -    void freeLast() {
 | 
| -        SkASSERT(fNumObjects > 0);
 | 
| -        Rec* rec = &fRecs[fNumObjects - 1];
 | 
| -        sk_free(rec->fHeapStorage);
 | 
| -        fStorageUsed -= rec->fStorageSize;
 | 
| -
 | 
| -        fNumObjects--;
 | 
| +    void deleteLast() {
 | 
| +        SkASSERT(fRecs.count() > 0);
 | 
| +        Rec& rec = fRecs.back();
 | 
| +        rec.fDestructor(rec.fObj);
 | 
| +        this->freeLast();
 | 
|      }
 | 
|  
 | 
|  private:
 | 
| +    using Destructor = void(*)(void*);
 | 
|      struct Rec {
 | 
| -        size_t fStorageSize;  // 0 if allocated on heap
 | 
| -        void*  fObj;
 | 
| -        void*  fHeapStorage;
 | 
| -        void   (*fKillProc)(void*);
 | 
| +        char*      fObj;
 | 
| +        Destructor fDestructor;
 | 
|      };
 | 
|  
 | 
|      // Used to call the destructor for allocated objects.
 | 
|      template<typename T>
 | 
| -    static void DestroyT(void* ptr) {
 | 
| +    static void DefaultDestructor(void* ptr) {
 | 
|          static_cast<T*>(ptr)->~T();
 | 
|      }
 | 
|  
 | 
| -    alignas(16) char fStorage[kTotalBytes];
 | 
| -    size_t           fStorageUsed;  // Number of bytes used so far.
 | 
| -    uint32_t         fNumObjects;
 | 
| -    Rec              fRecs[kMaxObjects];
 | 
| +    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<char*>()(rec.fObj, fStorage)
 | 
| +            || !std::less<char*>()(rec.fObj, &fStorage[kTotalBytes])) {
 | 
| +            delete [] rec.fObj;
 | 
| +        } else {
 | 
| +            fStorageEnd = rec.fObj;
 | 
| +        }
 | 
| +        fRecs.pop_back();
 | 
| +    }
 | 
| +
 | 
| +    SkSTArray<kExpectedObjects, Rec, true> 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
 | 
| 
 |