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