1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2010 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #ifndef SkTBlockList_DEFINED
9*c8dee2aaSAndroid Build Coastguard Worker #define SkTBlockList_DEFINED
10*c8dee2aaSAndroid Build Coastguard Worker
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAssert.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkDebug.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkBlockAllocator.h"
15*c8dee2aaSAndroid Build Coastguard Worker
16*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
17*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
18*c8dee2aaSAndroid Build Coastguard Worker #include <type_traits>
19*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
20*c8dee2aaSAndroid Build Coastguard Worker
21*c8dee2aaSAndroid Build Coastguard Worker // Forward declarations for the iterators used by SkTBlockList
22*c8dee2aaSAndroid Build Coastguard Worker using IndexFn = int (*)(const SkBlockAllocator::Block*);
23*c8dee2aaSAndroid Build Coastguard Worker using NextFn = int (*)(const SkBlockAllocator::Block*, int);
24*c8dee2aaSAndroid Build Coastguard Worker template<typename T, typename B> using ItemFn = T (*)(B*, int);
25*c8dee2aaSAndroid Build Coastguard Worker template <typename T, bool Forward, bool Const, IndexFn Start, IndexFn End, NextFn Next,
26*c8dee2aaSAndroid Build Coastguard Worker ItemFn<T, typename std::conditional<Const, const SkBlockAllocator::Block,
27*c8dee2aaSAndroid Build Coastguard Worker SkBlockAllocator::Block>::type> Resolve>
28*c8dee2aaSAndroid Build Coastguard Worker class BlockIndexIterator;
29*c8dee2aaSAndroid Build Coastguard Worker
30*c8dee2aaSAndroid Build Coastguard Worker /**
31*c8dee2aaSAndroid Build Coastguard Worker * SkTBlockList manages dynamic storage for instances of T, reserving fixed blocks such that
32*c8dee2aaSAndroid Build Coastguard Worker * allocation is amortized across every N instances. In this way it is a hybrid of an array-based
33*c8dee2aaSAndroid Build Coastguard Worker * vector and a linked-list. T can be any type and non-trivial destructors are automatically
34*c8dee2aaSAndroid Build Coastguard Worker * invoked when the SkTBlockList is destructed. The addresses of instances are guaranteed
35*c8dee2aaSAndroid Build Coastguard Worker * not to move except when a list is concatenated to another.
36*c8dee2aaSAndroid Build Coastguard Worker *
37*c8dee2aaSAndroid Build Coastguard Worker * The collection supports storing a templated number of elements inline before heap-allocated
38*c8dee2aaSAndroid Build Coastguard Worker * blocks are made to hold additional instances. By default, the heap blocks are sized to hold the
39*c8dee2aaSAndroid Build Coastguard Worker * same number of items as the inline block. A common pattern is to have the inline size hold only
40*c8dee2aaSAndroid Build Coastguard Worker * a small number of items for the common case and then allocate larger blocks when needed.
41*c8dee2aaSAndroid Build Coastguard Worker *
42*c8dee2aaSAndroid Build Coastguard Worker * If the size of a collection is N, and its block size is B, the complexity of the common
43*c8dee2aaSAndroid Build Coastguard Worker * operations are:
44*c8dee2aaSAndroid Build Coastguard Worker * - push_back()/emplace_back(): O(1), with malloc O(B)
45*c8dee2aaSAndroid Build Coastguard Worker * - pop_back(): O(1), with free O(B)
46*c8dee2aaSAndroid Build Coastguard Worker * - front()/back(): O(1)
47*c8dee2aaSAndroid Build Coastguard Worker * - reset(): O(N) for non-trivial types, O(N/B) for trivial types
48*c8dee2aaSAndroid Build Coastguard Worker * - concat(): O(B)
49*c8dee2aaSAndroid Build Coastguard Worker * - random access: O(N/B)
50*c8dee2aaSAndroid Build Coastguard Worker * - iteration: O(1) at each step
51*c8dee2aaSAndroid Build Coastguard Worker *
52*c8dee2aaSAndroid Build Coastguard Worker * These characteristics make it well suited for allocating items in a LIFO ordering, or otherwise
53*c8dee2aaSAndroid Build Coastguard Worker * acting as a stack, or simply using it as a typed allocator.
54*c8dee2aaSAndroid Build Coastguard Worker */
55*c8dee2aaSAndroid Build Coastguard Worker template <typename T, int StartingItems = 1>
56*c8dee2aaSAndroid Build Coastguard Worker class SkTBlockList {
57*c8dee2aaSAndroid Build Coastguard Worker public:
58*c8dee2aaSAndroid Build Coastguard Worker /**
59*c8dee2aaSAndroid Build Coastguard Worker * Create an list that defaults to using StartingItems as heap increment and the
60*c8dee2aaSAndroid Build Coastguard Worker * kFixed growth policy (e.g. all allocations will match StartingItems).
61*c8dee2aaSAndroid Build Coastguard Worker */
SkTBlockList()62*c8dee2aaSAndroid Build Coastguard Worker SkTBlockList() : SkTBlockList(SkBlockAllocator::GrowthPolicy::kFixed) {}
63*c8dee2aaSAndroid Build Coastguard Worker
64*c8dee2aaSAndroid Build Coastguard Worker /**
65*c8dee2aaSAndroid Build Coastguard Worker * Create an list that defaults to using StartingItems as the heap increment, but with
66*c8dee2aaSAndroid Build Coastguard Worker * the defined growth policy.
67*c8dee2aaSAndroid Build Coastguard Worker */
SkTBlockList(SkBlockAllocator::GrowthPolicy policy)68*c8dee2aaSAndroid Build Coastguard Worker explicit SkTBlockList(SkBlockAllocator::GrowthPolicy policy)
69*c8dee2aaSAndroid Build Coastguard Worker : SkTBlockList(StartingItems, policy) {}
70*c8dee2aaSAndroid Build Coastguard Worker
71*c8dee2aaSAndroid Build Coastguard Worker /**
72*c8dee2aaSAndroid Build Coastguard Worker * Create an list.
73*c8dee2aaSAndroid Build Coastguard Worker *
74*c8dee2aaSAndroid Build Coastguard Worker * @param itemsPerBlock the number of items to allocate at once
75*c8dee2aaSAndroid Build Coastguard Worker * @param policy the growth policy for subsequent blocks of items
76*c8dee2aaSAndroid Build Coastguard Worker */
77*c8dee2aaSAndroid Build Coastguard Worker explicit SkTBlockList(int itemsPerBlock,
78*c8dee2aaSAndroid Build Coastguard Worker SkBlockAllocator::GrowthPolicy policy =
79*c8dee2aaSAndroid Build Coastguard Worker SkBlockAllocator::GrowthPolicy::kFixed)
80*c8dee2aaSAndroid Build Coastguard Worker : fAllocator(policy,
81*c8dee2aaSAndroid Build Coastguard Worker SkBlockAllocator::BlockOverhead<alignof(T)>() + sizeof(T)*itemsPerBlock) {}
82*c8dee2aaSAndroid Build Coastguard Worker
~SkTBlockList()83*c8dee2aaSAndroid Build Coastguard Worker ~SkTBlockList() { this->reset(); }
84*c8dee2aaSAndroid Build Coastguard Worker
85*c8dee2aaSAndroid Build Coastguard Worker /**
86*c8dee2aaSAndroid Build Coastguard Worker * Adds an item and returns it.
87*c8dee2aaSAndroid Build Coastguard Worker *
88*c8dee2aaSAndroid Build Coastguard Worker * @return the added item.
89*c8dee2aaSAndroid Build Coastguard Worker */
push_back()90*c8dee2aaSAndroid Build Coastguard Worker T& push_back() {
91*c8dee2aaSAndroid Build Coastguard Worker return *new (this->pushItem()) T;
92*c8dee2aaSAndroid Build Coastguard Worker }
push_back(const T & t)93*c8dee2aaSAndroid Build Coastguard Worker T& push_back(const T& t) {
94*c8dee2aaSAndroid Build Coastguard Worker return *new (this->pushItem()) T(t);
95*c8dee2aaSAndroid Build Coastguard Worker }
push_back(T && t)96*c8dee2aaSAndroid Build Coastguard Worker T& push_back(T&& t) {
97*c8dee2aaSAndroid Build Coastguard Worker return *new (this->pushItem()) T(std::move(t));
98*c8dee2aaSAndroid Build Coastguard Worker }
99*c8dee2aaSAndroid Build Coastguard Worker
100*c8dee2aaSAndroid Build Coastguard Worker template <typename... Args>
emplace_back(Args &&...args)101*c8dee2aaSAndroid Build Coastguard Worker T& emplace_back(Args&&... args) {
102*c8dee2aaSAndroid Build Coastguard Worker return *new (this->pushItem()) T(std::forward<Args>(args)...);
103*c8dee2aaSAndroid Build Coastguard Worker }
104*c8dee2aaSAndroid Build Coastguard Worker
105*c8dee2aaSAndroid Build Coastguard Worker /**
106*c8dee2aaSAndroid Build Coastguard Worker * Move all items from 'other' to the end of this collection. When this returns, 'other' will
107*c8dee2aaSAndroid Build Coastguard Worker * be empty. Items in 'other' may be moved as part of compacting the pre-allocated start of
108*c8dee2aaSAndroid Build Coastguard Worker * 'other' into this list (using T's move constructor or memcpy if T is trivially copyable), but
109*c8dee2aaSAndroid Build Coastguard Worker * this is O(StartingItems) and not O(N). All other items are concatenated in O(1).
110*c8dee2aaSAndroid Build Coastguard Worker */
111*c8dee2aaSAndroid Build Coastguard Worker template <int SI>
112*c8dee2aaSAndroid Build Coastguard Worker void concat(SkTBlockList<T, SI>&& other);
113*c8dee2aaSAndroid Build Coastguard Worker
114*c8dee2aaSAndroid Build Coastguard Worker /**
115*c8dee2aaSAndroid Build Coastguard Worker * Allocate, if needed, space to hold N more Ts before another malloc will occur.
116*c8dee2aaSAndroid Build Coastguard Worker */
reserve(int n)117*c8dee2aaSAndroid Build Coastguard Worker void reserve(int n) {
118*c8dee2aaSAndroid Build Coastguard Worker int avail = fAllocator->currentBlock()->template avail<alignof(T)>() / sizeof(T);
119*c8dee2aaSAndroid Build Coastguard Worker if (n > avail) {
120*c8dee2aaSAndroid Build Coastguard Worker int reserved = n - avail;
121*c8dee2aaSAndroid Build Coastguard Worker // Don't consider existing bytes since we've already determined how to split the N items
122*c8dee2aaSAndroid Build Coastguard Worker fAllocator->template reserve<alignof(T)>(
123*c8dee2aaSAndroid Build Coastguard Worker reserved * sizeof(T), SkBlockAllocator::kIgnoreExistingBytes_Flag);
124*c8dee2aaSAndroid Build Coastguard Worker }
125*c8dee2aaSAndroid Build Coastguard Worker }
126*c8dee2aaSAndroid Build Coastguard Worker
127*c8dee2aaSAndroid Build Coastguard Worker /**
128*c8dee2aaSAndroid Build Coastguard Worker * Remove the last item, only call if count() != 0
129*c8dee2aaSAndroid Build Coastguard Worker */
pop_back()130*c8dee2aaSAndroid Build Coastguard Worker void pop_back() {
131*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->count() > 0);
132*c8dee2aaSAndroid Build Coastguard Worker
133*c8dee2aaSAndroid Build Coastguard Worker SkBlockAllocator::Block* block = fAllocator->currentBlock();
134*c8dee2aaSAndroid Build Coastguard Worker
135*c8dee2aaSAndroid Build Coastguard Worker // Run dtor for the popped item
136*c8dee2aaSAndroid Build Coastguard Worker int releaseIndex = Last(block);
137*c8dee2aaSAndroid Build Coastguard Worker GetItem(block, releaseIndex).~T();
138*c8dee2aaSAndroid Build Coastguard Worker
139*c8dee2aaSAndroid Build Coastguard Worker if (releaseIndex == First(block)) {
140*c8dee2aaSAndroid Build Coastguard Worker fAllocator->releaseBlock(block);
141*c8dee2aaSAndroid Build Coastguard Worker } else {
142*c8dee2aaSAndroid Build Coastguard Worker // Since this always follows LIFO, the block should always be able to release the memory
143*c8dee2aaSAndroid Build Coastguard Worker SkAssertResult(block->release(releaseIndex, releaseIndex + sizeof(T)));
144*c8dee2aaSAndroid Build Coastguard Worker block->setMetadata(Decrement(block, releaseIndex));
145*c8dee2aaSAndroid Build Coastguard Worker }
146*c8dee2aaSAndroid Build Coastguard Worker
147*c8dee2aaSAndroid Build Coastguard Worker fAllocator->setMetadata(fAllocator->metadata() - 1);
148*c8dee2aaSAndroid Build Coastguard Worker }
149*c8dee2aaSAndroid Build Coastguard Worker
150*c8dee2aaSAndroid Build Coastguard Worker /**
151*c8dee2aaSAndroid Build Coastguard Worker * Removes all added items.
152*c8dee2aaSAndroid Build Coastguard Worker */
reset()153*c8dee2aaSAndroid Build Coastguard Worker void reset() {
154*c8dee2aaSAndroid Build Coastguard Worker // Invoke destructors in reverse order if not trivially destructible
155*c8dee2aaSAndroid Build Coastguard Worker if constexpr (!std::is_trivially_destructible<T>::value) {
156*c8dee2aaSAndroid Build Coastguard Worker for (T& t : this->ritems()) {
157*c8dee2aaSAndroid Build Coastguard Worker t.~T();
158*c8dee2aaSAndroid Build Coastguard Worker }
159*c8dee2aaSAndroid Build Coastguard Worker }
160*c8dee2aaSAndroid Build Coastguard Worker
161*c8dee2aaSAndroid Build Coastguard Worker fAllocator->reset();
162*c8dee2aaSAndroid Build Coastguard Worker }
163*c8dee2aaSAndroid Build Coastguard Worker
164*c8dee2aaSAndroid Build Coastguard Worker /**
165*c8dee2aaSAndroid Build Coastguard Worker * Returns the item count.
166*c8dee2aaSAndroid Build Coastguard Worker */
count()167*c8dee2aaSAndroid Build Coastguard Worker int count() const {
168*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG
169*c8dee2aaSAndroid Build Coastguard Worker // Confirm total count matches sum of block counts
170*c8dee2aaSAndroid Build Coastguard Worker int count = 0;
171*c8dee2aaSAndroid Build Coastguard Worker for (const auto* b :fAllocator->blocks()) {
172*c8dee2aaSAndroid Build Coastguard Worker if (b->metadata() == 0) {
173*c8dee2aaSAndroid Build Coastguard Worker continue; // skip empty
174*c8dee2aaSAndroid Build Coastguard Worker }
175*c8dee2aaSAndroid Build Coastguard Worker count += (sizeof(T) + Last(b) - First(b)) / sizeof(T);
176*c8dee2aaSAndroid Build Coastguard Worker }
177*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(count == fAllocator->metadata());
178*c8dee2aaSAndroid Build Coastguard Worker #endif
179*c8dee2aaSAndroid Build Coastguard Worker return fAllocator->metadata();
180*c8dee2aaSAndroid Build Coastguard Worker }
181*c8dee2aaSAndroid Build Coastguard Worker
182*c8dee2aaSAndroid Build Coastguard Worker /**
183*c8dee2aaSAndroid Build Coastguard Worker * Is the count 0?
184*c8dee2aaSAndroid Build Coastguard Worker */
empty()185*c8dee2aaSAndroid Build Coastguard Worker bool empty() const { return this->count() == 0; }
186*c8dee2aaSAndroid Build Coastguard Worker
187*c8dee2aaSAndroid Build Coastguard Worker /**
188*c8dee2aaSAndroid Build Coastguard Worker * Access first item, only call if count() != 0
189*c8dee2aaSAndroid Build Coastguard Worker */
front()190*c8dee2aaSAndroid Build Coastguard Worker T& front() {
191*c8dee2aaSAndroid Build Coastguard Worker // This assumes that the head block actually have room to store the first item.
192*c8dee2aaSAndroid Build Coastguard Worker static_assert(StartingItems >= 1);
193*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->count() > 0 && fAllocator->headBlock()->metadata() > 0);
194*c8dee2aaSAndroid Build Coastguard Worker return GetItem(fAllocator->headBlock(), First(fAllocator->headBlock()));
195*c8dee2aaSAndroid Build Coastguard Worker }
front()196*c8dee2aaSAndroid Build Coastguard Worker const T& front() const {
197*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->count() > 0 && fAllocator->headBlock()->metadata() > 0);
198*c8dee2aaSAndroid Build Coastguard Worker return GetItem(fAllocator->headBlock(), First(fAllocator->headBlock()));
199*c8dee2aaSAndroid Build Coastguard Worker }
200*c8dee2aaSAndroid Build Coastguard Worker
201*c8dee2aaSAndroid Build Coastguard Worker /**
202*c8dee2aaSAndroid Build Coastguard Worker * Access last item, only call if count() != 0
203*c8dee2aaSAndroid Build Coastguard Worker */
back()204*c8dee2aaSAndroid Build Coastguard Worker T& back() {
205*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->count() > 0 && fAllocator->currentBlock()->metadata() > 0);
206*c8dee2aaSAndroid Build Coastguard Worker return GetItem(fAllocator->currentBlock(), Last(fAllocator->currentBlock()));
207*c8dee2aaSAndroid Build Coastguard Worker }
back()208*c8dee2aaSAndroid Build Coastguard Worker const T& back() const {
209*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->count() > 0 && fAllocator->currentBlock()->metadata() > 0);
210*c8dee2aaSAndroid Build Coastguard Worker return GetItem(fAllocator->currentBlock(), Last(fAllocator->currentBlock()));
211*c8dee2aaSAndroid Build Coastguard Worker }
212*c8dee2aaSAndroid Build Coastguard Worker
213*c8dee2aaSAndroid Build Coastguard Worker /**
214*c8dee2aaSAndroid Build Coastguard Worker * Access item by index. Not an operator[] since it should not be considered constant time.
215*c8dee2aaSAndroid Build Coastguard Worker * Use for-range loops by calling items() or ritems() instead to access all added items in order
216*c8dee2aaSAndroid Build Coastguard Worker */
item(int i)217*c8dee2aaSAndroid Build Coastguard Worker T& item(int i) {
218*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(i >= 0 && i < this->count());
219*c8dee2aaSAndroid Build Coastguard Worker
220*c8dee2aaSAndroid Build Coastguard Worker // Iterate over blocks until we find the one that contains i.
221*c8dee2aaSAndroid Build Coastguard Worker for (auto* b : fAllocator->blocks()) {
222*c8dee2aaSAndroid Build Coastguard Worker if (b->metadata() == 0) {
223*c8dee2aaSAndroid Build Coastguard Worker continue; // skip empty
224*c8dee2aaSAndroid Build Coastguard Worker }
225*c8dee2aaSAndroid Build Coastguard Worker
226*c8dee2aaSAndroid Build Coastguard Worker int start = First(b);
227*c8dee2aaSAndroid Build Coastguard Worker int end = Last(b) + sizeof(T); // exclusive
228*c8dee2aaSAndroid Build Coastguard Worker int index = start + i * sizeof(T);
229*c8dee2aaSAndroid Build Coastguard Worker if (index < end) {
230*c8dee2aaSAndroid Build Coastguard Worker return GetItem(b, index);
231*c8dee2aaSAndroid Build Coastguard Worker } else {
232*c8dee2aaSAndroid Build Coastguard Worker i -= (end - start) / sizeof(T);
233*c8dee2aaSAndroid Build Coastguard Worker }
234*c8dee2aaSAndroid Build Coastguard Worker }
235*c8dee2aaSAndroid Build Coastguard Worker SkUNREACHABLE;
236*c8dee2aaSAndroid Build Coastguard Worker }
item(int i)237*c8dee2aaSAndroid Build Coastguard Worker const T& item(int i) const {
238*c8dee2aaSAndroid Build Coastguard Worker return const_cast<SkTBlockList*>(this)->item(i);
239*c8dee2aaSAndroid Build Coastguard Worker }
240*c8dee2aaSAndroid Build Coastguard Worker
241*c8dee2aaSAndroid Build Coastguard Worker private:
242*c8dee2aaSAndroid Build Coastguard Worker // Let other SkTBlockLists have access (only ever used when T and S are the same but you
243*c8dee2aaSAndroid Build Coastguard Worker // cannot have partial specializations declared as a friend...)
244*c8dee2aaSAndroid Build Coastguard Worker template<typename S, int N> friend class SkTBlockList;
245*c8dee2aaSAndroid Build Coastguard Worker friend class TBlockListTestAccess; // for fAllocator
246*c8dee2aaSAndroid Build Coastguard Worker
247*c8dee2aaSAndroid Build Coastguard Worker inline static constexpr size_t StartingSize =
248*c8dee2aaSAndroid Build Coastguard Worker SkBlockAllocator::Overhead<alignof(T)>() + StartingItems * sizeof(T);
249*c8dee2aaSAndroid Build Coastguard Worker
GetItem(SkBlockAllocator::Block * block,int index)250*c8dee2aaSAndroid Build Coastguard Worker static T& GetItem(SkBlockAllocator::Block* block, int index) {
251*c8dee2aaSAndroid Build Coastguard Worker return *static_cast<T*>(block->ptr(index));
252*c8dee2aaSAndroid Build Coastguard Worker }
GetItem(const SkBlockAllocator::Block * block,int index)253*c8dee2aaSAndroid Build Coastguard Worker static const T& GetItem(const SkBlockAllocator::Block* block, int index) {
254*c8dee2aaSAndroid Build Coastguard Worker return *static_cast<const T*>(block->ptr(index));
255*c8dee2aaSAndroid Build Coastguard Worker }
First(const SkBlockAllocator::Block * b)256*c8dee2aaSAndroid Build Coastguard Worker static int First(const SkBlockAllocator::Block* b) {
257*c8dee2aaSAndroid Build Coastguard Worker return b->firstAlignedOffset<alignof(T)>();
258*c8dee2aaSAndroid Build Coastguard Worker }
Last(const SkBlockAllocator::Block * b)259*c8dee2aaSAndroid Build Coastguard Worker static int Last(const SkBlockAllocator::Block* b) {
260*c8dee2aaSAndroid Build Coastguard Worker return b->metadata();
261*c8dee2aaSAndroid Build Coastguard Worker }
Increment(const SkBlockAllocator::Block * b,int index)262*c8dee2aaSAndroid Build Coastguard Worker static int Increment(const SkBlockAllocator::Block* b, int index) {
263*c8dee2aaSAndroid Build Coastguard Worker return index + sizeof(T);
264*c8dee2aaSAndroid Build Coastguard Worker }
Decrement(const SkBlockAllocator::Block * b,int index)265*c8dee2aaSAndroid Build Coastguard Worker static int Decrement(const SkBlockAllocator::Block* b, int index) {
266*c8dee2aaSAndroid Build Coastguard Worker return index - sizeof(T);
267*c8dee2aaSAndroid Build Coastguard Worker }
268*c8dee2aaSAndroid Build Coastguard Worker
pushItem()269*c8dee2aaSAndroid Build Coastguard Worker void* pushItem() {
270*c8dee2aaSAndroid Build Coastguard Worker // 'template' required because fAllocator is a template, calling a template member
271*c8dee2aaSAndroid Build Coastguard Worker auto br = fAllocator->template allocate<alignof(T)>(sizeof(T));
272*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(br.fStart == br.fAlignedOffset ||
273*c8dee2aaSAndroid Build Coastguard Worker br.fAlignedOffset == First(fAllocator->currentBlock()));
274*c8dee2aaSAndroid Build Coastguard Worker br.fBlock->setMetadata(br.fAlignedOffset);
275*c8dee2aaSAndroid Build Coastguard Worker fAllocator->setMetadata(fAllocator->metadata() + 1);
276*c8dee2aaSAndroid Build Coastguard Worker return br.fBlock->ptr(br.fAlignedOffset);
277*c8dee2aaSAndroid Build Coastguard Worker }
278*c8dee2aaSAndroid Build Coastguard Worker
279*c8dee2aaSAndroid Build Coastguard Worker // N represents the number of items, whereas SkSBlockAllocator takes total bytes, so must
280*c8dee2aaSAndroid Build Coastguard Worker // account for the block allocator's size too.
281*c8dee2aaSAndroid Build Coastguard Worker //
282*c8dee2aaSAndroid Build Coastguard Worker // This class uses the SkBlockAllocator's metadata to track total count of items, and per-block
283*c8dee2aaSAndroid Build Coastguard Worker // metadata to track the index of the last allocated item within each block.
284*c8dee2aaSAndroid Build Coastguard Worker SkSBlockAllocator<StartingSize> fAllocator;
285*c8dee2aaSAndroid Build Coastguard Worker
286*c8dee2aaSAndroid Build Coastguard Worker public:
287*c8dee2aaSAndroid Build Coastguard Worker using Iter = BlockIndexIterator<T&, true, false, &First, &Last, &Increment, &GetItem>;
288*c8dee2aaSAndroid Build Coastguard Worker using CIter = BlockIndexIterator<const T&, true, true, &First, &Last, &Increment, &GetItem>;
289*c8dee2aaSAndroid Build Coastguard Worker using RIter = BlockIndexIterator<T&, false, false, &Last, &First, &Decrement, &GetItem>;
290*c8dee2aaSAndroid Build Coastguard Worker using CRIter = BlockIndexIterator<const T&, false, true, &Last, &First, &Decrement, &GetItem>;
291*c8dee2aaSAndroid Build Coastguard Worker
292*c8dee2aaSAndroid Build Coastguard Worker /**
293*c8dee2aaSAndroid Build Coastguard Worker * Iterate over all items in allocation order (oldest to newest) using a for-range loop:
294*c8dee2aaSAndroid Build Coastguard Worker *
295*c8dee2aaSAndroid Build Coastguard Worker * for (auto&& T : this->items()) {}
296*c8dee2aaSAndroid Build Coastguard Worker */
items()297*c8dee2aaSAndroid Build Coastguard Worker Iter items() { return Iter(fAllocator.allocator()); }
items()298*c8dee2aaSAndroid Build Coastguard Worker CIter items() const { return CIter(fAllocator.allocator()); }
299*c8dee2aaSAndroid Build Coastguard Worker
300*c8dee2aaSAndroid Build Coastguard Worker // Iterate from newest to oldest using a for-range loop.
ritems()301*c8dee2aaSAndroid Build Coastguard Worker RIter ritems() { return RIter(fAllocator.allocator()); }
ritems()302*c8dee2aaSAndroid Build Coastguard Worker CRIter ritems() const { return CRIter(fAllocator.allocator()); }
303*c8dee2aaSAndroid Build Coastguard Worker };
304*c8dee2aaSAndroid Build Coastguard Worker
305*c8dee2aaSAndroid Build Coastguard Worker template <typename T, int SI1>
306*c8dee2aaSAndroid Build Coastguard Worker template <int SI2>
concat(SkTBlockList<T,SI2> && other)307*c8dee2aaSAndroid Build Coastguard Worker void SkTBlockList<T, SI1>::concat(SkTBlockList<T, SI2>&& other) {
308*c8dee2aaSAndroid Build Coastguard Worker // Optimize the common case where the list to append only has a single item
309*c8dee2aaSAndroid Build Coastguard Worker if (other.empty()) {
310*c8dee2aaSAndroid Build Coastguard Worker return;
311*c8dee2aaSAndroid Build Coastguard Worker } else if (other.count() == 1) {
312*c8dee2aaSAndroid Build Coastguard Worker this->push_back(other.back());
313*c8dee2aaSAndroid Build Coastguard Worker other.pop_back();
314*c8dee2aaSAndroid Build Coastguard Worker return;
315*c8dee2aaSAndroid Build Coastguard Worker }
316*c8dee2aaSAndroid Build Coastguard Worker
317*c8dee2aaSAndroid Build Coastguard Worker // Manually move all items in other's head block into this list; all heap blocks from 'other'
318*c8dee2aaSAndroid Build Coastguard Worker // will be appended to the block linked list (no per-item moves needed then).
319*c8dee2aaSAndroid Build Coastguard Worker int headItemCount = 0;
320*c8dee2aaSAndroid Build Coastguard Worker SkBlockAllocator::Block* headBlock = other.fAllocator->headBlock();
321*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(int oldCount = this->count();)
322*c8dee2aaSAndroid Build Coastguard Worker if (headBlock->metadata() > 0) {
323*c8dee2aaSAndroid Build Coastguard Worker int headStart = First(headBlock);
324*c8dee2aaSAndroid Build Coastguard Worker int headEnd = Last(headBlock) + sizeof(T); // exclusive
325*c8dee2aaSAndroid Build Coastguard Worker headItemCount = (headEnd - headStart) / sizeof(T);
326*c8dee2aaSAndroid Build Coastguard Worker int avail = fAllocator->currentBlock()->template avail<alignof(T)>() / sizeof(T);
327*c8dee2aaSAndroid Build Coastguard Worker if (headItemCount > avail) {
328*c8dee2aaSAndroid Build Coastguard Worker // Make sure there is extra room for the items beyond what's already avail. Use the
329*c8dee2aaSAndroid Build Coastguard Worker // kIgnoreGrowthPolicy_Flag to make this reservation as tight as possible since
330*c8dee2aaSAndroid Build Coastguard Worker // 'other's heap blocks will be appended after it and any extra space is wasted.
331*c8dee2aaSAndroid Build Coastguard Worker fAllocator->template reserve<alignof(T)>((headItemCount - avail) * sizeof(T),
332*c8dee2aaSAndroid Build Coastguard Worker SkBlockAllocator::kIgnoreExistingBytes_Flag |
333*c8dee2aaSAndroid Build Coastguard Worker SkBlockAllocator::kIgnoreGrowthPolicy_Flag);
334*c8dee2aaSAndroid Build Coastguard Worker }
335*c8dee2aaSAndroid Build Coastguard Worker
336*c8dee2aaSAndroid Build Coastguard Worker if constexpr (std::is_trivially_copy_constructible<T>::value) {
337*c8dee2aaSAndroid Build Coastguard Worker // memcpy all items at once (or twice between current and reserved space).
338*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(std::is_trivially_destructible<T>::value);
339*c8dee2aaSAndroid Build Coastguard Worker auto copy = [](SkBlockAllocator::Block* src, int start, SkBlockAllocator* dst, int n) {
340*c8dee2aaSAndroid Build Coastguard Worker auto target = dst->template allocate<alignof(T)>(n * sizeof(T));
341*c8dee2aaSAndroid Build Coastguard Worker memcpy(target.fBlock->ptr(target.fAlignedOffset), src->ptr(start), n * sizeof(T));
342*c8dee2aaSAndroid Build Coastguard Worker target.fBlock->setMetadata(target.fAlignedOffset + (n - 1) * sizeof(T));
343*c8dee2aaSAndroid Build Coastguard Worker };
344*c8dee2aaSAndroid Build Coastguard Worker
345*c8dee2aaSAndroid Build Coastguard Worker if (avail > 0) {
346*c8dee2aaSAndroid Build Coastguard Worker // Copy 0 to avail items into existing tail block
347*c8dee2aaSAndroid Build Coastguard Worker copy(headBlock, headStart, fAllocator.allocator(), std::min(headItemCount, avail));
348*c8dee2aaSAndroid Build Coastguard Worker }
349*c8dee2aaSAndroid Build Coastguard Worker if (headItemCount > avail) {
350*c8dee2aaSAndroid Build Coastguard Worker // Copy (head count - avail) into the extra reserved space
351*c8dee2aaSAndroid Build Coastguard Worker copy(headBlock, headStart + avail * sizeof(T),
352*c8dee2aaSAndroid Build Coastguard Worker fAllocator.allocator(), headItemCount - avail);
353*c8dee2aaSAndroid Build Coastguard Worker }
354*c8dee2aaSAndroid Build Coastguard Worker fAllocator->setMetadata(fAllocator->metadata() + headItemCount);
355*c8dee2aaSAndroid Build Coastguard Worker } else {
356*c8dee2aaSAndroid Build Coastguard Worker // Move every item over one at a time
357*c8dee2aaSAndroid Build Coastguard Worker for (int i = headStart; i < headEnd; i += sizeof(T)) {
358*c8dee2aaSAndroid Build Coastguard Worker T& toMove = GetItem(headBlock, i);
359*c8dee2aaSAndroid Build Coastguard Worker this->push_back(std::move(toMove));
360*c8dee2aaSAndroid Build Coastguard Worker // Anything of interest should have been moved, but run this since T isn't
361*c8dee2aaSAndroid Build Coastguard Worker // a trusted type.
362*c8dee2aaSAndroid Build Coastguard Worker toMove.~T(); // NOLINT(bugprone-use-after-move): calling dtor always allowed
363*c8dee2aaSAndroid Build Coastguard Worker }
364*c8dee2aaSAndroid Build Coastguard Worker }
365*c8dee2aaSAndroid Build Coastguard Worker
366*c8dee2aaSAndroid Build Coastguard Worker other.fAllocator->releaseBlock(headBlock);
367*c8dee2aaSAndroid Build Coastguard Worker }
368*c8dee2aaSAndroid Build Coastguard Worker
369*c8dee2aaSAndroid Build Coastguard Worker // other's head block must have been fully copied since it cannot be stolen
370*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(other.fAllocator->headBlock()->metadata() == 0 &&
371*c8dee2aaSAndroid Build Coastguard Worker fAllocator->metadata() == oldCount + headItemCount);
372*c8dee2aaSAndroid Build Coastguard Worker fAllocator->stealHeapBlocks(other.fAllocator.allocator());
373*c8dee2aaSAndroid Build Coastguard Worker fAllocator->setMetadata(fAllocator->metadata() +
374*c8dee2aaSAndroid Build Coastguard Worker (other.fAllocator->metadata() - headItemCount));
375*c8dee2aaSAndroid Build Coastguard Worker other.fAllocator->setMetadata(0);
376*c8dee2aaSAndroid Build Coastguard Worker }
377*c8dee2aaSAndroid Build Coastguard Worker
378*c8dee2aaSAndroid Build Coastguard Worker /**
379*c8dee2aaSAndroid Build Coastguard Worker * BlockIndexIterator provides a reusable iterator template for collections built on top of a
380*c8dee2aaSAndroid Build Coastguard Worker * SkBlockAllocator, where each item is of the same type, and the index to an item can be iterated
381*c8dee2aaSAndroid Build Coastguard Worker * over in a known manner. It supports const and non-const, and forward and reverse, assuming it's
382*c8dee2aaSAndroid Build Coastguard Worker * provided with proper functions for starting, ending, and advancing.
383*c8dee2aaSAndroid Build Coastguard Worker */
384*c8dee2aaSAndroid Build Coastguard Worker template <typename T, // The element type (including any modifiers)
385*c8dee2aaSAndroid Build Coastguard Worker bool Forward, // Are indices within a block increasing or decreasing with iteration?
386*c8dee2aaSAndroid Build Coastguard Worker bool Const, // Whether or not T is const
387*c8dee2aaSAndroid Build Coastguard Worker IndexFn Start, // Returns the index of the first valid item in a block
388*c8dee2aaSAndroid Build Coastguard Worker IndexFn End, // Returns the index of the last valid item (so it is inclusive)
389*c8dee2aaSAndroid Build Coastguard Worker NextFn Next, // Returns the next index given the current index
390*c8dee2aaSAndroid Build Coastguard Worker ItemFn<T, typename std::conditional<Const, const SkBlockAllocator::Block,
391*c8dee2aaSAndroid Build Coastguard Worker SkBlockAllocator::Block>::type> Resolve>
392*c8dee2aaSAndroid Build Coastguard Worker class BlockIndexIterator {
393*c8dee2aaSAndroid Build Coastguard Worker using BlockIter = typename SkBlockAllocator::BlockIter<Forward, Const>;
394*c8dee2aaSAndroid Build Coastguard Worker public:
BlockIndexIterator(BlockIter iter)395*c8dee2aaSAndroid Build Coastguard Worker BlockIndexIterator(BlockIter iter) : fBlockIter(iter) {}
396*c8dee2aaSAndroid Build Coastguard Worker
397*c8dee2aaSAndroid Build Coastguard Worker class Item {
398*c8dee2aaSAndroid Build Coastguard Worker public:
399*c8dee2aaSAndroid Build Coastguard Worker bool operator!=(const Item& other) const {
400*c8dee2aaSAndroid Build Coastguard Worker return other.fBlock != fBlock || (SkToBool(*fBlock) && other.fIndex != fIndex);
401*c8dee2aaSAndroid Build Coastguard Worker }
402*c8dee2aaSAndroid Build Coastguard Worker
403*c8dee2aaSAndroid Build Coastguard Worker T operator*() const {
404*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(*fBlock);
405*c8dee2aaSAndroid Build Coastguard Worker return Resolve(*fBlock, fIndex);
406*c8dee2aaSAndroid Build Coastguard Worker }
407*c8dee2aaSAndroid Build Coastguard Worker
408*c8dee2aaSAndroid Build Coastguard Worker Item& operator++() {
409*c8dee2aaSAndroid Build Coastguard Worker const auto* block = *fBlock;
410*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(block && block->metadata() > 0);
411*c8dee2aaSAndroid Build Coastguard Worker SkASSERT((Forward && Next(block, fIndex) > fIndex) ||
412*c8dee2aaSAndroid Build Coastguard Worker (!Forward && Next(block, fIndex) < fIndex));
413*c8dee2aaSAndroid Build Coastguard Worker fIndex = Next(block, fIndex);
414*c8dee2aaSAndroid Build Coastguard Worker if ((Forward && fIndex > fEndIndex) || (!Forward && fIndex < fEndIndex)) {
415*c8dee2aaSAndroid Build Coastguard Worker ++fBlock;
416*c8dee2aaSAndroid Build Coastguard Worker this->setIndices();
417*c8dee2aaSAndroid Build Coastguard Worker }
418*c8dee2aaSAndroid Build Coastguard Worker return *this;
419*c8dee2aaSAndroid Build Coastguard Worker }
420*c8dee2aaSAndroid Build Coastguard Worker
421*c8dee2aaSAndroid Build Coastguard Worker private:
422*c8dee2aaSAndroid Build Coastguard Worker friend BlockIndexIterator;
423*c8dee2aaSAndroid Build Coastguard Worker using BlockItem = typename BlockIter::Item;
424*c8dee2aaSAndroid Build Coastguard Worker
Item(BlockItem block)425*c8dee2aaSAndroid Build Coastguard Worker Item(BlockItem block) : fBlock(block) {
426*c8dee2aaSAndroid Build Coastguard Worker this->setIndices();
427*c8dee2aaSAndroid Build Coastguard Worker }
428*c8dee2aaSAndroid Build Coastguard Worker
setIndices()429*c8dee2aaSAndroid Build Coastguard Worker void setIndices() {
430*c8dee2aaSAndroid Build Coastguard Worker // Skip empty blocks
431*c8dee2aaSAndroid Build Coastguard Worker while(*fBlock && (*fBlock)->metadata() == 0) {
432*c8dee2aaSAndroid Build Coastguard Worker ++fBlock;
433*c8dee2aaSAndroid Build Coastguard Worker }
434*c8dee2aaSAndroid Build Coastguard Worker if (*fBlock) {
435*c8dee2aaSAndroid Build Coastguard Worker fIndex = Start(*fBlock);
436*c8dee2aaSAndroid Build Coastguard Worker fEndIndex = End(*fBlock);
437*c8dee2aaSAndroid Build Coastguard Worker } else {
438*c8dee2aaSAndroid Build Coastguard Worker fIndex = 0;
439*c8dee2aaSAndroid Build Coastguard Worker fEndIndex = 0;
440*c8dee2aaSAndroid Build Coastguard Worker }
441*c8dee2aaSAndroid Build Coastguard Worker
442*c8dee2aaSAndroid Build Coastguard Worker SkASSERT((Forward && fIndex <= fEndIndex) || (!Forward && fIndex >= fEndIndex));
443*c8dee2aaSAndroid Build Coastguard Worker }
444*c8dee2aaSAndroid Build Coastguard Worker
445*c8dee2aaSAndroid Build Coastguard Worker BlockItem fBlock;
446*c8dee2aaSAndroid Build Coastguard Worker int fIndex;
447*c8dee2aaSAndroid Build Coastguard Worker int fEndIndex;
448*c8dee2aaSAndroid Build Coastguard Worker };
449*c8dee2aaSAndroid Build Coastguard Worker
begin()450*c8dee2aaSAndroid Build Coastguard Worker Item begin() const { return Item(fBlockIter.begin()); }
end()451*c8dee2aaSAndroid Build Coastguard Worker Item end() const { return Item(fBlockIter.end()); }
452*c8dee2aaSAndroid Build Coastguard Worker
453*c8dee2aaSAndroid Build Coastguard Worker private:
454*c8dee2aaSAndroid Build Coastguard Worker BlockIter fBlockIter;
455*c8dee2aaSAndroid Build Coastguard Worker };
456*c8dee2aaSAndroid Build Coastguard Worker
457*c8dee2aaSAndroid Build Coastguard Worker #endif
458