Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Public Types | Public Member Functions | Friends | List of all members
SkTBlockList< T, StartingItems > Class Template Reference

#include <SkTBlockList.h>

Public Types

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 >
 

Public Member Functions

 SkTBlockList ()
 
 SkTBlockList (SkBlockAllocator::GrowthPolicy policy)
 
 SkTBlockList (int itemsPerBlock, SkBlockAllocator::GrowthPolicy policy=SkBlockAllocator::GrowthPolicy::kFixed)
 
 ~SkTBlockList ()
 
Tpush_back ()
 
Tpush_back (const T &t)
 
Tpush_back (T &&t)
 
template<typename... Args>
Templace_back (Args &&... args)
 
template<int SI>
void concat (SkTBlockList< T, SI > &&other)
 
void reserve (int n)
 
void pop_back ()
 
void reset ()
 
int count () const
 
bool empty () const
 
Tfront ()
 
const Tfront () const
 
Tback ()
 
const Tback () const
 
Titem (int i)
 
const Titem (int i) const
 
Iter items ()
 
CIter items () const
 
RIter ritems ()
 
CRIter ritems () const
 
template<int SI2>
void concat (SkTBlockList< T, SI2 > &&other)
 

Friends

template<typename S , int N>
class SkTBlockList
 
class TBlockListTestAccess
 

Detailed Description

template<typename T, int StartingItems = 1>
class SkTBlockList< T, StartingItems >

SkTBlockList manages dynamic storage for instances of T, reserving fixed blocks such that allocation is amortized across every N instances. In this way it is a hybrid of an array-based vector and a linked-list. T can be any type and non-trivial destructors are automatically invoked when the SkTBlockList is destructed. The addresses of instances are guaranteed not to move except when a list is concatenated to another.

The collection supports storing a templated number of elements inline before heap-allocated blocks are made to hold additional instances. By default, the heap blocks are sized to hold the same number of items as the inline block. A common pattern is to have the inline size hold only a small number of items for the common case and then allocate larger blocks when needed.

If the size of a collection is N, and its block size is B, the complexity of the common operations are:

These characteristics make it well suited for allocating items in a LIFO ordering, or otherwise acting as a stack, or simply using it as a typed allocator.

Definition at line 56 of file SkTBlockList.h.

Member Typedef Documentation

◆ CIter

template<typename T , int StartingItems = 1>
using SkTBlockList< T, StartingItems >::CIter = BlockIndexIterator<const T&, true, true, &First, &Last, &Increment, &GetItem>

Definition at line 288 of file SkTBlockList.h.

◆ CRIter

template<typename T , int StartingItems = 1>
using SkTBlockList< T, StartingItems >::CRIter = BlockIndexIterator<const T&, false, true, &Last, &First, &Decrement, &GetItem>

Definition at line 290 of file SkTBlockList.h.

◆ Iter

template<typename T , int StartingItems = 1>
using SkTBlockList< T, StartingItems >::Iter = BlockIndexIterator<T&, true, false, &First, &Last, &Increment, &GetItem>

Definition at line 287 of file SkTBlockList.h.

◆ RIter

template<typename T , int StartingItems = 1>
using SkTBlockList< T, StartingItems >::RIter = BlockIndexIterator<T&, false, false, &Last, &First, &Decrement, &GetItem>

Definition at line 289 of file SkTBlockList.h.

Constructor & Destructor Documentation

◆ SkTBlockList() [1/3]

template<typename T , int StartingItems = 1>
SkTBlockList< T, StartingItems >::SkTBlockList ( )
inline

Create an list that defaults to using StartingItems as heap increment and the kFixed growth policy (e.g. all allocations will match StartingItems).

Definition at line 62 of file SkTBlockList.h.

62: SkTBlockList(SkBlockAllocator::GrowthPolicy::kFixed) {}
friend class SkTBlockList

◆ SkTBlockList() [2/3]

template<typename T , int StartingItems = 1>
SkTBlockList< T, StartingItems >::SkTBlockList ( SkBlockAllocator::GrowthPolicy  policy)
inlineexplicit

Create an list that defaults to using StartingItems as the heap increment, but with the defined growth policy.

Definition at line 68 of file SkTBlockList.h.

69 : SkTBlockList(StartingItems, policy) {}

◆ SkTBlockList() [3/3]

template<typename T , int StartingItems = 1>
SkTBlockList< T, StartingItems >::SkTBlockList ( int  itemsPerBlock,
SkBlockAllocator::GrowthPolicy  policy = SkBlockAllocator::GrowthPolicy::kFixed 
)
inlineexplicit

Create an list.

Parameters
itemsPerBlockthe number of items to allocate at once
policythe growth policy for subsequent blocks of items

Definition at line 77 of file SkTBlockList.h.

