| 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 "SkTDArray.h" |
| 12 #include "SkTypes.h" | 12 #include "SkTypes.h" |
| 13 | 13 |
| 14 #include <new> | 14 #include <new> |
| 15 | 15 |
| 16 /* | 16 /* |
| 17 * Template class for allocating small objects without additional heap memory | 17 * Template class for allocating small objects without additional heap memory |
| 18 * allocations. kMaxObjects is a hard limit on the number of objects that can | 18 * allocations. kMaxObjects is a hard limit on the number of objects that can |
| 19 * be allocated using this class. After that, attempts to create more objects | 19 * be allocated using this class. After that, attempts to create more objects |
| 20 * with this class will assert and return nullptr. | 20 * with this class will assert and return nullptr. |
| 21 * |
| 21 * kTotalBytes is the total number of bytes provided for storage for all | 22 * kTotalBytes is the total number of bytes provided for storage for all |
| 22 * objects created by this allocator. If an object to be created is larger | 23 * objects created by this allocator. If an object to be created is larger |
| 23 * than the storage (minus storage already used), it will be allocated on the | 24 * than the storage (minus storage already used), it will be allocated on the |
| 24 * heap. This class's destructor will handle calling the destructor for each | 25 * heap. This class's destructor will handle calling the destructor for each |
| 25 * object it allocated and freeing its memory. | 26 * object it allocated and freeing its memory. |
| 27 * |
| 28 * Current the class always aligns each allocation to 16-bytes to be safe, but
future |
| 29 * may reduce this to only the alignment that is required per alloc. |
| 26 */ | 30 */ |
| 27 template<uint32_t kMaxObjects, size_t kTotalBytes> | 31 template<uint32_t kMaxObjects, size_t kTotalBytes> |
| 28 class SkSmallAllocator : SkNoncopyable { | 32 class SkSmallAllocator : SkNoncopyable { |
| 29 public: | 33 public: |
| 30 SkSmallAllocator() | 34 SkSmallAllocator() |
| 31 : fStorageUsed(0) | 35 : fStorageUsed(0) |
| 32 , fNumObjects(0) | 36 , fNumObjects(0) |
| 33 {} | 37 {} |
| 34 | 38 |
| 35 ~SkSmallAllocator() { | 39 ~SkSmallAllocator() { |
| (...skipping 24 matching lines...) Expand all Loading... |
| 60 return new (buf) T(args...); | 64 return new (buf) T(args...); |
| 61 } | 65 } |
| 62 | 66 |
| 63 /* | 67 /* |
| 64 * Reserve a specified amount of space (must be enough space for one T). | 68 * Reserve a specified amount of space (must be enough space for one T). |
| 65 * The space will be in fStorage if there is room, or on the heap otherwise
. | 69 * The space will be in fStorage if there is room, or on the heap otherwise
. |
| 66 * Either way, this class will call ~T() in its destructor and free the hea
p | 70 * Either way, this class will call ~T() in its destructor and free the hea
p |
| 67 * allocation if necessary. | 71 * allocation if necessary. |
| 68 * Unlike createT(), this method will not call the constructor of T. | 72 * Unlike createT(), this method will not call the constructor of T. |
| 69 */ | 73 */ |
| 70 template<typename T> void* reserveT(size_t storageRequired = sizeof(T)) { | 74 template<typename T> void* reserveT(size_t storageRequested = sizeof(T)) { |
| 71 SkASSERT(fNumObjects < kMaxObjects); | 75 SkASSERT(fNumObjects < kMaxObjects); |
| 72 SkASSERT(storageRequired >= sizeof(T)); | 76 SkASSERT(storageRequested >= sizeof(T)); |
| 73 if (kMaxObjects == fNumObjects) { | 77 if (kMaxObjects == fNumObjects) { |
| 74 return nullptr; | 78 return nullptr; |
| 75 } | 79 } |
| 76 const size_t storageRemaining = SkAlign4(kTotalBytes) - fStorageUsed; | 80 const size_t storageRemaining = sizeof(fStorage) - fStorageUsed; |
| 77 storageRequired = SkAlign4(storageRequired); | 81 const size_t storageRequired = SkAlign16(storageRequested); |
| 78 Rec* rec = &fRecs[fNumObjects]; | 82 Rec* rec = &fRecs[fNumObjects]; |
| 79 if (storageRequired > storageRemaining) { | 83 if (storageRequired > storageRemaining) { |
| 80 // Allocate on the heap. Ideally we want to avoid this situation, | 84 // Allocate on the heap. Ideally we want to avoid this situation, |
| 81 // but we're not sure we can catch all callers, so handle it but | 85 // but we're not sure we can catch all callers, so handle it but |
| 82 // assert false in debug mode. | 86 // assert false in debug mode. |
| 83 SkASSERT(false); | 87 SkASSERT(false); |
| 84 rec->fStorageSize = 0; | 88 rec->fStorageSize = 0; |
| 85 rec->fHeapStorage = sk_malloc_throw(storageRequired); | 89 rec->fHeapStorage = sk_malloc_throw(storageRequired); |
| 86 rec->fObj = static_cast<void*>(rec->fHeapStorage); | 90 rec->fObj = static_cast<void*>(rec->fHeapStorage); |
| 87 } else { | 91 } else { |
| 88 // There is space in fStorage. | 92 // There is space in fStorage. |
| 89 rec->fStorageSize = storageRequired; | 93 rec->fStorageSize = storageRequired; |
| 90 rec->fHeapStorage = nullptr; | 94 rec->fHeapStorage = nullptr; |
| 91 SkASSERT(SkIsAlign4(fStorageUsed)); | 95 SkASSERT(SkIsAlign16(fStorageUsed)); |
| 92 rec->fObj = static_cast<void*>(fStorage + (fStorageUsed / 4)); | 96 rec->fObj = static_cast<void*>(fStorage.fBytes + fStorageUsed); |
| 93 fStorageUsed += storageRequired; | 97 fStorageUsed += storageRequired; |
| 94 } | 98 } |
| 95 rec->fKillProc = DestroyT<T>; | 99 rec->fKillProc = DestroyT<T>; |
| 96 fNumObjects++; | 100 fNumObjects++; |
| 97 return rec->fObj; | 101 return rec->fObj; |
| 98 } | 102 } |
| 99 | 103 |
| 100 /* | 104 /* |
| 101 * Free the memory reserved last without calling the destructor. | 105 * Free the memory reserved last without calling the destructor. |
| 102 * Can be used in a nested way, i.e. after reserving A and B, calling | 106 * Can be used in a nested way, i.e. after reserving A and B, calling |
| (...skipping 15 matching lines...) Expand all Loading... |
| 118 void* fHeapStorage; | 122 void* fHeapStorage; |
| 119 void (*fKillProc)(void*); | 123 void (*fKillProc)(void*); |
| 120 }; | 124 }; |
| 121 | 125 |
| 122 // Used to call the destructor for allocated objects. | 126 // Used to call the destructor for allocated objects. |
| 123 template<typename T> | 127 template<typename T> |
| 124 static void DestroyT(void* ptr) { | 128 static void DestroyT(void* ptr) { |
| 125 static_cast<T*>(ptr)->~T(); | 129 static_cast<T*>(ptr)->~T(); |
| 126 } | 130 } |
| 127 | 131 |
| 132 struct SK_STRUCT_ALIGN(16) Storage { |
| 133 // we add kMaxObjects * 15 to account for the worst-case slop, where eac
h allocation wasted |
| 134 // 15 bytes (due to forcing each to be 16-byte aligned) |
| 135 char fBytes[kTotalBytes + kMaxObjects * 15]; |
| 136 }; |
| 137 |
| 138 Storage fStorage; |
| 128 // Number of bytes used so far. | 139 // Number of bytes used so far. |
| 129 size_t fStorageUsed; | 140 size_t fStorageUsed; |
| 130 // Pad the storage size to be 4-byte aligned. | 141 uint32_t fNumObjects; |
| 131 uint32_t fStorage[SkAlign4(kTotalBytes) >> 2]; | 142 Rec fRecs[kMaxObjects]; |
| 132 uint32_t fNumObjects; | |
| 133 Rec fRecs[kMaxObjects]; | |
| 134 }; | 143 }; |
| 135 | 144 |
| 136 #endif // SkSmallAllocator_DEFINED | 145 #endif // SkSmallAllocator_DEFINED |
| OLD | NEW |