Chromium Code Reviews| Index: src/gpu/GrTBaseList.h |
| diff --git a/src/gpu/GrTBaseList.h b/src/gpu/GrTBaseList.h |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..91125d9eab35b76ab67d275006bc45ed61c15d3e |
| --- /dev/null |
| +++ b/src/gpu/GrTBaseList.h |
| @@ -0,0 +1,203 @@ |
| +/* |
| + * 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 GrTBaseList_DEFINED |
| +#define GrTBaseList_DEFINED |
| + |
| +#include "SkTemplates.h" |
| +#include "SkTypes.h" |
| + |
| +template<typename TBase, typename TAlign> class GrTBaseList; |
| +template<typename T> class GrTBaseListAllocDesc; |
| + |
| +template <typename TBase, typename TAlign, typename T> |
| +void* operator new(size_t, GrTBaseList<TBase, TAlign>&, const GrTBaseListAllocDesc<T>&); |
| + |
| +/** |
| + * List of objects with a common base type and permanent memory addresses. |
|
bsalomon
2014/10/08 19:51:44
This thing seems complicated enough, and isolated
Chris Dalton
2014/10/09 00:06:59
Done.
|
| + * |
| + * This class preallocates its own chunks of memory for storing items in the list, so new |
| + * objects can be created without excessive calls to malloc(). |
|
bsalomon
2014/10/08 19:51:44
Comment about macros to push back? Casual user of
Chris Dalton
2014/10/09 00:06:59
Done.
|
| + * |
| + * @param TBase Common base type of items in the list. If TBase is not a class with a |
| + * virtual destructor, the client is responsible for invoking any necessary |
| + * destructors. |
| + * |
| + * For now, any subclass used in the list must have the same start address |
| + * as TBase (or in other words, the types must be convertible via |
| + * reinterpret_cast<>). Classes with multiple inheritance (or any subclass |
| + * on an obscure compiler) may not be compatible. This is verified at |
|
bsalomon
2014/10/08 19:51:45
Can we say "runtime asserted in debug builds" rath
Chris Dalton
2014/10/09 00:06:59
Done.
|
| + * runtime. |
| + * |
| + * @param TAlign A type whose size is the desired memory alignment for object allocations. |
| + * This should be the largest known alignment requirement for all objects |
| + * that may be stored in the buffer. |
| + */ |
| +template<typename TBase, typename TAlign> class GrTBaseList : SkNoncopyable { |
|
bsalomon
2014/10/08 19:51:44
I'm having trouble coming up with a better name
|
| +public: |
| + class Iter; |
| + |
| + /** |
| + * Create a contiguous list |
|
bsalomon
2014/10/08 19:51:44
Can we not say contiguous? That seems like an impl
Chris Dalton
2014/10/09 00:06:59
Oops, I missed that one from before.
Done.
|
| + * |
| + * @param initialSizeInBytes The amount of memory reserved by the list initially, and |
| + * and after calls to reset(). |
| + */ |
| + GrTBaseList(int initialSizeInBytes) |
| + : fHeadBlock(bytes_to_length(initialSizeInBytes)), |
| + fTailBlock(&fHeadBlock), |
| + fLastItem(NULL) {} |
| + |
| + ~GrTBaseList() { this->reset(); } |
| + |
| + bool empty() { return NULL == fLastItem; } |
| + |
| + TBase& back() { |
| + SkASSERT(!this->empty()); |
| + return *fLastItem; |
| + } |
| + |
| + /** |
| + * Destruct all items in the list and reset to empty. |
| + */ |
| + void reset(); |
| + |
| +private: |
| + static int bytes_to_length(int bytes) { return (bytes + sizeof(TAlign) - 1) / sizeof(TAlign); } |
| + |
| + struct ItemHeader { |
| + int fTotalLength; |
| + }; |
| + enum { kHeaderLength = (sizeof(ItemHeader) + sizeof(TAlign) - 1) / sizeof(TAlign) }; |
| + |
| + template<typename T> void* alloc_back(const GrTBaseListAllocDesc<T>&); |
| + |
| + struct MemBlock { |
| + MemBlock(int length) : fLength(length), fBack(0), fBuffer(fLength) {} |
| + const int fLength; |
| + int fBack; |
| + SkAutoTMalloc<TAlign> fBuffer; |
| + SkAutoTDelete<MemBlock> fNext; |
| + }; |
| + MemBlock fHeadBlock; |
| + MemBlock* fTailBlock; |
| + |
| + TBase* fLastItem; |
| + |
| + template <typename UBase, typename UAlign, typename U> |
| + friend void* operator new(size_t, GrTBaseList<UBase, UAlign>&, const GrTBaseListAllocDesc<U>&); |
| + |
| + friend class Iter; |
| +}; |
| + |
| +/** |
| + * Describes a new object allocation for GrTBaseList. Captures type and size. |
| + */ |
| +template<typename T> class GrTBaseListAllocDesc { |
| +public: |
| + GrTBaseListAllocDesc(int sizeInBytes = sizeof(T)) : fSizeInBytes(sizeInBytes) {} |
| + int sizeInBytes() const { return fSizeInBytes; } |
| +private: |
| + const int fSizeInBytes; |
| +}; |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| + |
| +template <typename TBase, typename TAlign, typename T> |
| +void* operator new(size_t expectedClassSize, |
|
bsalomon
2014/10/08 19:51:44
I think you'll need an operator delete. We don't c
Chris Dalton
2014/10/09 00:06:59
Done.
|
| + GrTBaseList<TBase, TAlign>& list, |
| + const GrTBaseListAllocDesc<T>& desc) { |
| + SkASSERT(expectedClassSize == sizeof(T)); |
| + SkASSERT(desc.sizeInBytes() >= (int)sizeof(T)); |
| + return list.alloc_back(desc); |
| +} |
| + |
| +template<typename TBase, typename TAlign> |
| +template<typename T> |
| +void* GrTBaseList<TBase, TAlign>::alloc_back(const GrTBaseListAllocDesc<T>& desc) { |
| + SkASSERT(desc.sizeInBytes() >= (int)sizeof(T)); |
| + const int totalLength = kHeaderLength + bytes_to_length(desc.sizeInBytes()); |
| + |
| + if (fTailBlock->fBack + totalLength > fTailBlock->fLength) { |
| + SkASSERT(NULL == fTailBlock->fNext.get()); |
| + MemBlock* next = SkNEW_ARGS(MemBlock, (SkTMax(2 * fTailBlock->fLength, totalLength))); |
| + fTailBlock->fNext.reset(next); |
| + fTailBlock = next; |
| + } |
| + |
| + ItemHeader* header = reinterpret_cast<ItemHeader*>(&fTailBlock->fBuffer[fTailBlock->fBack]); |
| + TBase* rawPtr = reinterpret_cast<T*>(&fTailBlock->fBuffer[fTailBlock->fBack + kHeaderLength]); |
| + fTailBlock->fBack += totalLength; |
| + |
| + header->fTotalLength = totalLength; |
| + |
| + fLastItem = rawPtr; |
| + |
| + // FIXME: We currently require that the base and subclass share the same start address. |
| + // This is not required by the C++ spec, and is likely to not be true in the case of |
| + // multiple inheritance or a base class that doesn't have virtual methods (when the |
| + // subclass does). It would be ideal to find a more robust solution that comes at no |
| + // extra cost to performance or code generality. |
| + SkDEBUGCODE(void* baseAddr = fLastItem; |
| + void* subclassAddr = rawPtr); |
| + SkASSERT(baseAddr == subclassAddr); |
| + |
| + return rawPtr; |
| +} |
| + |
| +template<typename TBase, typename TAlign> |
| +class GrTBaseList<TBase, TAlign>::Iter { |
| +public: |
| + Iter(GrTBaseList& list) : fBlock(&list.fHeadBlock), fPosition(0), fItem(NULL) {} |
| + |
| + bool next() { |
| + if (fPosition >= fBlock->fBack) { |
| + SkASSERT(fPosition == fBlock->fBack); |
| + if (NULL == fBlock->fNext.get()) { |
| + return false; |
| + } |
| + SkASSERT(0 != fBlock->fNext->fBack); |
| + fBlock = fBlock->fNext.get(); |
| + fPosition = 0; |
| + } |
| + |
| + ItemHeader* header = reinterpret_cast<ItemHeader*>(&fBlock->fBuffer[fPosition]); |
| + fItem = reinterpret_cast<TBase*>(&fBlock->fBuffer[fPosition + kHeaderLength]); |
| + fPosition += header->fTotalLength; |
| + return true; |
| + } |
| + |
| + TBase* operator->() const { |
| + SkASSERT(fItem); |
| + return fItem; |
| + } |
| + |
| +private: |
| + MemBlock* fBlock; |
| + int fPosition; |
| + TBase* fItem; |
| +}; |
| + |
| +template<typename TBase, typename TAlign> |
| +void GrTBaseList<TBase, TAlign>::reset() { |
| + Iter iter(*this); |
| + while (iter.next()) { |
| + iter->~TBase(); |
| + } |
| + fHeadBlock.fBack = 0; |
| + fHeadBlock.fNext.free(); |
| + fTailBlock = &fHeadBlock; |
| + fLastItem = NULL; |
| +} |
| + |
| +#define GrNEW_APPEND_TO_TBASELIST(list, type_name, args) \ |
| + (new (list, GrTBaseListAllocDesc<type_name>()) type_name args) |
| + |
| +#define GrNEW_SIZE_APPEND_TO_TBASELIST(list, size, type_name, args) \ |
| + (new (list, GrTBaseListAllocDesc<type_name>(size)) type_name args) |
| + |
| +#endif |