1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2020 Google LLC 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#ifndef SkBlockAllocator_DEFINED 9cb93a386Sopenharmony_ci#define SkBlockAllocator_DEFINED 10cb93a386Sopenharmony_ci 11cb93a386Sopenharmony_ci#include "include/core/SkMath.h" 12cb93a386Sopenharmony_ci#include "include/core/SkTypes.h" 13cb93a386Sopenharmony_ci#include "include/private/SkMacros.h" 14cb93a386Sopenharmony_ci#include "include/private/SkNoncopyable.h" 15cb93a386Sopenharmony_ci#include "include/private/SkTo.h" 16cb93a386Sopenharmony_ci#include "src/core/SkASAN.h" 17cb93a386Sopenharmony_ci 18cb93a386Sopenharmony_ci#include <memory> // std::unique_ptr 19cb93a386Sopenharmony_ci#include <cstddef> // max_align_t 20cb93a386Sopenharmony_ci#include <limits> // numeric_limits 21cb93a386Sopenharmony_ci#include <algorithm> // max 22cb93a386Sopenharmony_ci 23cb93a386Sopenharmony_ci/** 24cb93a386Sopenharmony_ci * SkBlockAllocator provides low-level support for a block allocated arena with a dynamic tail that 25cb93a386Sopenharmony_ci * tracks space reservations within each block. Its APIs provide the ability to reserve space, 26cb93a386Sopenharmony_ci * resize reservations, and release reservations. It will automatically create new blocks if needed 27cb93a386Sopenharmony_ci * and destroy all remaining blocks when it is destructed. It assumes that anything allocated within 28cb93a386Sopenharmony_ci * its blocks has its destructors called externally. It is recommended that SkBlockAllocator is 29cb93a386Sopenharmony_ci * wrapped by a higher-level allocator that uses the low-level APIs to implement a simpler, 30cb93a386Sopenharmony_ci * purpose-focused API w/o having to worry as much about byte-level concerns. 31cb93a386Sopenharmony_ci * 32cb93a386Sopenharmony_ci * SkBlockAllocator has no limit to its total size, but each allocation is limited to 512MB (which 33cb93a386Sopenharmony_ci * should be sufficient for Skia's use cases). This upper allocation limit allows all internal 34cb93a386Sopenharmony_ci * operations to be performed using 'int' and avoid many overflow checks. Static asserts are used 35cb93a386Sopenharmony_ci * to ensure that those operations would not overflow when using the largest possible values. 36cb93a386Sopenharmony_ci * 37cb93a386Sopenharmony_ci * Possible use modes: 38cb93a386Sopenharmony_ci * 1. No upfront allocation, either on the stack or as a field 39cb93a386Sopenharmony_ci * SkBlockAllocator allocator(policy, heapAllocSize); 40cb93a386Sopenharmony_ci * 41cb93a386Sopenharmony_ci * 2. In-place new'd 42cb93a386Sopenharmony_ci * void* mem = operator new(totalSize); 43cb93a386Sopenharmony_ci * SkBlockAllocator* allocator = new (mem) SkBlockAllocator(policy, heapAllocSize, 44cb93a386Sopenharmony_ci * totalSize- sizeof(SkBlockAllocator)); 45cb93a386Sopenharmony_ci * delete allocator; 46cb93a386Sopenharmony_ci * 47cb93a386Sopenharmony_ci * 3. Use SkSBlockAllocator to increase the preallocation size 48cb93a386Sopenharmony_ci * SkSBlockAllocator<1024> allocator(policy, heapAllocSize); 49cb93a386Sopenharmony_ci * sizeof(allocator) == 1024; 50cb93a386Sopenharmony_ci */ 51cb93a386Sopenharmony_ci// TODO(michaelludwig) - While API is different, this shares similarities to SkArenaAlloc and 52cb93a386Sopenharmony_ci// SkFibBlockSizes, so we should work to integrate them. 53cb93a386Sopenharmony_ciclass SkBlockAllocator final : SkNoncopyable { 54cb93a386Sopenharmony_cipublic: 55cb93a386Sopenharmony_ci // Largest size that can be requested from allocate(), chosen because it's the largest pow-2 56cb93a386Sopenharmony_ci // that is less than int32_t::max()/2. 57cb93a386Sopenharmony_ci inline static constexpr int kMaxAllocationSize = 1 << 29; 58cb93a386Sopenharmony_ci 59cb93a386Sopenharmony_ci enum class GrowthPolicy : int { 60cb93a386Sopenharmony_ci kFixed, // Next block size = N 61cb93a386Sopenharmony_ci kLinear, // = #blocks * N 62cb93a386Sopenharmony_ci kFibonacci, // = fibonacci(#blocks) * N 63cb93a386Sopenharmony_ci kExponential, // = 2^#blocks * N 64cb93a386Sopenharmony_ci kLast = kExponential 65cb93a386Sopenharmony_ci }; 66cb93a386Sopenharmony_ci inline static constexpr int kGrowthPolicyCount = static_cast<int>(GrowthPolicy::kLast) + 1; 67cb93a386Sopenharmony_ci 68cb93a386Sopenharmony_ci class Block; 69cb93a386Sopenharmony_ci 70cb93a386Sopenharmony_ci // Tuple representing a range of bytes, marking the unaligned start, the first aligned point 71cb93a386Sopenharmony_ci // after any padding, and the upper limit depending on requested size. 72cb93a386Sopenharmony_ci struct ByteRange { 73cb93a386Sopenharmony_ci Block* fBlock; // Owning block 74cb93a386Sopenharmony_ci int fStart; // Inclusive byte lower limit of byte range 75cb93a386Sopenharmony_ci int fAlignedOffset; // >= start, matching alignment requirement (i.e. first real byte) 76cb93a386Sopenharmony_ci int fEnd; // Exclusive upper limit of byte range 77cb93a386Sopenharmony_ci }; 78cb93a386Sopenharmony_ci 79cb93a386Sopenharmony_ci class Block final { 80cb93a386Sopenharmony_ci public: 81cb93a386Sopenharmony_ci ~Block(); 82cb93a386Sopenharmony_ci void operator delete(void* p) { ::operator delete(p); } 83cb93a386Sopenharmony_ci 84cb93a386Sopenharmony_ci // Return the maximum allocation size with the given alignment that can fit in this block. 85cb93a386Sopenharmony_ci template <size_t Align = 1, size_t Padding = 0> 86cb93a386Sopenharmony_ci int avail() const { return std::max(0, fSize - this->cursor<Align, Padding>()); } 87cb93a386Sopenharmony_ci 88cb93a386Sopenharmony_ci // Return the aligned offset of the first allocation, assuming it was made with the 89cb93a386Sopenharmony_ci // specified Align, and Padding. The returned offset does not mean a valid allocation 90cb93a386Sopenharmony_ci // starts at that offset, this is a utility function for classes built on top to manage 91cb93a386Sopenharmony_ci // indexing into a block effectively. 92cb93a386Sopenharmony_ci template <size_t Align = 1, size_t Padding = 0> 93cb93a386Sopenharmony_ci int firstAlignedOffset() const { return this->alignedOffset<Align, Padding>(kDataStart); } 94cb93a386Sopenharmony_ci 95cb93a386Sopenharmony_ci // Convert an offset into this block's storage into a usable pointer. 96cb93a386Sopenharmony_ci void* ptr(int offset) { 97cb93a386Sopenharmony_ci SkASSERT(offset >= kDataStart && offset < fSize); 98cb93a386Sopenharmony_ci return reinterpret_cast<char*>(this) + offset; 99cb93a386Sopenharmony_ci } 100cb93a386Sopenharmony_ci const void* ptr(int offset) const { return const_cast<Block*>(this)->ptr(offset); } 101cb93a386Sopenharmony_ci 102cb93a386Sopenharmony_ci // Every block has an extra 'int' for clients to use however they want. It will start 103cb93a386Sopenharmony_ci // at 0 when a new block is made, or when the head block is reset. 104cb93a386Sopenharmony_ci int metadata() const { return fMetadata; } 105cb93a386Sopenharmony_ci void setMetadata(int value) { fMetadata = value; } 106cb93a386Sopenharmony_ci 107cb93a386Sopenharmony_ci /** 108cb93a386Sopenharmony_ci * Release the byte range between offset 'start' (inclusive) and 'end' (exclusive). This 109cb93a386Sopenharmony_ci * will return true if those bytes were successfully reclaimed, i.e. a subsequent allocation 110cb93a386Sopenharmony_ci * request could occupy the space. Regardless of return value, the provided byte range that 111cb93a386Sopenharmony_ci * [start, end) represents should not be used until it's re-allocated with allocate<...>(). 112cb93a386Sopenharmony_ci */ 113cb93a386Sopenharmony_ci inline bool release(int start, int end); 114cb93a386Sopenharmony_ci 115cb93a386Sopenharmony_ci /** 116cb93a386Sopenharmony_ci * Resize a previously reserved byte range of offset 'start' (inclusive) to 'end' 117cb93a386Sopenharmony_ci * (exclusive). 'deltaBytes' is the SIGNED change to length of the reservation. 118cb93a386Sopenharmony_ci * 119cb93a386Sopenharmony_ci * When negative this means the reservation is shrunk and the new length is (end - start - 120cb93a386Sopenharmony_ci * |deltaBytes|). If this new length would be 0, the byte range can no longer be used (as if 121cb93a386Sopenharmony_ci * it were released instead). Asserts that it would not shrink the reservation below 0. 122cb93a386Sopenharmony_ci * 123cb93a386Sopenharmony_ci * If 'deltaBytes' is positive, the allocator attempts to increase the length of the 124cb93a386Sopenharmony_ci * reservation. If 'deltaBytes' is less than or equal to avail() and it was the last 125cb93a386Sopenharmony_ci * allocation in the block, it can be resized. If there is not enough available bytes to 126cb93a386Sopenharmony_ci * accommodate the increase in size, or another allocation is blocking the increase in size, 127cb93a386Sopenharmony_ci * then false will be returned and the reserved byte range is unmodified. 128cb93a386Sopenharmony_ci */ 129cb93a386Sopenharmony_ci inline bool resize(int start, int end, int deltaBytes); 130cb93a386Sopenharmony_ci 131cb93a386Sopenharmony_ci private: 132cb93a386Sopenharmony_ci friend class SkBlockAllocator; 133cb93a386Sopenharmony_ci 134cb93a386Sopenharmony_ci Block(Block* prev, int allocationSize); 135cb93a386Sopenharmony_ci 136cb93a386Sopenharmony_ci // We poison the unallocated space in a Block to allow ASAN to catch invalid writes. 137cb93a386Sopenharmony_ci void poisonRange(int start, int end) { 138cb93a386Sopenharmony_ci sk_asan_poison_memory_region(reinterpret_cast<char*>(this) + start, end - start); 139cb93a386Sopenharmony_ci } 140cb93a386Sopenharmony_ci void unpoisonRange(int start, int end) { 141cb93a386Sopenharmony_ci sk_asan_unpoison_memory_region(reinterpret_cast<char*>(this) + start, end - start); 142cb93a386Sopenharmony_ci } 143cb93a386Sopenharmony_ci 144cb93a386Sopenharmony_ci // Get fCursor, but aligned such that ptr(rval) satisfies Align. 145cb93a386Sopenharmony_ci template <size_t Align, size_t Padding> 146cb93a386Sopenharmony_ci int cursor() const { return this->alignedOffset<Align, Padding>(fCursor); } 147cb93a386Sopenharmony_ci 148cb93a386Sopenharmony_ci template <size_t Align, size_t Padding> 149cb93a386Sopenharmony_ci int alignedOffset(int offset) const; 150cb93a386Sopenharmony_ci 151cb93a386Sopenharmony_ci bool isScratch() const { return fCursor < 0; } 152cb93a386Sopenharmony_ci void markAsScratch() { 153cb93a386Sopenharmony_ci fCursor = -1; 154cb93a386Sopenharmony_ci this->poisonRange(kDataStart, fSize); 155cb93a386Sopenharmony_ci } 156cb93a386Sopenharmony_ci 157cb93a386Sopenharmony_ci SkDEBUGCODE(int fSentinel;) // known value to check for bad back pointers to blocks 158cb93a386Sopenharmony_ci 159cb93a386Sopenharmony_ci Block* fNext; // doubly-linked list of blocks 160cb93a386Sopenharmony_ci Block* fPrev; 161cb93a386Sopenharmony_ci 162cb93a386Sopenharmony_ci // Each block tracks its own cursor because as later blocks are released, an older block 163cb93a386Sopenharmony_ci // may become the active tail again. 164cb93a386Sopenharmony_ci int fSize; // includes the size of the BlockHeader and requested metadata 165cb93a386Sopenharmony_ci int fCursor; // (this + fCursor) points to next available allocation 166cb93a386Sopenharmony_ci int fMetadata; 167cb93a386Sopenharmony_ci 168cb93a386Sopenharmony_ci // On release builds, a Block's other 2 pointers and 3 int fields leaves 4 bytes of padding 169cb93a386Sopenharmony_ci // for 8 and 16 aligned systems. Currently this is only manipulated in the head block for 170cb93a386Sopenharmony_ci // an allocator-level metadata and is explicitly not reset when the head block is "released" 171cb93a386Sopenharmony_ci // Down the road we could instead choose to offer multiple metadata slots per block. 172cb93a386Sopenharmony_ci int fAllocatorMetadata; 173cb93a386Sopenharmony_ci }; 174cb93a386Sopenharmony_ci 175cb93a386Sopenharmony_ci // The size of the head block is determined by 'additionalPreallocBytes'. Subsequent heap blocks 176cb93a386Sopenharmony_ci // are determined by 'policy' and 'blockIncrementBytes', although 'blockIncrementBytes' will be 177cb93a386Sopenharmony_ci // aligned to std::max_align_t. 178cb93a386Sopenharmony_ci // 179cb93a386Sopenharmony_ci // When 'additionalPreallocBytes' > 0, the allocator assumes that many extra bytes immediately 180cb93a386Sopenharmony_ci // after the allocator can be used by its inline head block. This is useful when the allocator 181cb93a386Sopenharmony_ci // is in-place new'ed into a larger block of memory, but it should remain set to 0 if stack 182cb93a386Sopenharmony_ci // allocated or if the class layout does not guarantee that space is present. 183cb93a386Sopenharmony_ci SkBlockAllocator(GrowthPolicy policy, size_t blockIncrementBytes, 184cb93a386Sopenharmony_ci size_t additionalPreallocBytes = 0); 185cb93a386Sopenharmony_ci 186cb93a386Sopenharmony_ci ~SkBlockAllocator() { this->reset(); } 187cb93a386Sopenharmony_ci void operator delete(void* p) { ::operator delete(p); } 188cb93a386Sopenharmony_ci 189cb93a386Sopenharmony_ci /** 190cb93a386Sopenharmony_ci * Helper to calculate the minimum number of bytes needed for heap block size, under the 191cb93a386Sopenharmony_ci * assumption that Align will be the requested alignment of the first call to allocate(). 192cb93a386Sopenharmony_ci * Ex. To store N instances of T in a heap block, the 'blockIncrementBytes' should be set to 193cb93a386Sopenharmony_ci * BlockOverhead<alignof(T)>() + N * sizeof(T) when making the SkBlockAllocator. 194cb93a386Sopenharmony_ci */ 195cb93a386Sopenharmony_ci template<size_t Align = 1, size_t Padding = 0> 196cb93a386Sopenharmony_ci static constexpr size_t BlockOverhead(); 197cb93a386Sopenharmony_ci 198cb93a386Sopenharmony_ci /** 199cb93a386Sopenharmony_ci * Helper to calculate the minimum number of bytes needed for a preallocation, under the 200cb93a386Sopenharmony_ci * assumption that Align will be the requested alignment of the first call to allocate(). 201cb93a386Sopenharmony_ci * Ex. To preallocate a SkSBlockAllocator to hold N instances of T, its arge should be 202cb93a386Sopenharmony_ci * Overhead<alignof(T)>() + N * sizeof(T) 203cb93a386Sopenharmony_ci */ 204cb93a386Sopenharmony_ci template<size_t Align = 1, size_t Padding = 0> 205cb93a386Sopenharmony_ci static constexpr size_t Overhead(); 206cb93a386Sopenharmony_ci 207cb93a386Sopenharmony_ci /** 208cb93a386Sopenharmony_ci * Return the total number of bytes of the allocator, including its instance overhead, per-block 209cb93a386Sopenharmony_ci * overhead and space used for allocations. 210cb93a386Sopenharmony_ci */ 211cb93a386Sopenharmony_ci size_t totalSize() const; 212cb93a386Sopenharmony_ci /** 213cb93a386Sopenharmony_ci * Return the total number of bytes usable for allocations. This includes bytes that have 214cb93a386Sopenharmony_ci * been reserved already by a call to allocate() and bytes that are still available. It is 215cb93a386Sopenharmony_ci * totalSize() minus all allocator and block-level overhead. 216cb93a386Sopenharmony_ci */ 217cb93a386Sopenharmony_ci size_t totalUsableSpace() const; 218cb93a386Sopenharmony_ci /** 219cb93a386Sopenharmony_ci * Return the total number of usable bytes that have been reserved by allocations. This will 220cb93a386Sopenharmony_ci * be less than or equal to totalUsableSpace(). 221cb93a386Sopenharmony_ci */ 222cb93a386Sopenharmony_ci size_t totalSpaceInUse() const; 223cb93a386Sopenharmony_ci 224cb93a386Sopenharmony_ci /** 225cb93a386Sopenharmony_ci * Return the total number of bytes that were pre-allocated for the SkBlockAllocator. This will 226cb93a386Sopenharmony_ci * include 'additionalPreallocBytes' passed to the constructor, and represents what the total 227cb93a386Sopenharmony_ci * size would become after a call to reset(). 228cb93a386Sopenharmony_ci */ 229cb93a386Sopenharmony_ci size_t preallocSize() const { 230cb93a386Sopenharmony_ci // Don't double count fHead's Block overhead in both sizeof(SkBlockAllocator) and fSize. 231cb93a386Sopenharmony_ci return sizeof(SkBlockAllocator) + fHead.fSize - BaseHeadBlockSize(); 232cb93a386Sopenharmony_ci } 233cb93a386Sopenharmony_ci /** 234cb93a386Sopenharmony_ci * Return the usable size of the inline head block; this will be equal to 235cb93a386Sopenharmony_ci * 'additionalPreallocBytes' plus any alignment padding that the system had to add to Block. 236cb93a386Sopenharmony_ci * The returned value represents what could be allocated before a heap block is be created. 237cb93a386Sopenharmony_ci */ 238cb93a386Sopenharmony_ci size_t preallocUsableSpace() const { 239cb93a386Sopenharmony_ci return fHead.fSize - kDataStart; 240cb93a386Sopenharmony_ci } 241cb93a386Sopenharmony_ci 242cb93a386Sopenharmony_ci /** 243cb93a386Sopenharmony_ci * Get the current value of the allocator-level metadata (a user-oriented slot). This is 244cb93a386Sopenharmony_ci * separate from any block-level metadata, but can serve a similar purpose to compactly support 245cb93a386Sopenharmony_ci * data collections on top of SkBlockAllocator. 246cb93a386Sopenharmony_ci */ 247cb93a386Sopenharmony_ci int metadata() const { return fHead.fAllocatorMetadata; } 248cb93a386Sopenharmony_ci 249cb93a386Sopenharmony_ci /** 250cb93a386Sopenharmony_ci * Set the current value of the allocator-level metadata. 251cb93a386Sopenharmony_ci */ 252cb93a386Sopenharmony_ci void setMetadata(int value) { fHead.fAllocatorMetadata = value; } 253cb93a386Sopenharmony_ci 254cb93a386Sopenharmony_ci /** 255cb93a386Sopenharmony_ci * Reserve space that will hold 'size' bytes. This will automatically allocate a new block if 256cb93a386Sopenharmony_ci * there is not enough available space in the current block to provide 'size' bytes. The 257cb93a386Sopenharmony_ci * returned ByteRange tuple specifies the Block owning the reserved memory, the full byte range, 258cb93a386Sopenharmony_ci * and the aligned offset within that range to use for the user-facing pointer. The following 259cb93a386Sopenharmony_ci * invariants hold: 260cb93a386Sopenharmony_ci * 261cb93a386Sopenharmony_ci * 1. block->ptr(alignedOffset) is aligned to Align 262cb93a386Sopenharmony_ci * 2. end - alignedOffset == size 263cb93a386Sopenharmony_ci * 3. Padding <= alignedOffset - start <= Padding + Align - 1 264cb93a386Sopenharmony_ci * 265cb93a386Sopenharmony_ci * Invariant #3, when Padding > 0, allows intermediate allocators to embed metadata along with 266cb93a386Sopenharmony_ci * the allocations. If the Padding bytes are used for some 'struct Meta', then 267cb93a386Sopenharmony_ci * ptr(alignedOffset - sizeof(Meta)) can be safely used as a Meta* if Meta's alignment 268cb93a386Sopenharmony_ci * requirements are less than or equal to the alignment specified in allocate<>. This can be 269cb93a386Sopenharmony_ci * easily guaranteed by using the pattern: 270cb93a386Sopenharmony_ci * 271cb93a386Sopenharmony_ci * allocate<max(UserAlign, alignof(Meta)), sizeof(Meta)>(userSize); 272cb93a386Sopenharmony_ci * 273cb93a386Sopenharmony_ci * This ensures that ptr(alignedOffset) will always satisfy UserAlign and 274cb93a386Sopenharmony_ci * ptr(alignedOffset - sizeof(Meta)) will always satisfy alignof(Meta). Alternatively, memcpy 275cb93a386Sopenharmony_ci * can be used to read and write values between start and alignedOffset without worrying about 276cb93a386Sopenharmony_ci * alignment requirements of the metadata. 277cb93a386Sopenharmony_ci * 278cb93a386Sopenharmony_ci * For over-aligned allocations, the alignedOffset (as an int) may not be a multiple of Align, 279cb93a386Sopenharmony_ci * but the result of ptr(alignedOffset) will be a multiple of Align. 280cb93a386Sopenharmony_ci */ 281cb93a386Sopenharmony_ci template <size_t Align, size_t Padding = 0> 282cb93a386Sopenharmony_ci ByteRange allocate(size_t size); 283cb93a386Sopenharmony_ci 284cb93a386Sopenharmony_ci enum ReserveFlags : unsigned { 285cb93a386Sopenharmony_ci // If provided to reserve(), the input 'size' will be rounded up to the next size determined 286cb93a386Sopenharmony_ci // by the growth policy of the SkBlockAllocator. If not, 'size' will be aligned to max_align 287cb93a386Sopenharmony_ci kIgnoreGrowthPolicy_Flag = 0b01, 288cb93a386Sopenharmony_ci // If provided to reserve(), the number of available bytes of the current block will not 289cb93a386Sopenharmony_ci // be used to satisfy the reservation (assuming the contiguous range was long enough to 290cb93a386Sopenharmony_ci // begin with). 291cb93a386Sopenharmony_ci kIgnoreExistingBytes_Flag = 0b10, 292cb93a386Sopenharmony_ci 293cb93a386Sopenharmony_ci kNo_ReserveFlags = 0b00 294cb93a386Sopenharmony_ci }; 295cb93a386Sopenharmony_ci 296cb93a386Sopenharmony_ci /** 297cb93a386Sopenharmony_ci * Ensure the block allocator has 'size' contiguous available bytes. After calling this 298cb93a386Sopenharmony_ci * function, currentBlock()->avail<Align, Padding>() may still report less than 'size' if the 299cb93a386Sopenharmony_ci * reserved space was added as a scratch block. This is done so that anything remaining in 300cb93a386Sopenharmony_ci * the current block can still be used if a smaller-than-size allocation is requested. If 'size' 301cb93a386Sopenharmony_ci * is requested by a subsequent allocation, the scratch block will automatically be activated 302cb93a386Sopenharmony_ci * and the request will not itself trigger any malloc. 303cb93a386Sopenharmony_ci * 304cb93a386Sopenharmony_ci * The optional 'flags' controls how the input size is allocated; by default it will attempt 305cb93a386Sopenharmony_ci * to use available contiguous bytes in the current block and will respect the growth policy 306cb93a386Sopenharmony_ci * of the allocator. 307cb93a386Sopenharmony_ci */ 308cb93a386Sopenharmony_ci template <size_t Align = 1, size_t Padding = 0> 309cb93a386Sopenharmony_ci void reserve(size_t size, ReserveFlags flags = kNo_ReserveFlags); 310cb93a386Sopenharmony_ci 311cb93a386Sopenharmony_ci /** 312cb93a386Sopenharmony_ci * Return a pointer to the start of the current block. This will never be null. 313cb93a386Sopenharmony_ci */ 314cb93a386Sopenharmony_ci const Block* currentBlock() const { return fTail; } 315cb93a386Sopenharmony_ci Block* currentBlock() { return fTail; } 316cb93a386Sopenharmony_ci 317cb93a386Sopenharmony_ci const Block* headBlock() const { return &fHead; } 318cb93a386Sopenharmony_ci Block* headBlock() { return &fHead; } 319cb93a386Sopenharmony_ci 320cb93a386Sopenharmony_ci /** 321cb93a386Sopenharmony_ci * Return the block that owns the allocated 'ptr'. Assuming that earlier, an allocation was 322cb93a386Sopenharmony_ci * returned as {b, start, alignedOffset, end}, and 'p = b->ptr(alignedOffset)', then a call 323cb93a386Sopenharmony_ci * to 'owningBlock<Align, Padding>(p, start) == b'. 324cb93a386Sopenharmony_ci * 325cb93a386Sopenharmony_ci * If calling code has already made a pointer to their metadata, i.e. 'm = p - Padding', then 326cb93a386Sopenharmony_ci * 'owningBlock<Align, 0>(m, start)' will also return b, allowing you to recover the block from 327cb93a386Sopenharmony_ci * the metadata pointer. 328cb93a386Sopenharmony_ci * 329cb93a386Sopenharmony_ci * If calling code has access to the original alignedOffset, this function should not be used 330cb93a386Sopenharmony_ci * since the owning block is just 'p - alignedOffset', regardless of original Align or Padding. 331cb93a386Sopenharmony_ci */ 332cb93a386Sopenharmony_ci template <size_t Align, size_t Padding = 0> 333cb93a386Sopenharmony_ci Block* owningBlock(const void* ptr, int start); 334cb93a386Sopenharmony_ci 335cb93a386Sopenharmony_ci template <size_t Align, size_t Padding = 0> 336cb93a386Sopenharmony_ci const Block* owningBlock(const void* ptr, int start) const { 337cb93a386Sopenharmony_ci return const_cast<SkBlockAllocator*>(this)->owningBlock<Align, Padding>(ptr, start); 338cb93a386Sopenharmony_ci } 339cb93a386Sopenharmony_ci 340cb93a386Sopenharmony_ci /** 341cb93a386Sopenharmony_ci * Find the owning block of the allocated pointer, 'p'. Without any additional information this 342cb93a386Sopenharmony_ci * is O(N) on the number of allocated blocks. 343cb93a386Sopenharmony_ci */ 344cb93a386Sopenharmony_ci Block* findOwningBlock(const void* ptr); 345cb93a386Sopenharmony_ci const Block* findOwningBlock(const void* ptr) const { 346cb93a386Sopenharmony_ci return const_cast<SkBlockAllocator*>(this)->findOwningBlock(ptr); 347cb93a386Sopenharmony_ci } 348cb93a386Sopenharmony_ci 349cb93a386Sopenharmony_ci /** 350cb93a386Sopenharmony_ci * Explicitly free an entire block, invalidating any remaining allocations from the block. 351cb93a386Sopenharmony_ci * SkBlockAllocator will release all alive blocks automatically when it is destroyed, but this 352cb93a386Sopenharmony_ci * function can be used to reclaim memory over the lifetime of the allocator. The provided 353cb93a386Sopenharmony_ci * 'block' pointer must have previously come from a call to currentBlock() or allocate(). 354cb93a386Sopenharmony_ci * 355cb93a386Sopenharmony_ci * If 'block' represents the inline-allocated head block, its cursor and metadata are instead 356cb93a386Sopenharmony_ci * reset to their defaults. 357cb93a386Sopenharmony_ci * 358cb93a386Sopenharmony_ci * If the block is not the head block, it may be kept as a scratch block to be reused for 359cb93a386Sopenharmony_ci * subsequent allocation requests, instead of making an entirely new block. A scratch block is 360cb93a386Sopenharmony_ci * not visible when iterating over blocks but is reported in the total size of the allocator. 361cb93a386Sopenharmony_ci */ 362cb93a386Sopenharmony_ci void releaseBlock(Block* block); 363cb93a386Sopenharmony_ci 364cb93a386Sopenharmony_ci /** 365cb93a386Sopenharmony_ci * Detach every heap-allocated block owned by 'other' and concatenate them to this allocator's 366cb93a386Sopenharmony_ci * list of blocks. This memory is now managed by this allocator. Since this only transfers 367cb93a386Sopenharmony_ci * ownership of a Block, and a Block itself does not move, any previous allocations remain 368cb93a386Sopenharmony_ci * valid and associated with their original Block instances. SkBlockAllocator-level functions 369cb93a386Sopenharmony_ci * that accept allocated pointers (e.g. findOwningBlock), must now use this allocator and not 370cb93a386Sopenharmony_ci * 'other' for these allocations. 371cb93a386Sopenharmony_ci * 372cb93a386Sopenharmony_ci * The head block of 'other' cannot be stolen, so higher-level allocators and memory structures 373cb93a386Sopenharmony_ci * must handle that data differently. 374cb93a386Sopenharmony_ci */ 375cb93a386Sopenharmony_ci void stealHeapBlocks(SkBlockAllocator* other); 376cb93a386Sopenharmony_ci 377cb93a386Sopenharmony_ci /** 378cb93a386Sopenharmony_ci * Explicitly free all blocks (invalidating all allocations), and resets the head block to its 379cb93a386Sopenharmony_ci * default state. The allocator-level metadata is reset to 0 as well. 380cb93a386Sopenharmony_ci */ 381cb93a386Sopenharmony_ci void reset(); 382cb93a386Sopenharmony_ci 383cb93a386Sopenharmony_ci /** 384cb93a386Sopenharmony_ci * Remove any reserved scratch space, either from calling reserve() or releaseBlock(). 385cb93a386Sopenharmony_ci */ 386cb93a386Sopenharmony_ci void resetScratchSpace(); 387cb93a386Sopenharmony_ci 388cb93a386Sopenharmony_ci template <bool Forward, bool Const> class BlockIter; 389cb93a386Sopenharmony_ci 390cb93a386Sopenharmony_ci /** 391cb93a386Sopenharmony_ci * Clients can iterate over all active Blocks in the SkBlockAllocator using for loops: 392cb93a386Sopenharmony_ci * 393cb93a386Sopenharmony_ci * Forward iteration from head to tail block (or non-const variant): 394cb93a386Sopenharmony_ci * for (const Block* b : this->blocks()) { } 395cb93a386Sopenharmony_ci * Reverse iteration from tail to head block: 396cb93a386Sopenharmony_ci * for (const Block* b : this->rblocks()) { } 397cb93a386Sopenharmony_ci * 398cb93a386Sopenharmony_ci * It is safe to call releaseBlock() on the active block while looping. 399cb93a386Sopenharmony_ci */ 400cb93a386Sopenharmony_ci inline BlockIter<true, false> blocks(); 401cb93a386Sopenharmony_ci inline BlockIter<true, true> blocks() const; 402cb93a386Sopenharmony_ci inline BlockIter<false, false> rblocks(); 403cb93a386Sopenharmony_ci inline BlockIter<false, true> rblocks() const; 404cb93a386Sopenharmony_ci 405cb93a386Sopenharmony_ci#ifdef SK_DEBUG 406cb93a386Sopenharmony_ci inline static constexpr int kAssignedMarker = 0xBEEFFACE; 407cb93a386Sopenharmony_ci inline static constexpr int kFreedMarker = 0xCAFEBABE; 408cb93a386Sopenharmony_ci 409cb93a386Sopenharmony_ci void validate() const; 410cb93a386Sopenharmony_ci#endif 411cb93a386Sopenharmony_ci 412cb93a386Sopenharmony_ciprivate: 413cb93a386Sopenharmony_ci friend class BlockAllocatorTestAccess; 414cb93a386Sopenharmony_ci friend class TBlockListTestAccess; 415cb93a386Sopenharmony_ci 416cb93a386Sopenharmony_ci inline static constexpr int kDataStart = sizeof(Block); 417cb93a386Sopenharmony_ci #ifdef SK_FORCE_8_BYTE_ALIGNMENT 418cb93a386Sopenharmony_ci // This is an issue for WASM builds using emscripten, which had std::max_align_t = 16, but 419cb93a386Sopenharmony_ci // was returning pointers only aligned to 8 bytes. 420cb93a386Sopenharmony_ci // https://github.com/emscripten-core/emscripten/issues/10072 421cb93a386Sopenharmony_ci // 422cb93a386Sopenharmony_ci // Setting this to 8 will let SkBlockAllocator properly correct for the pointer address if 423cb93a386Sopenharmony_ci // a 16-byte aligned allocation is requested in wasm (unlikely since we don't use long 424cb93a386Sopenharmony_ci // doubles). 425cb93a386Sopenharmony_ci inline static constexpr size_t kAddressAlign = 8; 426cb93a386Sopenharmony_ci #else 427cb93a386Sopenharmony_ci // The alignment Block addresses will be at when created using operator new 428cb93a386Sopenharmony_ci // (spec-compliant is pointers are aligned to max_align_t). 429cb93a386Sopenharmony_ci inline static constexpr size_t kAddressAlign = alignof(std::max_align_t); 430cb93a386Sopenharmony_ci #endif 431cb93a386Sopenharmony_ci 432cb93a386Sopenharmony_ci // Calculates the size of a new Block required to store a kMaxAllocationSize request for the 433cb93a386Sopenharmony_ci // given alignment and padding bytes. Also represents maximum valid fCursor value in a Block. 434cb93a386Sopenharmony_ci template<size_t Align, size_t Padding> 435cb93a386Sopenharmony_ci static constexpr size_t MaxBlockSize(); 436cb93a386Sopenharmony_ci 437cb93a386Sopenharmony_ci static constexpr int BaseHeadBlockSize() { 438cb93a386Sopenharmony_ci return sizeof(SkBlockAllocator) - offsetof(SkBlockAllocator, fHead); 439cb93a386Sopenharmony_ci } 440cb93a386Sopenharmony_ci 441cb93a386Sopenharmony_ci // Append a new block to the end of the block linked list, updating fTail. 'minSize' must 442cb93a386Sopenharmony_ci // have enough room for sizeof(Block). 'maxSize' is the upper limit of fSize for the new block 443cb93a386Sopenharmony_ci // that will preserve the static guarantees SkBlockAllocator makes. 444cb93a386Sopenharmony_ci void addBlock(int minSize, int maxSize); 445cb93a386Sopenharmony_ci 446cb93a386Sopenharmony_ci int scratchBlockSize() const { return fHead.fPrev ? fHead.fPrev->fSize : 0; } 447cb93a386Sopenharmony_ci 448cb93a386Sopenharmony_ci Block* fTail; // All non-head blocks are heap allocated; tail will never be null. 449cb93a386Sopenharmony_ci 450cb93a386Sopenharmony_ci // All remaining state is packed into 64 bits to keep SkBlockAllocator at 16 bytes + head block 451cb93a386Sopenharmony_ci // (on a 64-bit system). 452cb93a386Sopenharmony_ci 453cb93a386Sopenharmony_ci // Growth of the block size is controlled by four factors: BlockIncrement, N0 and N1, and a 454cb93a386Sopenharmony_ci // policy defining how N0 is updated. When a new block is needed, we calculate N1' = N0 + N1. 455cb93a386Sopenharmony_ci // Depending on the policy, N0' = N0 (no growth or linear growth), or N0' = N1 (Fibonacci), or 456cb93a386Sopenharmony_ci // N0' = N1' (exponential). The size of the new block is N1' * BlockIncrement * MaxAlign, 457cb93a386Sopenharmony_ci // after which fN0 and fN1 store N0' and N1' clamped into 23 bits. With current bit allocations, 458cb93a386Sopenharmony_ci // N1' is limited to 2^24, and assuming MaxAlign=16, then BlockIncrement must be '2' in order to 459cb93a386Sopenharmony_ci // eventually reach the hard 2^29 size limit of SkBlockAllocator. 460cb93a386Sopenharmony_ci 461cb93a386Sopenharmony_ci // Next heap block size = (fBlockIncrement * alignof(std::max_align_t) * (fN0 + fN1)) 462cb93a386Sopenharmony_ci uint64_t fBlockIncrement : 16; 463cb93a386Sopenharmony_ci uint64_t fGrowthPolicy : 2; // GrowthPolicy 464cb93a386Sopenharmony_ci uint64_t fN0 : 23; // = 1 for linear/exp.; = 0 for fixed/fibonacci, initially 465cb93a386Sopenharmony_ci uint64_t fN1 : 23; // = 1 initially 466cb93a386Sopenharmony_ci 467cb93a386Sopenharmony_ci // Inline head block, must be at the end so that it can utilize any additional reserved space 468cb93a386Sopenharmony_ci // from the initial allocation. 469cb93a386Sopenharmony_ci // The head block's prev pointer may be non-null, which signifies a scratch block that may be 470cb93a386Sopenharmony_ci // reused instead of allocating an entirely new block (this helps when allocate+release calls 471cb93a386Sopenharmony_ci // bounce back and forth across the capacity of a block). 472cb93a386Sopenharmony_ci alignas(kAddressAlign) Block fHead; 473cb93a386Sopenharmony_ci 474cb93a386Sopenharmony_ci static_assert(kGrowthPolicyCount <= 4); 475cb93a386Sopenharmony_ci}; 476cb93a386Sopenharmony_ci 477cb93a386Sopenharmony_ci// A wrapper around SkBlockAllocator that includes preallocated storage for the head block. 478cb93a386Sopenharmony_ci// N will be the preallocSize() reported by the allocator. 479cb93a386Sopenharmony_citemplate<size_t N> 480cb93a386Sopenharmony_ciclass SkSBlockAllocator : SkNoncopyable { 481cb93a386Sopenharmony_cipublic: 482cb93a386Sopenharmony_ci using GrowthPolicy = SkBlockAllocator::GrowthPolicy; 483cb93a386Sopenharmony_ci 484cb93a386Sopenharmony_ci SkSBlockAllocator() { 485cb93a386Sopenharmony_ci new (fStorage) SkBlockAllocator(GrowthPolicy::kFixed, N, N - sizeof(SkBlockAllocator)); 486cb93a386Sopenharmony_ci } 487cb93a386Sopenharmony_ci explicit SkSBlockAllocator(GrowthPolicy policy) { 488cb93a386Sopenharmony_ci new (fStorage) SkBlockAllocator(policy, N, N - sizeof(SkBlockAllocator)); 489cb93a386Sopenharmony_ci } 490cb93a386Sopenharmony_ci 491cb93a386Sopenharmony_ci SkSBlockAllocator(GrowthPolicy policy, size_t blockIncrementBytes) { 492cb93a386Sopenharmony_ci new (fStorage) SkBlockAllocator(policy, blockIncrementBytes, N - sizeof(SkBlockAllocator)); 493cb93a386Sopenharmony_ci } 494cb93a386Sopenharmony_ci 495cb93a386Sopenharmony_ci ~SkSBlockAllocator() { 496cb93a386Sopenharmony_ci this->allocator()->~SkBlockAllocator(); 497cb93a386Sopenharmony_ci } 498cb93a386Sopenharmony_ci 499cb93a386Sopenharmony_ci SkBlockAllocator* operator->() { return this->allocator(); } 500cb93a386Sopenharmony_ci const SkBlockAllocator* operator->() const { return this->allocator(); } 501cb93a386Sopenharmony_ci 502cb93a386Sopenharmony_ci SkBlockAllocator* allocator() { return reinterpret_cast<SkBlockAllocator*>(fStorage); } 503cb93a386Sopenharmony_ci const SkBlockAllocator* allocator() const { 504cb93a386Sopenharmony_ci return reinterpret_cast<const SkBlockAllocator*>(fStorage); 505cb93a386Sopenharmony_ci } 506cb93a386Sopenharmony_ci 507cb93a386Sopenharmony_ciprivate: 508cb93a386Sopenharmony_ci static_assert(N >= sizeof(SkBlockAllocator)); 509cb93a386Sopenharmony_ci 510cb93a386Sopenharmony_ci // Will be used to placement new the allocator 511cb93a386Sopenharmony_ci alignas(SkBlockAllocator) char fStorage[N]; 512cb93a386Sopenharmony_ci}; 513cb93a386Sopenharmony_ci 514cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////////////////////////// 515cb93a386Sopenharmony_ci// Template and inline implementations 516cb93a386Sopenharmony_ci 517cb93a386Sopenharmony_ciSK_MAKE_BITFIELD_OPS(SkBlockAllocator::ReserveFlags) 518cb93a386Sopenharmony_ci 519cb93a386Sopenharmony_citemplate<size_t Align, size_t Padding> 520cb93a386Sopenharmony_ciconstexpr size_t SkBlockAllocator::BlockOverhead() { 521cb93a386Sopenharmony_ci static_assert(SkAlignTo(kDataStart + Padding, Align) >= sizeof(Block)); 522cb93a386Sopenharmony_ci return SkAlignTo(kDataStart + Padding, Align); 523cb93a386Sopenharmony_ci} 524cb93a386Sopenharmony_ci 525cb93a386Sopenharmony_citemplate<size_t Align, size_t Padding> 526cb93a386Sopenharmony_ciconstexpr size_t SkBlockAllocator::Overhead() { 527cb93a386Sopenharmony_ci // NOTE: On most platforms, SkBlockAllocator is packed; this is not the case on debug builds 528cb93a386Sopenharmony_ci // due to extra fields, or on WASM due to 4byte pointers but 16byte max align. 529cb93a386Sopenharmony_ci return std::max(sizeof(SkBlockAllocator), 530cb93a386Sopenharmony_ci offsetof(SkBlockAllocator, fHead) + BlockOverhead<Align, Padding>()); 531cb93a386Sopenharmony_ci} 532cb93a386Sopenharmony_ci 533cb93a386Sopenharmony_citemplate<size_t Align, size_t Padding> 534cb93a386Sopenharmony_ciconstexpr size_t SkBlockAllocator::MaxBlockSize() { 535cb93a386Sopenharmony_ci // Without loss of generality, assumes 'align' will be the largest encountered alignment for the 536cb93a386Sopenharmony_ci // allocator (if it's not, the largest align will be encountered by the compiler and pass/fail 537cb93a386Sopenharmony_ci // the same set of static asserts). 538cb93a386Sopenharmony_ci return BlockOverhead<Align, Padding>() + kMaxAllocationSize; 539cb93a386Sopenharmony_ci} 540cb93a386Sopenharmony_ci 541cb93a386Sopenharmony_citemplate<size_t Align, size_t Padding> 542cb93a386Sopenharmony_civoid SkBlockAllocator::reserve(size_t size, ReserveFlags flags) { 543cb93a386Sopenharmony_ci if (size > kMaxAllocationSize) { 544cb93a386Sopenharmony_ci SK_ABORT("Allocation too large (%zu bytes requested)", size); 545cb93a386Sopenharmony_ci } 546cb93a386Sopenharmony_ci int iSize = (int) size; 547cb93a386Sopenharmony_ci if ((flags & kIgnoreExistingBytes_Flag) || 548cb93a386Sopenharmony_ci this->currentBlock()->avail<Align, Padding>() < iSize) { 549cb93a386Sopenharmony_ci 550cb93a386Sopenharmony_ci int blockSize = BlockOverhead<Align, Padding>() + iSize; 551cb93a386Sopenharmony_ci int maxSize = (flags & kIgnoreGrowthPolicy_Flag) ? blockSize 552cb93a386Sopenharmony_ci : MaxBlockSize<Align, Padding>(); 553cb93a386Sopenharmony_ci SkASSERT((size_t) maxSize <= (MaxBlockSize<Align, Padding>())); 554cb93a386Sopenharmony_ci 555cb93a386Sopenharmony_ci SkDEBUGCODE(auto oldTail = fTail;) 556cb93a386Sopenharmony_ci this->addBlock(blockSize, maxSize); 557cb93a386Sopenharmony_ci SkASSERT(fTail != oldTail); 558cb93a386Sopenharmony_ci // Releasing the just added block will move it into scratch space, allowing the original 559cb93a386Sopenharmony_ci // tail's bytes to be used first before the scratch block is activated. 560cb93a386Sopenharmony_ci this->releaseBlock(fTail); 561cb93a386Sopenharmony_ci } 562cb93a386Sopenharmony_ci} 563cb93a386Sopenharmony_ci 564cb93a386Sopenharmony_citemplate <size_t Align, size_t Padding> 565cb93a386Sopenharmony_ciSkBlockAllocator::ByteRange SkBlockAllocator::allocate(size_t size) { 566cb93a386Sopenharmony_ci // Amount of extra space for a new block to make sure the allocation can succeed. 567cb93a386Sopenharmony_ci static constexpr int kBlockOverhead = (int) BlockOverhead<Align, Padding>(); 568cb93a386Sopenharmony_ci 569cb93a386Sopenharmony_ci // Ensures 'offset' and 'end' calculations will be valid 570cb93a386Sopenharmony_ci static_assert((kMaxAllocationSize + SkAlignTo(MaxBlockSize<Align, Padding>(), Align)) 571cb93a386Sopenharmony_ci <= (size_t) std::numeric_limits<int32_t>::max()); 572cb93a386Sopenharmony_ci // Ensures size + blockOverhead + addBlock's alignment operations will be valid 573cb93a386Sopenharmony_ci static_assert(kMaxAllocationSize + kBlockOverhead + ((1 << 12) - 1) // 4K align for large blocks 574cb93a386Sopenharmony_ci <= std::numeric_limits<int32_t>::max()); 575cb93a386Sopenharmony_ci 576cb93a386Sopenharmony_ci if (size > kMaxAllocationSize) { 577cb93a386Sopenharmony_ci SK_ABORT("Allocation too large (%zu bytes requested)", size); 578cb93a386Sopenharmony_ci } 579cb93a386Sopenharmony_ci 580cb93a386Sopenharmony_ci int iSize = (int) size; 581cb93a386Sopenharmony_ci int offset = fTail->cursor<Align, Padding>(); 582cb93a386Sopenharmony_ci int end = offset + iSize; 583cb93a386Sopenharmony_ci if (end > fTail->fSize) { 584cb93a386Sopenharmony_ci this->addBlock(iSize + kBlockOverhead, MaxBlockSize<Align, Padding>()); 585cb93a386Sopenharmony_ci offset = fTail->cursor<Align, Padding>(); 586cb93a386Sopenharmony_ci end = offset + iSize; 587cb93a386Sopenharmony_ci } 588cb93a386Sopenharmony_ci 589cb93a386Sopenharmony_ci // Check invariants 590cb93a386Sopenharmony_ci SkASSERT(end <= fTail->fSize); 591cb93a386Sopenharmony_ci SkASSERT(end - offset == iSize); 592cb93a386Sopenharmony_ci SkASSERT(offset - fTail->fCursor >= (int) Padding && 593cb93a386Sopenharmony_ci offset - fTail->fCursor <= (int) (Padding + Align - 1)); 594cb93a386Sopenharmony_ci SkASSERT(reinterpret_cast<uintptr_t>(fTail->ptr(offset)) % Align == 0); 595cb93a386Sopenharmony_ci 596cb93a386Sopenharmony_ci int start = fTail->fCursor; 597cb93a386Sopenharmony_ci fTail->fCursor = end; 598cb93a386Sopenharmony_ci 599cb93a386Sopenharmony_ci fTail->unpoisonRange(offset - Padding, end); 600cb93a386Sopenharmony_ci 601cb93a386Sopenharmony_ci return {fTail, start, offset, end}; 602cb93a386Sopenharmony_ci} 603cb93a386Sopenharmony_ci 604cb93a386Sopenharmony_citemplate <size_t Align, size_t Padding> 605cb93a386Sopenharmony_ciSkBlockAllocator::Block* SkBlockAllocator::owningBlock(const void* p, int start) { 606cb93a386Sopenharmony_ci // 'p' was originally formed by aligning 'block + start + Padding', producing the inequality: 607cb93a386Sopenharmony_ci // block + start + Padding <= p <= block + start + Padding + Align-1 608cb93a386Sopenharmony_ci // Rearranging this yields: 609cb93a386Sopenharmony_ci // block <= p - start - Padding <= block + Align-1 610cb93a386Sopenharmony_ci // Masking these terms by ~(Align-1) reconstructs 'block' if the alignment of the block is 611cb93a386Sopenharmony_ci // greater than or equal to Align (since block & ~(Align-1) == (block + Align-1) & ~(Align-1) 612cb93a386Sopenharmony_ci // in that case). Overalignment does not reduce to inequality unfortunately. 613cb93a386Sopenharmony_ci if /* constexpr */ (Align <= kAddressAlign) { 614cb93a386Sopenharmony_ci Block* block = reinterpret_cast<Block*>( 615cb93a386Sopenharmony_ci (reinterpret_cast<uintptr_t>(p) - start - Padding) & ~(Align - 1)); 616cb93a386Sopenharmony_ci SkASSERT(block->fSentinel == kAssignedMarker); 617cb93a386Sopenharmony_ci return block; 618cb93a386Sopenharmony_ci } else { 619cb93a386Sopenharmony_ci // There's not a constant-time expression available to reconstruct the block from 'p', 620cb93a386Sopenharmony_ci // but this is unlikely to happen frequently. 621cb93a386Sopenharmony_ci return this->findOwningBlock(p); 622cb93a386Sopenharmony_ci } 623cb93a386Sopenharmony_ci} 624cb93a386Sopenharmony_ci 625cb93a386Sopenharmony_citemplate <size_t Align, size_t Padding> 626cb93a386Sopenharmony_ciint SkBlockAllocator::Block::alignedOffset(int offset) const { 627cb93a386Sopenharmony_ci static_assert(SkIsPow2(Align)); 628cb93a386Sopenharmony_ci // Aligning adds (Padding + Align - 1) as an intermediate step, so ensure that can't overflow 629cb93a386Sopenharmony_ci static_assert(MaxBlockSize<Align, Padding>() + Padding + Align - 1 630cb93a386Sopenharmony_ci <= (size_t) std::numeric_limits<int32_t>::max()); 631cb93a386Sopenharmony_ci 632cb93a386Sopenharmony_ci if /* constexpr */ (Align <= kAddressAlign) { 633cb93a386Sopenharmony_ci // Same as SkAlignTo, but operates on ints instead of size_t 634cb93a386Sopenharmony_ci return (offset + Padding + Align - 1) & ~(Align - 1); 635cb93a386Sopenharmony_ci } else { 636cb93a386Sopenharmony_ci // Must take into account that 'this' may be starting at a pointer that doesn't satisfy the 637cb93a386Sopenharmony_ci // larger alignment request, so must align the entire pointer, not just offset 638cb93a386Sopenharmony_ci uintptr_t blockPtr = reinterpret_cast<uintptr_t>(this); 639cb93a386Sopenharmony_ci uintptr_t alignedPtr = (blockPtr + offset + Padding + Align - 1) & ~(Align - 1); 640cb93a386Sopenharmony_ci SkASSERT(alignedPtr - blockPtr <= (uintptr_t) std::numeric_limits<int32_t>::max()); 641cb93a386Sopenharmony_ci return (int) (alignedPtr - blockPtr); 642cb93a386Sopenharmony_ci } 643cb93a386Sopenharmony_ci} 644cb93a386Sopenharmony_ci 645cb93a386Sopenharmony_cibool SkBlockAllocator::Block::resize(int start, int end, int deltaBytes) { 646cb93a386Sopenharmony_ci SkASSERT(fSentinel == kAssignedMarker); 647cb93a386Sopenharmony_ci SkASSERT(start >= kDataStart && end <= fSize && start < end); 648cb93a386Sopenharmony_ci 649cb93a386Sopenharmony_ci if (deltaBytes > kMaxAllocationSize || deltaBytes < -kMaxAllocationSize) { 650cb93a386Sopenharmony_ci // Cannot possibly satisfy the resize and could overflow subsequent math 651cb93a386Sopenharmony_ci return false; 652cb93a386Sopenharmony_ci } 653cb93a386Sopenharmony_ci if (fCursor == end) { 654cb93a386Sopenharmony_ci int nextCursor = end + deltaBytes; 655cb93a386Sopenharmony_ci SkASSERT(nextCursor >= start); 656cb93a386Sopenharmony_ci // We still check nextCursor >= start for release builds that wouldn't assert. 657cb93a386Sopenharmony_ci if (nextCursor <= fSize && nextCursor >= start) { 658cb93a386Sopenharmony_ci if (nextCursor < fCursor) { 659cb93a386Sopenharmony_ci // The allocation got smaller; poison the space that can no longer be used. 660cb93a386Sopenharmony_ci this->poisonRange(nextCursor + 1, end); 661cb93a386Sopenharmony_ci } else { 662cb93a386Sopenharmony_ci // The allocation got larger; unpoison the space that can now be used. 663cb93a386Sopenharmony_ci this->unpoisonRange(end, nextCursor); 664cb93a386Sopenharmony_ci } 665cb93a386Sopenharmony_ci 666cb93a386Sopenharmony_ci fCursor = nextCursor; 667cb93a386Sopenharmony_ci return true; 668cb93a386Sopenharmony_ci } 669cb93a386Sopenharmony_ci } 670cb93a386Sopenharmony_ci return false; 671cb93a386Sopenharmony_ci} 672cb93a386Sopenharmony_ci 673cb93a386Sopenharmony_ci// NOTE: release is equivalent to resize(start, end, start - end), and the compiler can optimize 674cb93a386Sopenharmony_ci// most of the operations away, but it wasn't able to remove the unnecessary branch comparing the 675cb93a386Sopenharmony_ci// new cursor to the block size or old start, so release() gets a specialization. 676cb93a386Sopenharmony_cibool SkBlockAllocator::Block::release(int start, int end) { 677cb93a386Sopenharmony_ci SkASSERT(fSentinel == kAssignedMarker); 678cb93a386Sopenharmony_ci SkASSERT(start >= kDataStart && end <= fSize && start < end); 679cb93a386Sopenharmony_ci 680cb93a386Sopenharmony_ci this->poisonRange(start, end); 681cb93a386Sopenharmony_ci 682cb93a386Sopenharmony_ci if (fCursor == end) { 683cb93a386Sopenharmony_ci fCursor = start; 684cb93a386Sopenharmony_ci return true; 685cb93a386Sopenharmony_ci } else { 686cb93a386Sopenharmony_ci return false; 687cb93a386Sopenharmony_ci } 688cb93a386Sopenharmony_ci} 689cb93a386Sopenharmony_ci 690cb93a386Sopenharmony_ci///////// Block iteration 691cb93a386Sopenharmony_citemplate <bool Forward, bool Const> 692cb93a386Sopenharmony_ciclass SkBlockAllocator::BlockIter { 693cb93a386Sopenharmony_ciprivate: 694cb93a386Sopenharmony_ci using BlockT = typename std::conditional<Const, const Block, Block>::type; 695cb93a386Sopenharmony_ci using AllocatorT = 696cb93a386Sopenharmony_ci typename std::conditional<Const, const SkBlockAllocator, SkBlockAllocator>::type; 697cb93a386Sopenharmony_ci 698cb93a386Sopenharmony_cipublic: 699cb93a386Sopenharmony_ci BlockIter(AllocatorT* allocator) : fAllocator(allocator) {} 700cb93a386Sopenharmony_ci 701cb93a386Sopenharmony_ci class Item { 702cb93a386Sopenharmony_ci public: 703cb93a386Sopenharmony_ci bool operator!=(const Item& other) const { return fBlock != other.fBlock; } 704cb93a386Sopenharmony_ci 705cb93a386Sopenharmony_ci BlockT* operator*() const { return fBlock; } 706cb93a386Sopenharmony_ci 707cb93a386Sopenharmony_ci Item& operator++() { 708cb93a386Sopenharmony_ci this->advance(fNext); 709cb93a386Sopenharmony_ci return *this; 710cb93a386Sopenharmony_ci } 711cb93a386Sopenharmony_ci 712cb93a386Sopenharmony_ci private: 713cb93a386Sopenharmony_ci friend BlockIter; 714cb93a386Sopenharmony_ci 715cb93a386Sopenharmony_ci Item(BlockT* block) { this->advance(block); } 716cb93a386Sopenharmony_ci 717cb93a386Sopenharmony_ci void advance(BlockT* block) { 718cb93a386Sopenharmony_ci fBlock = block; 719cb93a386Sopenharmony_ci fNext = block ? (Forward ? block->fNext : block->fPrev) : nullptr; 720cb93a386Sopenharmony_ci if (!Forward && fNext && fNext->isScratch()) { 721cb93a386Sopenharmony_ci // For reverse-iteration only, we need to stop at the head, not the scratch block 722cb93a386Sopenharmony_ci // possibly stashed in head->prev. 723cb93a386Sopenharmony_ci fNext = nullptr; 724cb93a386Sopenharmony_ci } 725cb93a386Sopenharmony_ci SkASSERT(!fNext || !fNext->isScratch()); 726cb93a386Sopenharmony_ci } 727cb93a386Sopenharmony_ci 728cb93a386Sopenharmony_ci BlockT* fBlock; 729cb93a386Sopenharmony_ci // Cache this before operator++ so that fBlock can be released during iteration 730cb93a386Sopenharmony_ci BlockT* fNext; 731cb93a386Sopenharmony_ci }; 732cb93a386Sopenharmony_ci 733cb93a386Sopenharmony_ci Item begin() const { return Item(Forward ? &fAllocator->fHead : fAllocator->fTail); } 734cb93a386Sopenharmony_ci Item end() const { return Item(nullptr); } 735cb93a386Sopenharmony_ci 736cb93a386Sopenharmony_ciprivate: 737cb93a386Sopenharmony_ci AllocatorT* fAllocator; 738cb93a386Sopenharmony_ci}; 739cb93a386Sopenharmony_ci 740cb93a386Sopenharmony_ciSkBlockAllocator::BlockIter<true, false> SkBlockAllocator::blocks() { 741cb93a386Sopenharmony_ci return BlockIter<true, false>(this); 742cb93a386Sopenharmony_ci} 743cb93a386Sopenharmony_ciSkBlockAllocator::BlockIter<true, true> SkBlockAllocator::blocks() const { 744cb93a386Sopenharmony_ci return BlockIter<true, true>(this); 745cb93a386Sopenharmony_ci} 746cb93a386Sopenharmony_ciSkBlockAllocator::BlockIter<false, false> SkBlockAllocator::rblocks() { 747cb93a386Sopenharmony_ci return BlockIter<false, false>(this); 748cb93a386Sopenharmony_ci} 749cb93a386Sopenharmony_ciSkBlockAllocator::BlockIter<false, true> SkBlockAllocator::rblocks() const { 750cb93a386Sopenharmony_ci return BlockIter<false, true>(this); 751cb93a386Sopenharmony_ci} 752cb93a386Sopenharmony_ci 753cb93a386Sopenharmony_ci#endif // SkBlockAllocator_DEFINED 754