/* * 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 "include/gpu/GrTypes.h" #include "include/private/SkTLogic.h" #include "src/core/SkArenaAlloc.h" /** * Records a list of items with a common base type, optional associated data, and * permanent memory addresses. It supports forward iteration. * * This class allocates space for the stored items and associated data in a SkArenaAlloc. * There is an overhead of 1 pointer for each stored item. * * 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. It is assumed that the items are * trivially destructable or that TBase has a virtual destructor as ~TBase() * is called to destroy the items. */ template class GrTRecorder { private: template class IterImpl; public: using iterator = IterImpl; using const_iterator = IterImpl; /** * Create a recorder. * * @param initialSizeInBytes The amount of memory reserved by the recorder initially, and after calls to reset(). */ explicit GrTRecorder(size_t initialSizeInBytes) : fArena(initialSizeInBytes) {} GrTRecorder(const GrTRecorder&) = delete; GrTRecorder& operator=(const GrTRecorder&) = delete; ~GrTRecorder() { this->reset(); } bool empty() { return !SkToBool(fTail); } /** The last item. Must not be empty. */ TBase& back() { SkASSERT(!this->empty()); return *fTail->get(); } /** Forward mutable iteration */ iterator begin() { return iterator(fHead); } iterator end() { return iterator(nullptr); } /** Forward const iteration */ const_iterator begin() const { return const_iterator(fHead); } const_iterator end() const { return const_iterator(nullptr); } /** Destruct all items in the list and reset to empty. Frees memory allocated from arena. */ void reset(); /** * Emplace a new TItem (which derives from TBase) in the recorder. This requires equivalence * between reinterpret_cast and static_cast when operating on TItem*. * Multiple inheritance may make this not true. It is runtime asserted. */ template TItem& emplace(Args&&... args) { return this->emplaceWithData(0, std::forward(args)...); } /** * Emplace a new TItem (which derives from TBase) in the recorder with extra data space. The * extra data immediately follows the stored item with no extra alignment. E.g., * void* extraData = &recorder->emplaceWithData(dataSize, ...) + 1; * * This requires equivalence between reinterpret_cast and static_cast when * operating on TItem*. Multiple inheritance may make this not true. It is runtime asserted. */ template SK_WHEN((std::is_base_of::value), TItem&) emplaceWithData(size_t extraDataSize, Args... args); private: struct Header { Header* fNext = nullptr; // We always store the T immediately after the header (and ensure proper alignment). See // emplaceWithData() implementation. TBase* get() const { return reinterpret_cast(const_cast(this) + 1); } }; SkArenaAlloc fArena; Header* fHead = nullptr; Header* fTail = nullptr; }; //////////////////////////////////////////////////////////////////////////////// template template inline SK_WHEN((std::is_base_of::value), TItem&) GrTRecorder::emplaceWithData(size_t extraDataSize, Args... args) { static constexpr size_t kTAlign = alignof(TItem); static constexpr size_t kHeaderAlign = alignof(Header); static constexpr size_t kAllocAlign = kTAlign > kHeaderAlign ? kTAlign : kHeaderAlign; static constexpr size_t kTItemOffset = GrAlignTo(sizeof(Header), kAllocAlign); // We're assuming if we back up from kItemOffset by sizeof(Header) we will still be aligned. static_assert(sizeof(Header) % alignof(Header) == 0); const size_t totalSize = kTItemOffset + sizeof(TItem) + extraDataSize; auto alloc = reinterpret_cast(fArena.makeBytesAlignedTo(totalSize, kAllocAlign)); Header* header = new (alloc + kTItemOffset - sizeof(Header)) Header(); if (fTail) { fTail->fNext = header; } fTail = header; if (!fHead) { fHead = header; } auto* item = new (alloc + kTItemOffset) TItem(std::forward(args)...); // We require that we can reinterpret_cast between TBase* and TItem*. Could not figure out how // to statically assert this. See proposal for std::is_initial_base_of here: // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0466r0.pdf SkASSERT(reinterpret_cast(item) == reinterpret_cast(static_cast(item))); return *item; } template inline void GrTRecorder::reset() { for (auto& i : *this) { i.~TBase(); } static_assert(std::is_trivially_destructible
::value); fHead = fTail = nullptr; fArena.reset(); } /** * Iterates through a recorder front-to-back, const or not. */ template template class GrTRecorder::IterImpl { private: using T = typename std::conditional::type; public: IterImpl() = default; IterImpl operator++() { fCurr = fCurr->fNext; return *this; } IterImpl operator++(int) { auto old = fCurr; fCurr = fCurr->fNext; return {old}; } T& operator*() const { return *fCurr->get(); } T* operator->() const { return fCurr->get(); } bool operator==(const IterImpl& that) const { return fCurr == that.fCurr; } bool operator!=(const IterImpl& that) const { return !(*this == that); } private: IterImpl(Header* curr) : fCurr(curr) {} Header* fCurr = nullptr; friend class GrTRecorder; // To construct from Header. }; #endif