|  | /* | 
|  | * 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 GrTRecorder_DEFINED | 
|  | #define GrTRecorder_DEFINED | 
|  |  | 
|  | #include "SkTypes.h" | 
|  |  | 
|  | template<typename TBase, typename TAlign> class GrTRecorder; | 
|  | template<typename TItem> struct GrTRecorderAllocWrapper; | 
|  |  | 
|  | /** | 
|  | * Records a list of items with a common base type, optional associated data, and | 
|  | * permanent memory addresses. | 
|  | * | 
|  | * This class preallocates its own chunks of memory for hosting objects, so new items 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_RECORDER(recorder, SubclassName, (args)) | 
|  | *     GrNEW_APPEND_WITH_DATA_TO_RECORDER(recorder, SubclassName, (args), sizeOfData) | 
|  | * | 
|  | * Upon reset or delete, the items are destructed in the same order they were received, | 
|  | * not reverse (stack) order. | 
|  | * | 
|  | * @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 list. | 
|  | */ | 
|  | template<typename TBase, typename TAlign> class GrTRecorder : SkNoncopyable { | 
|  | public: | 
|  | class Iter; | 
|  | class ReverseIter; | 
|  |  | 
|  | /** | 
|  | * Create a recorder. | 
|  | * | 
|  | * @param initialSizeInBytes  The amount of memory reserved by the recorder initially, | 
|  | and after calls to reset(). | 
|  | */ | 
|  | GrTRecorder(int initialSizeInBytes) | 
|  | : fHeadBlock(MemBlock::Alloc(LengthOf(initialSizeInBytes), nullptr)), | 
|  | fTailBlock(fHeadBlock), | 
|  | fLastItem(nullptr) {} | 
|  |  | 
|  | ~GrTRecorder() { | 
|  | this->reset(); | 
|  | MemBlock::Free(fHeadBlock); | 
|  | } | 
|  |  | 
|  | bool empty() { return !fLastItem; } | 
|  |  | 
|  | TBase& back() { | 
|  | SkASSERT(!this->empty()); | 
|  | return *reinterpret_cast<TBase*>(fLastItem); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Removes and destroys the last block added to the recorder. It may not be called when the | 
|  | * recorder is empty. | 
|  | */ | 
|  | void pop_back(); | 
|  |  | 
|  | /** | 
|  | * Destruct all items in the list and reset to empty. | 
|  | */ | 
|  | void reset(); | 
|  |  | 
|  | /** | 
|  | * Retrieve the extra data associated with an item that was allocated using | 
|  | * GrNEW_APPEND_WITH_DATA_TO_RECORDER(). | 
|  | * | 
|  | * @param item  The item whose data to retrieve. The pointer must be of the same type | 
|  | *              that was allocated initally; it can't be a pointer to a base class. | 
|  | * | 
|  | * @return The item's associated data. | 
|  | */ | 
|  | template<typename TItem> static const void* GetDataForItem(const TItem* item) { | 
|  | const TAlign* ptr = reinterpret_cast<const TAlign*>(item); | 
|  | return &ptr[length_of<TItem>::kValue]; | 
|  | } | 
|  | template<typename TItem> static void* GetDataForItem(TItem* item) { | 
|  | TAlign* ptr = reinterpret_cast<TAlign*>(item); | 
|  | return &ptr[length_of<TItem>::kValue]; | 
|  | } | 
|  |  | 
|  | private: | 
|  | template<typename TItem> struct length_of { | 
|  | enum { kValue = (sizeof(TItem) + sizeof(TAlign) - 1) / sizeof(TAlign) }; | 
|  | }; | 
|  | static int LengthOf(int bytes) { return (bytes + sizeof(TAlign) - 1) / sizeof(TAlign); } | 
|  |  | 
|  | struct Header { | 
|  | int fTotalLength; // The length of an entry including header, item, and data in TAligns. | 
|  | int fPrevLength;  // Same but for the previous entry. Used for iterating backwards. | 
|  | }; | 
|  | template<typename TItem> void* alloc_back(int dataLength); | 
|  |  | 
|  | struct MemBlock : SkNoncopyable { | 
|  | /** Allocates a new block and appends it to prev if not nullptr. The length param is in units | 
|  | of TAlign. */ | 
|  | static MemBlock* Alloc(int length, MemBlock* prev) { | 
|  | MemBlock* block = reinterpret_cast<MemBlock*>( | 
|  | sk_malloc_throw(sizeof(TAlign) * (length_of<MemBlock>::kValue + length))); | 
|  | block->fLength = length; | 
|  | block->fBack = 0; | 
|  | block->fNext = nullptr; | 
|  | block->fPrev = prev; | 
|  | if (prev) { | 
|  | SkASSERT(nullptr == prev->fNext); | 
|  | prev->fNext = block; | 
|  | } | 
|  | return block; | 
|  | } | 
|  |  | 
|  | // Frees from this block forward. Also adjusts prev block's next ptr. | 
|  | static void Free(MemBlock* block) { | 
|  | if (block && block->fPrev) { | 
|  | SkASSERT(block->fPrev->fNext == block); | 
|  | block->fPrev->fNext = nullptr; | 
|  | } | 
|  | while (block) { | 
|  | MemBlock* next = block->fNext; | 
|  | sk_free(block); | 
|  | block = next; | 
|  | } | 
|  | } | 
|  |  | 
|  | TAlign& operator [](int i) { | 
|  | return reinterpret_cast<TAlign*>(this)[length_of<MemBlock>::kValue + i]; | 
|  | } | 
|  |  | 
|  | int       fLength; // Length in units of TAlign of the block. | 
|  | int       fBack;   // Offset, in TAligns, to unused portion of the memory block. | 
|  | MemBlock* fNext; | 
|  | MemBlock* fPrev; | 
|  | }; | 
|  | MemBlock* const fHeadBlock; | 
|  | MemBlock* fTailBlock; | 
|  |  | 
|  | void*    fLastItem; // really a ptr to TBase | 
|  |  | 
|  | template<typename TItem> friend struct GrTRecorderAllocWrapper; | 
|  |  | 
|  | template <typename UBase, typename UAlign, typename UItem> | 
|  | friend void* operator new(size_t, GrTRecorder<UBase, UAlign>&, | 
|  | const GrTRecorderAllocWrapper<UItem>&); | 
|  |  | 
|  | friend class Iter; | 
|  | friend class ReverseIter; | 
|  | }; | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | template<typename TBase, typename TAlign> | 
|  | void GrTRecorder<TBase, TAlign>::pop_back() { | 
|  | SkASSERT(fLastItem); | 
|  | Header* header = reinterpret_cast<Header*>( | 
|  | reinterpret_cast<TAlign*>(fLastItem) - length_of<Header>::kValue); | 
|  | fTailBlock->fBack -= header->fTotalLength; | 
|  | reinterpret_cast<TBase*>(fLastItem)->~TBase(); | 
|  |  | 
|  | int lastItemLength = header->fPrevLength; | 
|  |  | 
|  | if (!header->fPrevLength) { | 
|  | // We popped the first entry in the recorder. | 
|  | SkASSERT(0 == fTailBlock->fBack); | 
|  | fLastItem = nullptr; | 
|  | return; | 
|  | } | 
|  | while (!fTailBlock->fBack) { | 
|  | // We popped the last entry in a block that isn't the head block. Move back a block but | 
|  | // don't free it since we'll probably grow into it shortly. | 
|  | fTailBlock = fTailBlock->fPrev; | 
|  | SkASSERT(fTailBlock); | 
|  | } | 
|  | fLastItem = &(*fTailBlock)[fTailBlock->fBack - lastItemLength + length_of<Header>::kValue]; | 
|  | } | 
|  |  | 
|  | template<typename TBase, typename TAlign> | 
|  | template<typename TItem> | 
|  | void* GrTRecorder<TBase, TAlign>::alloc_back(int dataLength) { | 
|  | // Find the header of the previous entry and get its length. We need to store that in the new | 
|  | // header for backwards iteration (pop_back()). | 
|  | int prevLength = 0; | 
|  | if (fLastItem) { | 
|  | Header* lastHeader = reinterpret_cast<Header*>( | 
|  | reinterpret_cast<TAlign*>(fLastItem) - length_of<Header>::kValue); | 
|  | prevLength = lastHeader->fTotalLength; | 
|  | } | 
|  |  | 
|  | const int totalLength = length_of<Header>::kValue + length_of<TItem>::kValue + dataLength; | 
|  |  | 
|  | // Check if there is room in the current block and if not walk to next (allocating if | 
|  | // necessary). Note that pop_back() and reset() can leave the recorder in a state where it | 
|  | // has preallocated blocks hanging off the tail that are currently unused. | 
|  | while (fTailBlock->fBack + totalLength > fTailBlock->fLength) { | 
|  | if (!fTailBlock->fNext) { | 
|  | fTailBlock = MemBlock::Alloc(SkTMax(2 * fTailBlock->fLength, totalLength), fTailBlock); | 
|  | } else { | 
|  | fTailBlock = fTailBlock->fNext; | 
|  | } | 
|  | SkASSERT(0 == fTailBlock->fBack); | 
|  | } | 
|  |  | 
|  | Header* header = reinterpret_cast<Header*>(&(*fTailBlock)[fTailBlock->fBack]); | 
|  | void* rawPtr = &(*fTailBlock)[fTailBlock->fBack + length_of<Header>::kValue]; | 
|  |  | 
|  | header->fTotalLength = totalLength; | 
|  | header->fPrevLength = prevLength; | 
|  | fLastItem = rawPtr; | 
|  | fTailBlock->fBack += totalLength; | 
|  |  | 
|  | // 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; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Iterates through a recorder from front to back. The initial state of the iterator is | 
|  | * to not have the front item loaded yet; next() must be called first. Usage model: | 
|  | * | 
|  | *    GrTRecorder<TBase, TAlign>::Iter iter(recorder); | 
|  | *    while (iter.next()) { | 
|  | *        iter->doSomething(); | 
|  | *    } | 
|  | */ | 
|  | template<typename TBase, typename TAlign> | 
|  | class GrTRecorder<TBase, TAlign>::Iter { | 
|  | public: | 
|  | Iter(GrTRecorder& recorder) : fBlock(recorder.fHeadBlock), fPosition(0), fItem(nullptr) {} | 
|  |  | 
|  | bool next() { | 
|  | while (fPosition >= fBlock->fBack) { | 
|  | SkASSERT(fPosition == fBlock->fBack); | 
|  | if (!fBlock->fNext) { | 
|  | return false; | 
|  | } | 
|  | fBlock = fBlock->fNext; | 
|  | fPosition = 0; | 
|  | } | 
|  |  | 
|  | Header* header = reinterpret_cast<Header*>(&(*fBlock)[fPosition]); | 
|  | fItem = reinterpret_cast<TBase*>(&(*fBlock)[fPosition + length_of<Header>::kValue]); | 
|  | 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; | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Iterates through a recorder in reverse, from back to front. This version mirrors "Iter", | 
|  | * so the initial state is to have recorder.back() loaded already. (Note that this will | 
|  | * assert if the recorder is empty.) Usage model: | 
|  | * | 
|  | *    GrTRecorder<TBase, TAlign>::ReverseIter reverseIter(recorder); | 
|  | *    do { | 
|  | *        reverseIter->doSomething(); | 
|  | *    } while (reverseIter.previous()); | 
|  | */ | 
|  | template<typename TBase, typename TAlign> | 
|  | class GrTRecorder<TBase, TAlign>::ReverseIter { | 
|  | public: | 
|  | ReverseIter(GrTRecorder& recorder) | 
|  | : fBlock(recorder.fTailBlock), | 
|  | fItem(&recorder.back()) { | 
|  | Header* lastHeader = reinterpret_cast<Header*>( | 
|  | reinterpret_cast<TAlign*>(fItem) - length_of<Header>::kValue); | 
|  | fPosition = fBlock->fBack - lastHeader->fTotalLength; | 
|  | } | 
|  |  | 
|  | bool previous() { | 
|  | Header* header = reinterpret_cast<Header*>(&(*fBlock)[fPosition]); | 
|  |  | 
|  | while (0 == fPosition) { | 
|  | if (!fBlock->fPrev) { | 
|  | // We've reached the front of the recorder. | 
|  | return false; | 
|  | } | 
|  | fBlock = fBlock->fPrev; | 
|  | fPosition = fBlock->fBack; | 
|  | } | 
|  |  | 
|  | fPosition -= header->fPrevLength; | 
|  | SkASSERT(fPosition >= 0); | 
|  |  | 
|  | fItem = reinterpret_cast<TBase*>(&(*fBlock)[fPosition + length_of<Header>::kValue]); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | TBase* get() const { return fItem; } | 
|  | TBase* operator->() const { return this->get(); } | 
|  |  | 
|  | private: | 
|  | MemBlock* fBlock; | 
|  | int       fPosition; | 
|  | TBase*    fItem; | 
|  | }; | 
|  |  | 
|  | template<typename TBase, typename TAlign> | 
|  | void GrTRecorder<TBase, TAlign>::reset() { | 
|  | Iter iter(*this); | 
|  | while (iter.next()) { | 
|  | iter->~TBase(); | 
|  | } | 
|  |  | 
|  | // Assume the next time this recorder fills up it will use approximately the same | 
|  | // amount of space as last time. Leave enough space for up to ~50% growth; free | 
|  | // everything else. | 
|  | if (fTailBlock->fBack <= fTailBlock->fLength / 2) { | 
|  | MemBlock::Free(fTailBlock->fNext); | 
|  | } else if (fTailBlock->fNext) { | 
|  | MemBlock::Free(fTailBlock->fNext->fNext); | 
|  | fTailBlock->fNext->fNext = nullptr; | 
|  | } | 
|  |  | 
|  | for (MemBlock* block = fHeadBlock; block; block = block->fNext) { | 
|  | block->fBack = 0; | 
|  | } | 
|  |  | 
|  | fTailBlock = fHeadBlock; | 
|  | fLastItem = nullptr; | 
|  | } | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | template<typename TItem> struct GrTRecorderAllocWrapper { | 
|  | GrTRecorderAllocWrapper() : fDataLength(0) {} | 
|  |  | 
|  | template <typename TBase, typename TAlign> | 
|  | GrTRecorderAllocWrapper(const GrTRecorder<TBase, TAlign>&, int sizeOfData) | 
|  | : fDataLength(GrTRecorder<TBase, TAlign>::LengthOf(sizeOfData)) {} | 
|  |  | 
|  | const int fDataLength; | 
|  | }; | 
|  |  | 
|  | template <typename TBase, typename TAlign, typename TItem> | 
|  | void* operator new(size_t size, GrTRecorder<TBase, TAlign>& recorder, | 
|  | const GrTRecorderAllocWrapper<TItem>& wrapper) { | 
|  | SkASSERT(size == sizeof(TItem)); | 
|  | return recorder.template alloc_back<TItem>(wrapper.fDataLength); | 
|  | } | 
|  |  | 
|  | template <typename TBase, typename TAlign, typename TItem> | 
|  | void operator delete(void*, GrTRecorder<TBase, TAlign>&, const GrTRecorderAllocWrapper<TItem>&) { | 
|  | // We only provide an operator delete to work around compiler warnings that can come | 
|  | // up for an unmatched operator new when compiling with exceptions. | 
|  | SK_ABORT("Invalid Operation"); | 
|  | } | 
|  |  | 
|  | #define GrNEW_APPEND_TO_RECORDER(recorder, type_name, args) \ | 
|  | (new (recorder, GrTRecorderAllocWrapper<type_name>()) type_name args) | 
|  |  | 
|  | #define GrNEW_APPEND_WITH_DATA_TO_RECORDER(recorder, type_name, args, size_of_data) \ | 
|  | (new (recorder, GrTRecorderAllocWrapper<type_name>(recorder, size_of_data)) type_name args) | 
|  |  | 
|  | #endif |