80 : fAllocator(policy,
81 SkBlockAllocator::BlockOverhead<alignof(T)>() + sizeof(T)*itemsPerBlock) {}
static constexpr size_t BlockOverhead()
#define T

◆ ~SkTBlockList()

template<typename T , int StartingItems = 1>
SkTBlockList< T, StartingItems >::~SkTBlockList ( )
inline

Definition at line 83 of file SkTBlockList.h.

83{ this->reset(); }

Member Function Documentation

◆ back() [1/2]

template<typename T , int StartingItems = 1>
T & SkTBlockList< T, StartingItems >::back ( )
inline

Access last item, only call if count() != 0

Definition at line 204 of file SkTBlockList.h.

204 {
205 SkASSERT(this->count() > 0 && fAllocator->currentBlock()->metadata() > 0);
206 return GetItem(fAllocator->currentBlock(), Last(fAllocator->currentBlock()));
207 }
int count
#define SkASSERT(cond)
Definition SkAssert.h:116
const Block * currentBlock() const

◆ back() [2/2]

template<typename T , int StartingItems = 1>
const T & SkTBlockList< T, StartingItems >::back ( ) const
inline

Definition at line 208 of file SkTBlockList.h.

208 {
209 SkASSERT(this->count() > 0 && fAllocator->currentBlock()->metadata() > 0);
210 return GetItem(fAllocator->currentBlock(), Last(fAllocator->currentBlock()));
211 }

◆ concat() [1/2]

template<typename T , int StartingItems = 1>
template<int SI>
void SkTBlockList< T, StartingItems >::concat ( SkTBlockList< T, SI > &&  other)

