| Index: src/gpu/GrTBaseList.h
|
| diff --git a/src/gpu/GrTBaseList.h b/src/gpu/GrTBaseList.h
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..fbaab6f4233a8341367ba11cbc8dc20722919561
|
| --- /dev/null
|
| +++ b/src/gpu/GrTBaseList.h
|
| @@ -0,0 +1,220 @@
|
| +/*
|
| + * 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.
|
| + *
|
| + * 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().
|
| + *
|
| + * To create a new item and append it to the back of the list, use the following macros:
|
| + *
|
| + * GrNEW_APPEND_TO_TBASELIST(list, SubclassName, (args))
|
| + * GrNEW_APPEND_WITH_DATA_TO_TBASELIST(list, SubclassName, (args), extraBytesToAllocate)
|
| + *
|
| + * @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 runtime asserted
|
| + * in debug builds.
|
| + *
|
| + * @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 {
|
| +public:
|
| + class Iter;
|
| +
|
| + /**
|
| + * Create a list for items with a common base type.
|
| + *
|
| + * @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 extraBytesToAllocate = 0)
|
| + : fBytesToAllocate(sizeof(T) + extraBytesToAllocate) {
|
| + SkASSERT(extraBytesToAllocate >= 0);
|
| + }
|
| + int bytesToAllocate() const { return fBytesToAllocate; }
|
| +private:
|
| + const int fBytesToAllocate;
|
| +};
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +
|
| +template <typename TBase, typename TAlign, typename T>
|
| +void* operator new(size_t expectedClassSize,
|
| + GrTBaseList<TBase, TAlign>& list,
|
| + const GrTBaseListAllocDesc<T>& desc) {
|
| + SkASSERT(expectedClassSize == sizeof(T));
|
| + return list.alloc_back(desc);
|
| +}
|
| +
|
| +// Skia doesn't use C++ exceptions but it may be compiled with them enabled. Having an op delete
|
| +// to match the op new silences warnings about missing op delete when a constructor throws an
|
| +// exception.
|
| +template <typename TBase, typename TAlign, typename T>
|
| +void operator delete(void*, GrTBaseList<TBase, TAlign>&, const GrTBaseListAllocDesc<T>&) {
|
| + SK_CRASH();
|
| +}
|
| +
|
| +template<typename TBase, typename TAlign>
|
| +template<typename T>
|
| +void* GrTBaseList<TBase, TAlign>::alloc_back(const GrTBaseListAllocDesc<T>& desc) {
|
| + SkASSERT(desc.bytesToAllocate() >= (int)sizeof(T));
|
| + const int totalLength = kHeaderLength + bytes_to_length(desc.bytesToAllocate());
|
| +
|
| + 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* get() const {
|
| + SkASSERT(fItem);
|
| + return fItem;
|
| + }
|
| +
|
| + TBase* operator->() const { return this->get(); }
|
| +
|
| +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_APPEND_WITH_DATA_TO_TBASELIST(list, type_name, args, extra_bytes_to_allocate) \
|
| + (new (list, GrTBaseListAllocDesc<type_name>(extra_bytes_to_allocate)) type_name args)
|
| +
|
| +#endif
|
|
|