| /* | 
 |  * 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 |