Chromium Code Reviews| Index: src/core/SkStackAllocator.h |
| diff --git a/src/core/SkStackAllocator.h b/src/core/SkStackAllocator.h |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..bbc5fe648a51243f1a7241b4518197c6bdedf2fa |
| --- /dev/null |
| +++ b/src/core/SkStackAllocator.h |
| @@ -0,0 +1,144 @@ |
| +/* |
| + * Copyright 2014 Google, Inc |
| + * |
| + * Use of this source code is governed by a BSD-style license that can be |
| + * found in the LICENSE file. |
| + */ |
| + |
| +// FIXME: Move to SkSmallAllocator.h |
|
mtklein
2014/03/05 20:59:56
:) I have found writing a git-fr (find-replace) s
|
| +#ifndef SkSmallAllocator_DEFINED |
| +#define SkSmallAllocator_DEFINED |
| + |
| +#include "SkTDArray.h" |
| +#include "SkTypes.h" |
| + |
| +// Used by SkSmallAllocator to call the destructor for objects it has |
| +// allocated. |
| +template<typename T> void destroyT(void* ptr) { |
| + static_cast<T*>(ptr)->~T(); |
| +} |
| + |
| +/* |
| + * Template class for allocating small objects without additional heap memory |
| + * allocations. kMaxObjects is a hard limit on the number of objects that can |
| + * be allocated using this class. After that, attempts to create more objects |
| + * with this class will return NULL. |
| + * 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
|
| + * 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
|
| + * heap. This class's destructor will handle calling the destructor for each |
| + * object it allocated and freeing its memory. |
| + */ |
| +template<uint32_t kMaxObjects, size_t kBytes> |
| +class SkSmallAllocator : public SkNoncopyable { |
| +public: |
| + SkSmallAllocator() |
| + : fStorageUsed(0) |
| + , fNumObjects(0) |
| + {} |
| + |
| + ~SkSmallAllocator() { |
| + // Destruct in reverse order, in case an earlier object points to a |
| + // later object. |
| + while (fNumObjects > 0) { |
| + fNumObjects--; |
| + Rec* rec = &fRecs[fNumObjects]; |
| + rec->fKillProc(rec->fObj); |
| + // Safe to do if fObj is in fStorage, since fHeapStorage will |
| + // point to NULL. |
| + sk_free(rec->fHeapStorage); |
| + } |
| + } |
| + |
| + /* |
| + * Create a new object of type T. Its lifetime will be handled by this |
| + * SkSmallAllocator. |
| + * Each version behaves the same but takes a different number of |
| + * arguments. |
| + * Note: If kMaxObjects have been created by this SkSmallAllocator, NULL |
| + * will be returned. |
| + */ |
| + 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.
|
| + void* buf = this->reserveT<T>(); |
| + if (NULL == buf) { |
| + return NULL; |
| + } |
| + SkNEW_PLACEMENT(buf, T); |
| + return static_cast<T*>(buf); |
| + } |
| + |
| + template<typename T, typename A1> T* createT(const A1& a1) { |
| + void* buf = this->reserveT<T>(); |
| + if (NULL == buf) { |
| + return NULL; |
| + } |
| + SkNEW_PLACEMENT_ARGS(buf, T, (a1)); |
| + return static_cast<T*>(buf); |
| + } |
| + |
| + template<typename T, typename A1, typename A2> |
| + T* createT(const A1& a1, const A2& a2) { |
| + void* buf = this->reserveT<T>(); |
| + if (NULL == buf) { |
| + return NULL; |
| + } |
| + SkNEW_PLACEMENT_ARGS(buf, T, (a1, a2)); |
| + return static_cast<T*>(buf); |
| + } |
| + |
| + template<typename T, typename A1, typename A2, typename A3> |
| + T* createT(const A1& a1, const A2& a2, const A3& a3) { |
| + void* buf = this->reserveT<T>(); |
| + if (NULL == buf) { |
| + return NULL; |
| + } |
| + SkNEW_PLACEMENT_ARGS(buf, T, (a1, a2, a3)); |
| + return static_cast<T*>(buf); |
| + } |
| + |
| +private: |
| + /* |
| + * Helper function to provide space for one T. The space will be in |
| + * fStorage if there is room, or on the heap otherwise. Either way, this |
| + * class will call ~T() in its destructor and free the heap allocation if |
| + * necessary. |
| + */ |
| + template<typename T> void* reserveT() { |
| + SkASSERT(fNumObjects <= kMaxObjects); |
| + if (kMaxObjects == fNumObjects) { |
| + return NULL; |
| + } |
| + const size_t storageRemaining = SkAlign4(kBytes) - fStorageUsed; |
| + const size_t storageRequired = SkAlign4(sizeof(T)); |
| + Rec* rec = &fRecs[fNumObjects]; |
| + if (storageRequired > storageRemaining) { |
| + // Allocate on the heap |
| + rec->fHeapStorage = sk_malloc_throw(storageRequired); |
| + rec->fObj = static_cast<void*>(rec->fHeapStorage); |
| + } else { |
| + // There is space in fStorage. |
| + rec->fHeapStorage = NULL; |
| + SkASSERT(SkIsAlign4(fStorageUsed)); |
| + 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.
|
| + fStorageUsed += storageRequired; |
| + } |
| + rec->fKillProc = destroyT<T>; |
| + fNumObjects++; |
| + return rec->fObj; |
| + } |
| + |
| +private: |
| + struct Rec { |
| + void* fObj; |
|
mtklein
2014/03/05 20:59:56
Now that I'm thinking about it, it's probably not
|
| + void* fHeapStorage; |
| + void (*fKillProc)(void*); |
| + }; |
| + |
| + // Number of bytes used so far. |
| + size_t fStorageUsed; |
| + // Pad the storage size to be 4-byte aligned. |
| + uint32_t fStorage[SkAlign4(kBytes) >> 2]; |
| + uint32_t fNumObjects; |
| + Rec fRecs[kMaxObjects]; |
| +}; |
| + |
| +#endif // SkSmallAllocator_DEFINED |