OLD | NEW |
---|---|
(Empty) | |
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 | |
scroggo
2014/03/06 18:48:18
Reitveld isn't smart enough to notice this but thi
| |
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 private: | |
100 /* | |
101 * Helper function to provide space for one T. The space will be in | |
102 * fStorage if there is room, or on the heap otherwise. Either way, this | |
103 * class will call ~T() in its destructor and free the heap allocation if | |
104 * necessary. | |
105 */ | |
106 template<typename T> void* reserveT() { | |
107 SkASSERT(fNumObjects < kMaxObjects); | |
108 if (kMaxObjects == fNumObjects) { | |
109 return NULL; | |
110 } | |
111 const size_t storageRemaining = SkAlign4(kTotalBytes) - fStorageUsed; | |
112 const size_t storageRequired = SkAlign4(sizeof(T)); | |
113 Rec* rec = &fRecs[fNumObjects]; | |
114 if (storageRequired > storageRemaining) { | |
115 // Allocate on the heap. Ideally we want to avoid this situation, | |
116 // but we're not sure we can catch all callers, so handle it but | |
117 // assert false in debug mode. | |
118 SkASSERT(false); | |
119 rec->fHeapStorage = sk_malloc_throw(storageRequired); | |
120 rec->fObj = static_cast<void*>(rec->fHeapStorage); | |
121 } else { | |
122 // There is space in fStorage. | |
123 rec->fHeapStorage = NULL; | |
124 SkASSERT(SkIsAlign4(fStorageUsed)); | |
125 rec->fObj = static_cast<void*>(fStorage + (fStorageUsed / 4)); | |
126 fStorageUsed += storageRequired; | |
127 } | |
128 rec->fKillProc = destroyT<T>; | |
129 fNumObjects++; | |
130 return rec->fObj; | |
131 } | |
132 | |
133 private: | |
134 struct Rec { | |
135 void* fObj; | |
136 void* fHeapStorage; | |
137 void (*fKillProc)(void*); | |
138 }; | |
139 | |
140 // Number of bytes used so far. | |
141 size_t fStorageUsed; | |
142 // Pad the storage size to be 4-byte aligned. | |
143 uint32_t fStorage[SkAlign4(kTotalBytes) >> 2]; | |
144 uint32_t fNumObjects; | |
145 Rec fRecs[kMaxObjects]; | |
146 }; | |
147 | |
148 #endif // SkSmallAllocator_DEFINED | |
OLD | NEW |