| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2014 Google, Inc | 2 * Copyright 2014 Google, Inc |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #ifndef SkSmallAllocator_DEFINED | 8 #ifndef SkSmallAllocator_DEFINED |
| 9 #define SkSmallAllocator_DEFINED | 9 #define SkSmallAllocator_DEFINED |
| 10 | 10 |
| 11 #include "SkTDArray.h" | 11 #include "SkTArray.h" |
| 12 #include "SkTypes.h" | 12 #include "SkTypes.h" |
| 13 | 13 |
| 14 #include <new> | 14 #include <functional> |
| 15 #include <type_traits> |
| 15 #include <utility> | 16 #include <utility> |
| 16 | 17 |
| 18 |
| 19 // max_align_t is needed to calculate the alignment for createWithIniterT when t
he T used is an |
| 20 // abstract type. The complication with max_align_t is that it is defined differ
ently for |
| 21 // different builds. |
| 22 namespace { |
| 23 #if defined(SK_BUILD_FOR_WIN32) || defined(SK_BUILD_FOR_MAC) |
| 24 // Use std::max_align_t for compiles that follow the standard. |
| 25 #include <cstddef> |
| 26 using SystemAlignment = std::max_align_t; |
| 27 #else |
| 28 // Ubuntu compiles don't have std::max_align_t defined, but MSVC does not de
fine max_align_t. |
| 29 #include <stddef.h> |
| 30 using SystemAlignment = max_align_t; |
| 31 #endif |
| 32 } |
| 33 |
| 17 /* | 34 /* |
| 18 * Template class for allocating small objects without additional heap memory | 35 * Template class for allocating small objects without additional heap memory |
| 19 * allocations. kMaxObjects is a hard limit on the number of objects that can | 36 * allocations. |
| 20 * be allocated using this class. After that, attempts to create more objects | |
| 21 * with this class will assert and return nullptr. | |
| 22 * | 37 * |
| 23 * kTotalBytes is the total number of bytes provided for storage for all | 38 * kTotalBytes is the total number of bytes provided for storage for all |
| 24 * objects created by this allocator. If an object to be created is larger | 39 * objects created by this allocator. If an object to be created is larger |
| 25 * than the storage (minus storage already used), it will be allocated on the | 40 * than the storage (minus storage already used), it will be allocated on the |
| 26 * heap. This class's destructor will handle calling the destructor for each | 41 * heap. This class's destructor will handle calling the destructor for each |
| 27 * object it allocated and freeing its memory. | 42 * object it allocated and freeing its memory. |
| 28 * | |
| 29 * Current the class always aligns each allocation to 16-bytes to be safe, but
future | |
| 30 * may reduce this to only the alignment that is required per alloc. | |
| 31 */ | 43 */ |
| 32 template<uint32_t kMaxObjects, size_t kTotalBytes> | 44 template<uint32_t kExpectedObjects, size_t kTotalBytes> |
| 33 class SkSmallAllocator : SkNoncopyable { | 45 class SkSmallAllocator : SkNoncopyable { |
| 34 public: | 46 public: |
| 35 SkSmallAllocator() | |
| 36 : fStorageUsed(0) | |
| 37 , fNumObjects(0) | |
| 38 {} | |
| 39 | |
| 40 ~SkSmallAllocator() { | 47 ~SkSmallAllocator() { |
| 41 // Destruct in reverse order, in case an earlier object points to a | 48 // Destruct in reverse order, in case an earlier object points to a |
| 42 // later object. | 49 // later object. |
| 43 while (fNumObjects > 0) { | 50 while (fRecs.count() > 0) { |
| 44 fNumObjects--; | 51 this->deleteLast(); |
| 45 Rec* rec = &fRecs[fNumObjects]; | |
| 46 rec->fKillProc(rec->fObj); | |
| 47 // Safe to do if fObj is in fStorage, since fHeapStorage will | |
| 48 // point to nullptr. | |
| 49 sk_free(rec->fHeapStorage); | |
| 50 } | 52 } |
| 51 } | 53 } |
| 52 | 54 |
| 53 /* | 55 /* |
| 54 * Create a new object of type T. Its lifetime will be handled by this | 56 * Create a new object of type T. Its lifetime will be handled by this |
| 55 * SkSmallAllocator. | 57 * SkSmallAllocator. |
| 56 * Note: If kMaxObjects have been created by this SkSmallAllocator, nullptr | |
| 57 * will be returned. | |
| 58 */ | 58 */ |
| 59 template<typename T, typename... Args> | 59 template<typename T, typename... Args> |
| 60 T* createT(Args&&... args) { | 60 T* createT(Args&&... args) { |
| 61 void* buf = this->reserveT<T>(); | 61 void* buf = this->reserve(sizeof(T), DefaultDestructor<T>); |
| 62 if (nullptr == buf) { | |
| 63 return nullptr; | |
| 64 } | |
| 65 return new (buf) T(std::forward<Args>(args)...); | 62 return new (buf) T(std::forward<Args>(args)...); |
| 66 } | 63 } |
| 67 | 64 |
| 68 /* | 65 /* |
| 69 * Reserve a specified amount of space (must be enough space for one T). | 66 * Create a new object of size using initer to initialize the memory. The in
iter function has |
| 70 * The space will be in fStorage if there is room, or on the heap otherwise
. | 67 * the signature T* initer(void* storage). If initer is unable to initialize
the memory it |
| 71 * Either way, this class will call ~T() in its destructor and free the hea
p | 68 * should return nullptr where SkSmallAllocator will free the memory. |
| 72 * allocation if necessary. | |
| 73 * Unlike createT(), this method will not call the constructor of T. | |
| 74 */ | 69 */ |
| 75 template<typename T> void* reserveT(size_t storageRequired = sizeof(T)) { | 70 template <typename Initer> |
| 76 SkASSERT(fNumObjects < kMaxObjects); | 71 auto createWithIniter(size_t size, Initer initer) -> decltype(initer(nullptr
)) { |
| 77 SkASSERT(storageRequired >= sizeof(T)); | 72 using ObjType = typename std::remove_pointer<decltype(initer(nullptr))>:
:type; |
| 78 if (kMaxObjects == fNumObjects) { | 73 SkASSERT(size >= sizeof(ObjType)); |
| 79 return nullptr; | 74 |
| 75 void* storage = this->reserve(size, DefaultDestructor<ObjType>); |
| 76 auto candidate = initer(storage); |
| 77 if (!candidate) { |
| 78 // Initializing didn't workout so free the memory. |
| 79 this->freeLast(); |
| 80 } | 80 } |
| 81 const size_t storageRemaining = sizeof(fStorage) - fStorageUsed; | |
| 82 Rec* rec = &fRecs[fNumObjects]; | |
| 83 if (storageRequired > storageRemaining) { | |
| 84 // Allocate on the heap. Ideally we want to avoid this situation. | |
| 85 | 81 |
| 86 // With the gm composeshader_bitmap2, storage required is 4476 | 82 return candidate; |
| 87 // and storage remaining is 3392. Increasing the base storage | |
| 88 // causes google 3 tests to fail. | |
| 89 | |
| 90 rec->fStorageSize = 0; | |
| 91 rec->fHeapStorage = sk_malloc_throw(storageRequired); | |
| 92 rec->fObj = static_cast<void*>(rec->fHeapStorage); | |
| 93 } else { | |
| 94 // There is space in fStorage. | |
| 95 rec->fStorageSize = storageRequired; | |
| 96 rec->fHeapStorage = nullptr; | |
| 97 rec->fObj = static_cast<void*>(fStorage + fStorageUsed); | |
| 98 fStorageUsed += storageRequired; | |
| 99 } | |
| 100 rec->fKillProc = DestroyT<T>; | |
| 101 fNumObjects++; | |
| 102 return rec->fObj; | |
| 103 } | 83 } |
| 104 | 84 |
| 105 /* | 85 /* |
| 106 * Free the memory reserved last without calling the destructor. | 86 * Free the last object allocated and call its destructor. This can be calle
d multiple times |
| 107 * Can be used in a nested way, i.e. after reserving A and B, calling | 87 * removing objects from the pool in reverse order. |
| 108 * freeLast once will free B and calling it again will free A. | |
| 109 */ | 88 */ |
| 110 void freeLast() { | 89 void deleteLast() { |
| 111 SkASSERT(fNumObjects > 0); | 90 SkASSERT(fRecs.count() > 0); |
| 112 Rec* rec = &fRecs[fNumObjects - 1]; | 91 Rec& rec = fRecs.back(); |
| 113 sk_free(rec->fHeapStorage); | 92 rec.fDestructor(rec.fObj); |
| 114 fStorageUsed -= rec->fStorageSize; | 93 this->freeLast(); |
| 115 | |
| 116 fNumObjects--; | |
| 117 } | 94 } |
| 118 | 95 |
| 119 private: | 96 private: |
| 97 using Destructor = void(*)(void*); |
| 120 struct Rec { | 98 struct Rec { |
| 121 size_t fStorageSize; // 0 if allocated on heap | 99 char* fObj; |
| 122 void* fObj; | 100 Destructor fDestructor; |
| 123 void* fHeapStorage; | |
| 124 void (*fKillProc)(void*); | |
| 125 }; | 101 }; |
| 126 | 102 |
| 127 // Used to call the destructor for allocated objects. | 103 // Used to call the destructor for allocated objects. |
| 128 template<typename T> | 104 template<typename T> |
| 129 static void DestroyT(void* ptr) { | 105 static void DefaultDestructor(void* ptr) { |
| 130 static_cast<T*>(ptr)->~T(); | 106 static_cast<T*>(ptr)->~T(); |
| 131 } | 107 } |
| 132 | 108 |
| 133 alignas(16) char fStorage[kTotalBytes]; | 109 static constexpr size_t kAlignment = alignof(SystemAlignment); |
| 134 size_t fStorageUsed; // Number of bytes used so far. | 110 |
| 135 uint32_t fNumObjects; | 111 static constexpr size_t AlignSize(size_t size) { |
| 136 Rec fRecs[kMaxObjects]; | 112 return (size + kAlignment - 1) & ~(kAlignment - 1); |
| 113 } |
| 114 |
| 115 // Reserve storageRequired from fStorage if possible otherwise allocate on t
he heap. |
| 116 void* reserve(size_t storageRequired, Destructor destructor) { |
| 117 // Make sure that all allocations stay aligned by rounding the storageRe
quired up to the |
| 118 // aligned value. |
| 119 char* objectStart = fStorageEnd; |
| 120 char* objectEnd = objectStart + AlignSize(storageRequired); |
| 121 Rec& rec = fRecs.push_back(); |
| 122 if (objectEnd > &fStorage[kTotalBytes]) { |
| 123 // Allocate on the heap. Ideally we want to avoid this situation. |
| 124 rec.fObj = new char [storageRequired]; |
| 125 } else { |
| 126 // There is space in fStorage. |
| 127 rec.fObj = objectStart; |
| 128 fStorageEnd = objectEnd; |
| 129 } |
| 130 rec.fDestructor = destructor; |
| 131 return rec.fObj; |
| 132 } |
| 133 |
| 134 void freeLast() { |
| 135 Rec& rec = fRecs.back(); |
| 136 if (std::less<char*>()(rec.fObj, fStorage) |
| 137 || !std::less<char*>()(rec.fObj, &fStorage[kTotalBytes])) { |
| 138 delete [] rec.fObj; |
| 139 } else { |
| 140 fStorageEnd = rec.fObj; |
| 141 } |
| 142 fRecs.pop_back(); |
| 143 } |
| 144 |
| 145 SkSTArray<kExpectedObjects, Rec, true> fRecs; |
| 146 char* fStorageEnd {fStorage}; |
| 147 // Since char have an alignment of 1, it should be forced onto an alignment
the compiler |
| 148 // expects which is the alignment of std::max_align_t. |
| 149 alignas (kAlignment) char fStorage[kTotalBytes]; |
| 137 }; | 150 }; |
| 138 | 151 |
| 139 #endif // SkSmallAllocator_DEFINED | 152 #endif // SkSmallAllocator_DEFINED |
| OLD | NEW |