Move all items from 'other' to the end of this collection. When this returns, 'other' will be empty. Items in 'other' may be moved as part of compacting the pre-allocated start of 'other' into this list (using T's move constructor or memcpy if T is trivially copyable), but this is O(StartingItems) and not O(N). All other items are concatenated in O(1).

◆ concat() [2/2]

template<typename T , int StartingItems = 1>
template<int SI2>
void SkTBlockList< T, StartingItems >::concat ( SkTBlockList< T, SI2 > &&  other)

Definition at line 307 of file SkTBlockList.h.

307 {
308 // Optimize the common case where the list to append only has a single item
309 if (other.empty()) {
310 return;
311 } else if (other.count() == 1) {
312 this->push_back(other.back());
313 other.pop_back();
314 return;
315 }
316
317 // Manually move all items in other's head block into this list; all heap blocks from 'other'
318 // will be appended to the block linked list (no per-item moves needed then).
319 int headItemCount = 0;
320 SkBlockAllocator::Block* headBlock = other.fAllocator->headBlock();
321 SkDEBUGCODE(int oldCount = this->count();)
322 if (headBlock->metadata() > 0) {
323 int headStart = First(headBlock);
324 int headEnd = Last(headBlock) + sizeof(T); // exclusive
325 headItemCount = (headEnd - headStart) / sizeof(T);
326 int avail = fAllocator->currentBlock()->template avail<alignof(T)>() / sizeof(T);
327 if (headItemCount > avail) {
328 // Make sure there is extra room for the items beyond what's already avail. Use the
329 // kIgnoreGrowthPolicy_Flag to make this reservation as tight as possible since
330 // 'other's heap blocks will be appended after it and any extra space is wasted.
331 fAllocator->template reserve<alignof(T)>((headItemCount - avail) * sizeof(T),
334 }
335
336 if constexpr (std::is_trivially_copy_constructible<T>::value) {
337 // memcpy all items at once (or twice between current and reserved space).
338 SkASSERT(std::is_trivially_destructible<T>::value);
339 auto copy = [](SkBlockAllocator::Block* src, int start, SkBlockAllocator* dst, int n) {
340 auto target = dst->template allocate<alignof(T)>(n * sizeof(T));
341 memcpy(target.fBlock->ptr(target.fAlignedOffset), src->ptr(start), n * sizeof(T));
342 target.fBlock->setMetadata(target.fAlignedOffset + (n - 1) * sizeof(T));
343 };
344
345 if (avail > 0) {
346 // Copy 0 to avail items into existing tail block
347 copy(headBlock, headStart, fAllocator.allocator(), std::min(headItemCount, avail));
348 }
349 if (headItemCount > avail) {
350 // Copy (head count - avail) into the extra reserved space
351 copy(headBlock, headStart + avail * sizeof(T),
352 fAllocator.allocator(), headItemCount - avail);
353 }
354 fAllocator->setMetadata(fAllocator->metadata() + headItemCount);
355 } else {
356 // Move every item over one at a time
357 for (int i = headStart; i < headEnd; i += sizeof(T)) {
358 T& toMove = GetItem(headBlock, i);
359 this->push_back(std::move(toMove));
360 // Anything of interest should have been moved, but run this since T isn't
361 // a trusted type.
362 toMove.~T(); // NOLINT(bugprone-use-after-move): calling dtor always allowed
363 }
364 }
365
366 other.fAllocator->releaseBlock(headBlock);
367 }
368
369 // other's head block must have been fully copied since it cannot be stolen
370 SkASSERT(other.fAllocator->headBlock()->metadata() == 0 &&
371 fAllocator->metadata() == oldCount + headItemCount);
372 fAllocator->stealHeapBlocks(other.fAllocator.allocator());
373 fAllocator->setMetadata(fAllocator->metadata() +
374 (other.fAllocator->metadata() - headItemCount));
375 other.fAllocator->setMetadata(0);
376}
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
void setMetadata(int value)
void releaseBlock(Block *block)
void stealHeapBlocks(SkBlockAllocator *other)
const Block * headBlock() const
SkBlockAllocator * allocator()
int count() const
bool empty() const
uint32_t * target
Definition copy.py:1
dst
Definition cp.py:12

◆ count()

template<typename T , int StartingItems = 1>
int SkTBlockList< T, StartingItems >::count ( ) const
inline

Returns the item count.

Definition at line 167 of file SkTBlockList.h.

167 {
168#ifdef SK_DEBUG
169 // Confirm total count matches sum of block counts
170 int count = 0;
171 for (const auto* b :fAllocator->blocks()) {
172 if (b->metadata() == 0) {
173 continue; // skip empty
174 }
175 count += (sizeof(T) + Last(b) - First(b)) / sizeof(T);
176 }
177 SkASSERT(count == fAllocator->metadata());
178#endif
179 return fAllocator->metadata();
180 }
static bool b

◆ emplace_back()

template<typename T , int StartingItems = 1>
template<typename... Args>
T & SkTBlockList< T, StartingItems >::emplace_back ( Args &&...  args)
inline

Definition at line 101 of file SkTBlockList.h.

101 {
102 return *new (this->pushItem()) T(std::forward<Args>(args)...);
103 }
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args

◆ empty()

template<typename T , int StartingItems = 1>
bool SkTBlockList< T, StartingItems >::empty ( ) const
inline

Is the count 0?

Definition at line 185 of file SkTBlockList.h.

185{ return this->count() == 0; }

◆ front() [1/2]

template<typename T , int StartingItems = 1>
T & SkTBlockList< T, StartingItems >::front ( )
inline

Access first item, only call if count() != 0

Definition at line 190 of file SkTBlockList.h.

190 {
191 // This assumes that the head block actually have room to store the first item.
192 static_assert(StartingItems >= 1);
193 SkASSERT(this->count() > 0 && fAllocator->headBlock()->metadata() > 0);
194 return GetItem(fAllocator->headBlock(), First(fAllocator->headBlock()));
195 }

◆ front() [2/2]

template<typename T , int StartingItems = 1>
const T & SkTBlockList< T, StartingItems >::front ( ) const
inline

Definition at line 196 of file SkTBlockList.h.

196 {
197 SkASSERT(this->count() > 0 && fAllocator->headBlock()->metadata() > 0);
198 return GetItem(fAllocator->headBlock(), First(fAllocator->headBlock()));
199 }

◆ item() [1/2]

template<typename T , int StartingItems = 1>
T & SkTBlockList< T, StartingItems >::item ( int  i)
inline

Access item by index. Not an operator[] since it should not be considered constant time. Use for-range loops by calling items() or ritems() instead to access all added items in order

Definition at line 217 of file SkTBlockList.h.

217 {
218 SkASSERT(i >= 0 && i < this->count());
219
220 // Iterate over blocks until we find the one that contains i.
221 for (auto* b : fAllocator->blocks()) {
222 if (b->metadata() == 0) {
223 continue; // skip empty
224 }
225
226 int start = First(b);
227 int end = Last(b) + sizeof(T); // exclusive
228 int index = start + i * sizeof(T);
229 if (index < end) {
230 return GetItem(b, index);
231 } else {
232 i -= (end - start) / sizeof(T);
233 }
234 }
236 }
#define SkUNREACHABLE
Definition SkAssert.h:135
glong glong end

◆ item() [2/2]

template<typename T , int StartingItems = 1>
const T & SkTBlockList< T, StartingItems >::item ( int  i) const
inline

Definition at line 237 of file SkTBlockList.h.

237 {
238 return const_cast<SkTBlockList*>(this)->item(i);
239 }
T & item(int i)

◆ items() [1/2]

template<typename T , int StartingItems = 1>
Iter SkTBlockList< T, StartingItems >::items ( )
inline

Iterate over all items in allocation order (oldest to newest) using a for-range loop:

for (auto&& T : this->items()) {}

Definition at line 297 of file SkTBlockList.h.

297{ return Iter(fAllocator.allocator()); }
BlockIndexIterator< T &, true, false, &First, &Last, &Increment, &GetItem > Iter

◆ items() [2/2]

template<typename T , int StartingItems = 1>
CIter SkTBlockList< T, StartingItems >::items ( ) const
inline

Definition at line 298 of file SkTBlockList.h.

298{ return CIter(fAllocator.allocator()); }
BlockIndexIterator< const T &, true, true, &First, &Last, &Increment, &GetItem > CIter

◆ pop_back()

template<typename T , int StartingItems = 1>
void SkTBlockList< T, StartingItems >::pop_back ( )
inline

Remove the last item, only call if count() != 0

Definition at line 130 of file SkTBlockList.h.

130 {
131 SkASSERT(this->count() > 0);
132
133 SkBlockAllocator::Block* block = fAllocator->currentBlock();
134
135 // Run dtor for the popped item
136 int releaseIndex = Last(block);
137 GetItem(block, releaseIndex).~T();
138
139 if (releaseIndex == First(block)) {
140 fAllocator->releaseBlock(block);
141 } else {
142 // Since this always follows LIFO, the block should always be able to release the memory
143 SkAssertResult(block->release(releaseIndex, releaseIndex + sizeof(T)));
144 block->setMetadata(Decrement(block, releaseIndex));
145 }
146
147 fAllocator->setMetadata(fAllocator->metadata() - 1);
148 }
#define SkAssertResult(cond)
Definition SkAssert.h:123
bool release(int start, int end)
void setMetadata(int value)

◆ push_back() [1/3]

template<typename T , int StartingItems = 1>
T & SkTBlockList< T, StartingItems >::push_back ( )
inline

Adds an item and returns it.

Returns
the added item.

Definition at line 90 of file SkTBlockList.h.

90 {
91 return *new (this->pushItem()) T;
92 }

◆ push_back() [2/3]

template<typename T , int StartingItems = 1>
T & SkTBlockList< T, StartingItems >::push_back ( const T t)
inline

Definition at line 93 of file SkTBlockList.h.

93 {
94 return *new (this->pushItem()) T(t);
95 }

◆ push_back() [3/3]

template<typename T , int StartingItems = 1>
T & SkTBlockList< T, StartingItems >::push_back ( T &&  t)
inline

Definition at line 96 of file SkTBlockList.h.

96 {
97 return *new (this->pushItem()) T(std::move(t));
98 }

◆ reserve()

template<typename T , int StartingItems = 1>
void SkTBlockList< T, StartingItems >::reserve ( int  n)
inline

Allocate, if needed, space to hold N more Ts before another malloc will occur.

Definition at line 117 of file SkTBlockList.h.

117 {
118 int avail = fAllocator->currentBlock()->template avail<alignof(T)>() / sizeof(T);
119 if (n > avail) {
120 int reserved = n - avail;
121 // Don't consider existing bytes since we've already determined how to split the N items
122 fAllocator->template reserve<alignof(T)>(
124 }
125 }

◆ reset()

template<typename T , int StartingItems = 1>
void SkTBlockList< T, StartingItems >::reset ( )
inline

Removes all added items.

Definition at line 153 of file SkTBlockList.h.

153 {
154 // Invoke destructors in reverse order if not trivially destructible
155 if constexpr (!std::is_trivially_destructible<T>::value) {
156 for (T& t : this->ritems()) {
157 t.~T();
158 }
159 }
160
161 fAllocator->reset();
162 }

◆ ritems() [1/2]

template<typename T , int StartingItems = 1>
RIter SkTBlockList< T, StartingItems >::ritems ( )
inline

Definition at line 301 of file SkTBlockList.h.

301{ return RIter(fAllocator.allocator()); }
BlockIndexIterator< T &, false, false, &Last, &First, &Decrement, &GetItem > RIter

◆ ritems() [2/2]

template<typename T , int StartingItems = 1>
CRIter SkTBlockList< T, StartingItems >::ritems ( ) const
inline

Definition at line 302 of file SkTBlockList.h.

302{ return CRIter(fAllocator.allocator()); }
BlockIndexIterator< const T &, false, true, &Last, &First, &Decrement, &GetItem > CRIter

Friends And Related Symbol Documentation

◆ SkTBlockList

template<typename T , int StartingItems = 1>
template<typename S , int N>
friend class SkTBlockList
friend

Definition at line 244 of file SkTBlockList.h.

◆ TBlockListTestAccess

template<typename T , int StartingItems = 1>
friend class TBlockListTestAccess
friend

Definition at line 245 of file SkTBlockList.h.


The documentation for this class was generated from the following file: