|
1 /* |
|
2 * Copyright 2014 Google, Inc |
|
3 * |
|
4 * Use of this source code is governed by a BSD-style license that can be |
|
5 * found in the LICENSE file. |
|
6 */ |
|
7 |
|
8 #ifndef SkSmallAllocator_DEFINED |
|
9 #define SkSmallAllocator_DEFINED |
|
10 |
|
11 #include "SkTDArray.h" |
|
12 #include "SkTypes.h" |
|
13 |
|
14 // Used by SkSmallAllocator to call the destructor for objects it has |
|
15 // allocated. |
|
16 template<typename T> void destroyT(void* ptr) { |
|
17 static_cast<T*>(ptr)->~T(); |
|
18 } |
|
19 |
|
20 /* |
|
21 * Template class for allocating small objects without additional heap memory |
|
22 * allocations. kMaxObjects is a hard limit on the number of objects that can |
|
23 * be allocated using this class. After that, attempts to create more objects |
|
24 * with this class will assert and return NULL. |
|
25 * kTotalBytes is the total number of bytes provided for storage for all |
|
26 * objects created by this allocator. If an object to be created is larger |
|
27 * than the storage (minus storage already used), it will be allocated on the |
|
28 * heap. This class's destructor will handle calling the destructor for each |
|
29 * object it allocated and freeing its memory. |
|
30 */ |
|
31 template<uint32_t kMaxObjects, size_t kTotalBytes> |
|
32 class SkSmallAllocator : public SkNoncopyable { |
|
33 public: |
|
34 SkSmallAllocator() |
|
35 : fStorageUsed(0) |
|
36 , fNumObjects(0) |
|
37 {} |
|
38 |
|
39 ~SkSmallAllocator() { |
|
40 // Destruct in reverse order, in case an earlier object points to a |
|
41 // later object. |
|
42 while (fNumObjects > 0) { |
|
43 fNumObjects--; |
|
44 Rec* rec = &fRecs[fNumObjects]; |
|
45 rec->fKillProc(rec->fObj); |
|
46 // Safe to do if fObj is in fStorage, since fHeapStorage will |
|
47 // point to NULL. |
|
48 sk_free(rec->fHeapStorage); |
|
49 } |
|
50 } |
|
51 |
|
52 /* |
|
53 * Create a new object of type T. Its lifetime will be handled by this |
|
54 * SkSmallAllocator. |
|
55 * Each version behaves the same but takes a different number of |
|
56 * arguments. |
|
57 * Note: If kMaxObjects have been created by this SkSmallAllocator, NULL |
|
58 * will be returned. |
|
59 */ |
|
60 template<typename T> |
|
61 T* createT() { |
|
62 void* buf = this->reserveT<T>(); |
|
63 if (NULL == buf) { |
|
64 return NULL; |
|
65 } |
|
66 SkNEW_PLACEMENT(buf, T); |
|
67 return static_cast<T*>(buf); |
|
68 } |
|
69 |
|
70 template<typename T, typename A1> T* createT(const A1& a1) { |
|
71 void* buf = this->reserveT<T>(); |
|
72 if (NULL == buf) { |
|
73 return NULL; |
|
74 } |
|
75 SkNEW_PLACEMENT_ARGS(buf, T, (a1)); |
|
76 return static_cast<T*>(buf); |
|
77 } |
|
78 |
|
79 template<typename T, typename A1, typename A2> |
|
80 T* createT(const A1& a1, const A2& a2) { |
|
81 void* buf = this->reserveT<T>(); |
|
82 if (NULL == buf) { |
|
83 return NULL; |
|
84 } |
|
85 SkNEW_PLACEMENT_ARGS(buf, T, (a1, a2)); |
|
86 return static_cast<T*>(buf); |
|
87 } |
|
88 |
|
89 template<typename T, typename A1, typename A2, typename A3> |
|
90 T* createT(const A1& a1, const A2& a2, const A3& a3) { |
|
91 void* buf = this->reserveT<T>(); |
|
92 if (NULL == buf) { |
|
93 return NULL; |
|
94 } |
|
95 SkNEW_PLACEMENT_ARGS(buf, T, (a1, a2, a3)); |
|
96 return static_cast<T*>(buf); |
|
97 } |
|
98 |
|
99 /* |
|
100 * Reserve a specified amount of space (must be enough space for one T). |
|
101 * The space will be in fStorage if there is room, or on the heap otherwise. |
|
102 * Either way, this class will call ~T() in its destructor and free the heap |
|
103 * allocation if necessary. |
|
104 * Unlike createT(), this method will not call the constructor of T. |
|
105 */ |
|
106 template<typename T> void* reserveT(size_t storageRequired = sizeof(T)) { |
|
107 SkASSERT(fNumObjects < kMaxObjects); |
|
108 SkASSERT(storageRequired >= sizeof(T)); |
|
109 if (kMaxObjects == fNumObjects) { |
|
110 return NULL; |
|
111 } |
|
112 const size_t storageRemaining = SkAlign4(kTotalBytes) - fStorageUsed; |
|
113 storageRequired = SkAlign4(storageRequired); |
|
114 Rec* rec = &fRecs[fNumObjects]; |
|
115 if (storageRequired > storageRemaining) { |
|
116 // Allocate on the heap. Ideally we want to avoid this situation, |
|
117 // but we're not sure we can catch all callers, so handle it but |
|
118 // assert false in debug mode. |
|
119 SkASSERT(false); |
|
120 rec->fHeapStorage = sk_malloc_throw(storageRequired); |
|
121 rec->fObj = static_cast<void*>(rec->fHeapStorage); |
|
122 } else { |
|
123 // There is space in fStorage. |
|
124 rec->fHeapStorage = NULL; |
|
125 SkASSERT(SkIsAlign4(fStorageUsed)); |
|
126 rec->fObj = static_cast<void*>(fStorage + (fStorageUsed / 4)); |
|
127 fStorageUsed += storageRequired; |
|
128 } |
|
129 rec->fKillProc = destroyT<T>; |
|
130 fNumObjects++; |
|
131 return rec->fObj; |
|
132 } |
|
133 |
|
134 private: |
|
135 struct Rec { |
|
136 void* fObj; |
|
137 void* fHeapStorage; |
|
138 void (*fKillProc)(void*); |
|
139 }; |
|
140 |
|
141 // Number of bytes used so far. |
|
142 size_t fStorageUsed; |
|
143 // Pad the storage size to be 4-byte aligned. |
|
144 uint32_t fStorage[SkAlign4(kTotalBytes) >> 2]; |
|
145 uint32_t fNumObjects; |
|
146 Rec fRecs[kMaxObjects]; |
|
147 }; |
|
148 |
|
149 #endif // SkSmallAllocator_DEFINED |