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..8de02977adae070734ef8230e2b7c446cc0c6aec |
| --- /dev/null |
| +++ b/src/core/SkStackAllocator.h |
| @@ -0,0 +1,120 @@ |
| +/* |
| + * Copyright 2014 Google, Inc |
| + * |
| + * Use of this source code is governed by a BSD-style license that can be |
| + * found in the LICENSE file. |
| + */ |
| + |
| +#ifndef SkStackAllocator_DEFINED |
| +#define SkStackAllocator_DEFINED |
| + |
| +#include "SkTDArray.h" |
| +#include "SkTypes.h" |
| + |
| +/* |
| + * Helper for using an SkStackAllocator to create a new object. |
| + * @param ptr Raw pointer to typeName. It should be pointing to NULL, since |
| + * its old value will be ignored. After this macro finishes, it will point |
| + * to a new object of type typeName, allocated by the SkStackAllocator, |
| + * which will handle deleting it. |
| + * @param allocator Reference to an SkStackAllocator. Will be used to allocate |
| + * an object of type typeName. It handles deleting the new object in its |
| + * destructor. |
| + * @param typeName type of the new object to create. |
| + * @param args Arguments to pass to the constructor of the new object, in |
| + * parentheses, or () for the default constructor. |
| + * |
| + * Sample usage: |
| + * <code> |
| + * SkStackAllocator<100> alloc; |
| + * SkBlitter* blitter (NULL); |
| + * SkSTACK_ALLOCATE(blitter, alloc, SkNullBlitter, ()); |
| + * </code> |
| + * |
| + * After this line, blitter points to a new SkNullBlitter object, which will |
| + * be destroyed when alloc goes out of scope. |
| + */ |
| +#define SkSTACK_ALLOCATE(ptr, allocator, typeName, args) \ |
| + void* buf = allocator.reserveT<typeName>(); \ |
| + SkNEW_PLACEMENT_ARGS(buf, typeName, args); \ |
| + ptr = static_cast<typeName*>(buf) |
| + |
| +// Used by SkStackAllocator 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 (as defined by kSize) objects on the |
|
mtklein
2014/02/27 23:40:54
This sort of reads like we'll be able to allocate
scroggo
2014/03/05 20:25:52
Correct. I've updated the comment. Sound better?
|
| + * stack. If an object is larger than kSize, it will be allocated on the |
| + * heap, This class's destructor will handle calling the destructor for each |
| + * object it allocated and freeing its memory. |
| + */ |
| +template<size_t kSize> class SkStackAllocator : public SkNoncopyable { |
|
mtklein
2014/02/27 23:40:54
It looked like we're generally using this to alloc
scroggo
2014/03/05 20:25:52
Actually, no. The Sk3DBlitter will be allocated by
|
| +public: |
| + SkStackAllocator() |
| + : fStorageUsed(0) |
| + {} |
| + |
| + ~SkStackAllocator() { |
| + // Destruct in reverse order, in case an earlier object points to a |
| + // later object. |
| + while (fObjects.count() > 0) { |
| + Triple triple; |
| + fObjects.pop(&triple); |
| + triple.fKillProc(triple.fObj); |
| + // Safe to do if fObj is in fStorage, since fHeapStorage will |
| + // point to NULL. |
| + sk_free(triple.fHeapStorage); |
| + } |
| + } |
| + |
| + /* |
| + * Create and return space for a T. The space will be on the stack 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. |
| + * |
| + * The caller MUST create a new T in the returned buffer (using |
| + * SkNEW_PLACEMENT or SkNEW_PLACEMENT_ARGS), as this class will call ~T() |
| + * in its destructor. (FIXME: Can we pass T's constructor's arguments to |
| + * this function so we can create the object as well?) |
|
bungeman-skia
2014/02/27 18:26:01
With perfect forwarding in c++11 you can. With c++
mtklein
2014/02/27 18:51:02
A good compromise is to make a few versions:
temp
scroggo
2014/03/05 20:25:52
Done.
|
| + */ |
| + template<typename T> void* reserveT() { |
| + const size_t storageRemaining = SkAlign4(kSize) - fStorageUsed; |
| + const size_t storageRequired = SkAlign4(sizeof(T)); |
| + Triple triple; |
| + if (storageRequired > storageRemaining) { |
| + // Allocate on the heap |
| + triple.fHeapStorage = sk_malloc_throw(storageRequired); |
| + triple.fObj = static_cast<void*>(triple.fHeapStorage); |
| + } else { |
| + // There is space in fStorage. |
| + triple.fHeapStorage = NULL; |
| + SkASSERT(SkIsAlign4(fStorageUsed)); |
| + triple.fObj = static_cast<void*>(fStorage + (fStorageUsed >> 2)); |
| + fStorageUsed += storageRequired; |
| + } |
| + triple.fKillProc = destroyT<T>; |
| + fObjects.push(triple); |
| + return triple.fObj; |
| + } |
| + |
| +private: |
| + struct Triple { |
| + void* fObj; |
|
mtklein
2014/02/27 23:40:54
If you really want to keep things small, we might
scroggo
2014/03/05 20:25:52
Interesting idea! Are we guaranteed the bottom bit
|
| + void* fHeapStorage; |
| + void (*fKillProc)(void*); |
| + |
| + }; |
| + |
| + // Number of bytes used so far. |
| + size_t fStorageUsed; |
| + SkTDArray<Triple> fObjects; |
| + // Pad the storage size to be 4-byte aligned. |
| + uint32_t fStorage[SkAlign4(kSize) >> 2]; |
|
mtklein
2014/02/27 23:40:54
Consider storing everything in fStorage? Does it
scroggo
2014/03/05 20:25:52
That's a very good point. I've removed the SkTDArr
|
| +}; |
| +// This class only makes sense on the stack. |
|
mtklein
2014/02/27 23:40:54
This doesn't strictly have to be on the stack righ
scroggo
2014/03/05 20:25:52
Agreed. Renamed SkSmallAllocator.
And I've remove
|
| +#define SkStackAllocator(...) SK_REQUIRE_LOCAL_VAR(SkStackAllocator) |
| + |
| +#endif // SkStackAllocator_DEFINED |