// Forward declarations for the iterators used by SkTBlockList
using IndexFn = int (*)(const SkBlockAllocator::Block*);
using NextFn = int (*)(const SkBlockAllocator::Block*, int); template<typename T, typename B> using ItemFn = T (*)(B*, int); template <typename T, bool Forward, boolConst, IndexFn Start, IndexFn End, NextFn Next,
ItemFn<T, typename std::conditional<Const, const SkBlockAllocator::Block,
SkBlockAllocator::Block>::type> Resolve> class BlockIndexIterator;
// Run dtor for the popped item int releaseIndex = Last(block);
GetItem(block, releaseIndex).~T();
if (releaseIndex == First(block)) {
fAllocator->releaseBlock(block);
} else { // Since this always follows LIFO, the block should always be able to release the memory
SkAssertResult(block->release(releaseIndex, releaseIndex + sizeof(T)));
block->setMetadata(Decrement(block, releaseIndex));
}
/** *Removesalladdeditems.
*/ void reset() { // Invoke destructors in reverse order if not trivially destructible if constexpr (!std::is_trivially_destructible<T>::value) { for (T& t : this->ritems()) {
t.~T();
}
}
fAllocator->reset();
}
/** *Returnstheitemcount.
*/ int count() const { #ifdef SK_DEBUG // Confirm total count matches sum of block counts int count = 0; for (constauto* b :fAllocator->blocks()) { if (b->metadata() == 0) { continue; // skip empty
}
count += (sizeof(T) + Last(b) - First(b)) / sizeof(T);
}
SkASSERT(count == fAllocator->metadata()); #endif return fAllocator->metadata();
}
// Iterate over blocks until we find the one that contains i. for (auto* b : fAllocator->blocks()) { if (b->metadata() == 0) { continue; // skip empty
}
int start = First(b); int end = Last(b) + sizeof(T); // exclusive int index = start + i * sizeof(T); if (index < end) { return GetItem(b, index);
} else {
i -= (end - start) / sizeof(T);
}
}
SkUNREACHABLE;
} const T& item(int i) const { returnconst_cast<SkTBlockList*>(this)->item(i);
}
private: // Let other SkTBlockLists have access (only ever used when T and S are the same but you // cannot have partial specializations declared as a friend...) template<typename S, int N> friendclass SkTBlockList; friendclass TBlockListTestAccess; // for fAllocator
static T& GetItem(SkBlockAllocator::Block* block, int index) { return *static_cast<T*>(block->ptr(index));
} staticconst T& GetItem(const SkBlockAllocator::Block* block, int index) { return *static_cast<const T*>(block->ptr(index));
} staticint First(const SkBlockAllocator::Block* b) { return b->firstAlignedOffset<alignof(T)>();
} staticint Last(const SkBlockAllocator::Block* b) { return b->metadata();
} staticint Increment(const SkBlockAllocator::Block* b, int index) { return index + sizeof(T);
} staticint Decrement(const SkBlockAllocator::Block* b, int index) { return index - sizeof(T);
}
void* pushItem() { // 'template' required because fAllocator is a template, calling a template member auto br = fAllocator->template allocate<alignof(T)>(sizeof(T));
SkASSERT(br.fStart == br.fAlignedOffset ||
br.fAlignedOffset == First(fAllocator->currentBlock()));
br.fBlock->setMetadata(br.fAlignedOffset);
fAllocator->setMetadata(fAllocator->metadata() + 1); return br.fBlock->ptr(br.fAlignedOffset);
}
// N represents the number of items, whereas SkSBlockAllocator takes total bytes, so must // account for the block allocator's size too. // // This class uses the SkBlockAllocator's metadata to track total count of items, and per-block // metadata to track the index of the last allocated item within each block.
SkSBlockAllocator<StartingSize> fAllocator;
public:
using Iter = BlockIndexIterator<T&, true, false, &First, &Last, &Increment, &GetItem>;
using CIter = BlockIndexIterator<const T&, true, true, &First, &Last, &Increment, &GetItem>;
using RIter = BlockIndexIterator<T&, false, false, &Last, &First, &Decrement, &GetItem>;
using CRIter = BlockIndexIterator<const T&, false, true, &Last, &First, &Decrement, &GetItem>;
// Iterate from newest to oldest using a for-range loop.
RIter ritems() { return RIter(fAllocator.allocator()); }
CRIter ritems() const { return CRIter(fAllocator.allocator()); }
};
template <typename T, int SI1> template <int SI2> void SkTBlockList<T, SI1>::concat(SkTBlockList<T, SI2>&& other) { // Optimize the common case where the list to append only has a single item if (other.empty()) { return;
} elseif (other.count() == 1) {
this->push_back(other.back());
other.pop_back(); return;
}
// Manually move all items in other's head block into this list; all heap blocks from 'other' // will be appended to the block linked list (no per-item moves needed then). int headItemCount = 0;
SkBlockAllocator::Block* headBlock = other.fAllocator->headBlock();
SkDEBUGCODE(int oldCount = this->count();) if (headBlock->metadata() > 0) { int headStart = First(headBlock); int headEnd = Last(headBlock) + sizeof(T); // exclusive
headItemCount = (headEnd - headStart) / sizeof(T); int avail = fAllocator->currentBlock()->template avail<alignof(T)>() / sizeof(T); if (headItemCount > avail) { // Make sure there is extra room for the items beyond what's already avail. Use the // kIgnoreGrowthPolicy_Flag to make this reservation as tight as possible since // 'other's heap blocks will be appended after it and any extra space is wasted.
fAllocator->template reserve<alignof(T)>((headItemCount - avail) * sizeof(T),
SkBlockAllocator::kIgnoreExistingBytes_Flag |
SkBlockAllocator::kIgnoreGrowthPolicy_Flag);
}
if constexpr (std::is_trivially_copy_constructible<T>::value) { // memcpy all items at once (or twice between current and reserved space).
SkASSERT(std::is_trivially_destructible<T>::value); auto copy = [](SkBlockAllocator::Block* src, int start, SkBlockAllocator* dst, int n) { auto target = dst->template allocate<alignof(T)>(n * sizeof(T));
memcpy(target.fBlock->ptr(target.fAlignedOffset), src->ptr(start), n * sizeof(T));
target.fBlock->setMetadata(target.fAlignedOffset + (n - 1) * sizeof(T));
};
if (avail > 0) { // Copy 0 to avail items into existing tail block
copy(headBlock, headStart, fAllocator.allocator(), std::min(headItemCount, avail));
} if (headItemCount > avail) { // Copy (head count - avail) into the extra reserved space
copy(headBlock, headStart + avail * sizeof(T),
fAllocator.allocator(), headItemCount - avail);
}
fAllocator->setMetadata(fAllocator->metadata() + headItemCount);
} else { // Move every item over one at a time for (int i = headStart; i < headEnd; i += sizeof(T)) {
T& toMove = GetItem(headBlock, i);
this->push_back(std::move(toMove)); // Anything of interest should have been moved, but run this since T isn't // a trusted type.
toMove.~T(); // NOLINT(bugprone-use-after-move): calling dtor always allowed
}
}
other.fAllocator->releaseBlock(headBlock);
}
// other's head block must have been fully copied since it cannot be stolen
SkASSERT(other.fAllocator->headBlock()->metadata() == 0 &&
fAllocator->metadata() == oldCount + headItemCount);
fAllocator->stealHeapBlocks(other.fAllocator.allocator());
fAllocator->setMetadata(fAllocator->metadata() +
(other.fAllocator->metadata() - headItemCount));
other.fAllocator->setMetadata(0);
}
/** *BlockIndexIteratorprovidesareusableiteratortemplateforcollectionsbuiltontopofa *SkBlockAllocator,whereeachitemisofthesametype,andtheindextoanitemcanbeiterated *overinaknownmanner.Itsupportsconstandnon-const,andforwardandreverse,assumingit's *providedwithproperfunctionsforstarting,ending,andadvancing.
*/ template <typename T, // The element type (including any modifiers) bool Forward, // Are indices within a block increasing or decreasing with iteration? boolConst, // Whether or not T is const
IndexFn Start, // Returns the index of the first valid item in a block
IndexFn End, // Returns the index of the last valid item (so it is inclusive)
NextFn Next, // Returns the next index given the current index
ItemFn<T, typename std::conditional<Const, const SkBlockAllocator::Block,
SkBlockAllocator::Block>::type> Resolve> class BlockIndexIterator {
using BlockIter = typename SkBlockAllocator::BlockIter<Forward, Const>;
public:
BlockIndexIterator(BlockIter iter) : fBlockIter(iter) {}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.