Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(46)

Side by Side Diff: src/core/SkStackAllocator.h

Issue 179343005: Add a class to allocate small objects w/o extra calls to new. (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Small comment fixes. Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 // FIXME: Move to SkSmallAllocator.h
mtklein 2014/03/05 20:59:56 :) I have found writing a git-fr (find-replace) s
9 #ifndef SkSmallAllocator_DEFINED
10 #define SkSmallAllocator_DEFINED
11
12 #include "SkTDArray.h"
13 #include "SkTypes.h"
14
15 // Used by SkSmallAllocator to call the destructor for objects it has
16 // allocated.
17 template<typename T> void destroyT(void* ptr) {
18 static_cast<T*>(ptr)->~T();
19 }
20
21 /*
22 * Template class for allocating small objects without additional heap memory
23 * allocations. kMaxObjects is a hard limit on the number of objects that can
24 * be allocated using this class. After that, attempts to create more objects
25 * with this class will return NULL.
26 * kBytes is the number of bytes provided for storage. If an object is larger
mtklein 2014/03/05 20:59:56 It's kBytes total? Or kBytes per object? E.g. if
scroggo 2014/03/05 23:23:26 I thought this was pretty clear, but I've changed
mtklein 2014/03/06 15:04:07 Thanks! I am no longer confused. I see now this
27 * than the storage (minus storage already used), it will be allocated on the
mtklein 2014/03/05 20:59:56 Can we get away with a hard failure like for count
scroggo 2014/03/05 23:23:26 That's a good idea. Added an assert for too many o
mtklein 2014/03/06 15:04:07 Hmm, that's a good point. It'd be easy to see a h
scroggo 2014/03/06 18:07:04 That sounds like a good compromise. Done. I didn't
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 kBytes>
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> T* createT() {
mtklein 2014/03/05 20:59:56 Might want to break the line after > for all of th
scroggo 2014/03/05 23:23:26 Done.
61 void* buf = this->reserveT<T>();
62 if (NULL == buf) {
63 return NULL;
64 }
65 SkNEW_PLACEMENT(buf, T);
66 return static_cast<T*>(buf);
67 }
68
69 template<typename T, typename A1> T* createT(const A1& a1) {
70 void* buf = this->reserveT<T>();
71 if (NULL == buf) {
72 return NULL;
73 }
74 SkNEW_PLACEMENT_ARGS(buf, T, (a1));
75 return static_cast<T*>(buf);
76 }
77
78 template<typename T, typename A1, typename A2>
79 T* createT(const A1& a1, const A2& a2) {
80 void* buf = this->reserveT<T>();
81 if (NULL == buf) {
82 return NULL;
83 }
84 SkNEW_PLACEMENT_ARGS(buf, T, (a1, a2));
85 return static_cast<T*>(buf);
86 }
87
88 template<typename T, typename A1, typename A2, typename A3>
89 T* createT(const A1& a1, const A2& a2, const A3& a3) {
90 void* buf = this->reserveT<T>();
91 if (NULL == buf) {
92 return NULL;
93 }
94 SkNEW_PLACEMENT_ARGS(buf, T, (a1, a2, a3));
95 return static_cast<T*>(buf);
96 }
97
98 private:
99 /*
100 * Helper function to provide space for one T. The space will be in
101 * fStorage if there is room, or on the heap otherwise. Either way, this
102 * class will call ~T() in its destructor and free the heap allocation if
103 * necessary.
104 */
105 template<typename T> void* reserveT() {
106 SkASSERT(fNumObjects <= kMaxObjects);
107 if (kMaxObjects == fNumObjects) {
108 return NULL;
109 }
110 const size_t storageRemaining = SkAlign4(kBytes) - fStorageUsed;
111 const size_t storageRequired = SkAlign4(sizeof(T));
112 Rec* rec = &fRecs[fNumObjects];
113 if (storageRequired > storageRemaining) {
114 // Allocate on the heap
115 rec->fHeapStorage = sk_malloc_throw(storageRequired);
116 rec->fObj = static_cast<void*>(rec->fHeapStorage);
117 } else {
118 // There is space in fStorage.
119 rec->fHeapStorage = NULL;
120 SkASSERT(SkIsAlign4(fStorageUsed));
121 rec->fObj = static_cast<void*>(fStorage + (fStorageUsed >> 2));
mtklein 2014/03/05 20:59:56 I'm developing a preference for / 4 unless the arg
scroggo 2014/03/05 23:23:26 Done.
122 fStorageUsed += storageRequired;
123 }
124 rec->fKillProc = destroyT<T>;
125 fNumObjects++;
126 return rec->fObj;
127 }
128
129 private:
130 struct Rec {
131 void* fObj;
mtklein 2014/03/05 20:59:56 Now that I'm thinking about it, it's probably not
132 void* fHeapStorage;
133 void (*fKillProc)(void*);
134 };
135
136 // Number of bytes used so far.
137 size_t fStorageUsed;
138 // Pad the storage size to be 4-byte aligned.
139 uint32_t fStorage[SkAlign4(kBytes) >> 2];
140 uint32_t fNumObjects;
141 Rec fRecs[kMaxObjects];
142 };
143
144 #endif // SkSmallAllocator_DEFINED
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698