blob: f39ebee208a23970a97d672ecf0a022086771690 [file] [log] [blame]
Mike Klein58b13062016-11-11 10:38:49 -05001/*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Brian Salomoncbcb0a12017-11-19 13:20:13 -05008#ifndef SkArenaAlloc_DEFINED
9#define SkArenaAlloc_DEFINED
Mike Klein58b13062016-11-11 10:38:49 -050010
Ben Wagnere1789af2018-07-25 15:20:25 -040011#include "../private/SkTFitsIn.h"
12
Mike Klein5f444c92018-06-07 12:53:29 -040013#include <cassert>
Herb Derby0497f082017-01-13 11:30:44 -050014#include <cstddef>
Mike Klein5f444c92018-06-07 12:53:29 -040015#include <cstdint>
16#include <cstdlib>
17#include <cstring>
18#include <limits>
Mike Klein58b13062016-11-11 10:38:49 -050019#include <new>
Herb Derby0497f082017-01-13 11:30:44 -050020#include <type_traits>
Mike Klein58b13062016-11-11 10:38:49 -050021#include <utility>
22#include <vector>
23
Herb Derby0497f082017-01-13 11:30:44 -050024// SkArenaAlloc allocates object and destroys the allocated objects when destroyed. It's designed
25// to minimize the number of underlying block allocations. SkArenaAlloc allocates first out of an
26// (optional) user-provided block of memory, and when that's exhausted it allocates on the heap,
27// starting with an allocation of extraSize bytes. If your data (plus a small overhead) fits in
28// the user-provided block, SkArenaAlloc never uses the heap, and if it fits in extraSize bytes,
29// it'll use the heap only once. If you pass extraSize = 0, it allocates blocks for each call to
30// make<T>.
31//
32// Examples:
33//
34// char block[mostCasesSize];
Mike Klein9f752aa2018-06-07 13:13:54 -040035// SkArenaAlloc arena(block, mostCasesSize);
Herb Derby0497f082017-01-13 11:30:44 -050036//
37// If mostCasesSize is too large for the stack, you can use the following pattern.
38//
39// std::unique_ptr<char[]> block{new char[mostCasesSize]};
40// SkArenaAlloc arena(block.get(), mostCasesSize, almostAllCasesSize);
41//
Mike Klein9f752aa2018-06-07 13:13:54 -040042// If the program only sometimes allocates memory, use the following pattern.
Herb Derby0497f082017-01-13 11:30:44 -050043//
44// SkArenaAlloc arena(nullptr, 0, almostAllCasesSize);
45//
46// The storage does not necessarily need to be on the stack. Embedding the storage in a class also
47// works.
48//
49// class Foo {
50// char storage[mostCasesSize];
Mike Klein9f752aa2018-06-07 13:13:54 -040051// SkArenaAlloc arena (storage, mostCasesSize);
Herb Derby0497f082017-01-13 11:30:44 -050052// };
53//
54// In addition, the system is optimized to handle POD data including arrays of PODs (where
55// POD is really data with no destructors). For POD data it has zero overhead per item, and a
56// typical block overhead of 8 bytes. For non-POD objects there is a per item overhead of 4 bytes.
57// For arrays of non-POD objects there is a per array overhead of typically 8 bytes. There is an
58// addition overhead when switching from POD data to non-POD data of typically 8 bytes.
Herb Derby01254bc2017-03-03 15:09:43 -050059//
60// If additional blocks are needed they are increased exponentially. This strategy bounds the
Herb Derby7dd57b62017-03-08 14:17:49 -050061// recursion of the RunDtorsOnBlock to be limited to O(log size-of-memory). Block size grow using
62// the Fibonacci sequence which means that for 2^32 memory there are 48 allocations, and for 2^48
63// there are 71 allocations.
Herb Derby0497f082017-01-13 11:30:44 -050064class SkArenaAlloc {
65public:
Mike Klein9f752aa2018-06-07 13:13:54 -040066 SkArenaAlloc(char* block, size_t blockSize, size_t extraSize);
Herb Derby0497f082017-01-13 11:30:44 -050067
Mike Klein2e361a32018-06-07 12:57:05 -040068 SkArenaAlloc(size_t extraSize)
69 : SkArenaAlloc(nullptr, 0, extraSize)
Herb Derby2a0daee2017-01-20 16:58:06 -050070 {}
71
Herb Derby0497f082017-01-13 11:30:44 -050072 ~SkArenaAlloc();
73
74 template <typename T, typename... Args>
75 T* make(Args&&... args) {
Mike Klein5f444c92018-06-07 12:53:29 -040076 uint32_t size = ToU32(sizeof(T));
77 uint32_t alignment = ToU32(alignof(T));
Herb Derby0497f082017-01-13 11:30:44 -050078 char* objStart;
Mike Kleind62c4bd2018-06-07 10:00:49 -040079 if (std::is_trivially_destructible<T>::value) {
Herb Derby1bf3fc72017-02-17 10:45:47 -050080 objStart = this->allocObject(size, alignment);
81 fCursor = objStart + size;
Herb Derby0497f082017-01-13 11:30:44 -050082 } else {
Herb Derby1bf3fc72017-02-17 10:45:47 -050083 objStart = this->allocObjectWithFooter(size + sizeof(Footer), alignment);
Herb Derby39728d32017-01-25 17:05:05 -050084 // Can never be UB because max value is alignof(T).
Mike Klein5f444c92018-06-07 12:53:29 -040085 uint32_t padding = ToU32(objStart - fCursor);
Herb Derby0497f082017-01-13 11:30:44 -050086
87 // Advance to end of object to install footer.
Herb Derby1bf3fc72017-02-17 10:45:47 -050088 fCursor = objStart + size;
Herb Derby0497f082017-01-13 11:30:44 -050089 FooterAction* releaser = [](char* objEnd) {
90 char* objStart = objEnd - (sizeof(T) + sizeof(Footer));
91 ((T*)objStart)->~T();
92 return objStart;
93 };
94 this->installFooter(releaser, padding);
95 }
96
97 // This must be last to make objects with nested use of this allocator work.
98 return new(objStart) T(std::forward<Args>(args)...);
99 }
100
101 template <typename T>
102 T* makeArrayDefault(size_t count) {
Ben Wagnere1789af2018-07-25 15:20:25 -0400103 AssertRelease(SkTFitsIn<uint32_t>(count));
Mike Klein5f444c92018-06-07 12:53:29 -0400104 uint32_t safeCount = ToU32(count);
Herb Derby1bf3fc72017-02-17 10:45:47 -0500105 T* array = (T*)this->commonArrayAlloc<T>(safeCount);
Herb Derby0497f082017-01-13 11:30:44 -0500106
107 // If T is primitive then no initialization takes place.
Herb Derby1bf3fc72017-02-17 10:45:47 -0500108 for (size_t i = 0; i < safeCount; i++) {
Herb Derby0497f082017-01-13 11:30:44 -0500109 new (&array[i]) T;
110 }
111 return array;
112 }
113
114 template <typename T>
115 T* makeArray(size_t count) {
Ben Wagnere1789af2018-07-25 15:20:25 -0400116 AssertRelease(SkTFitsIn<uint32_t>(count));
Mike Klein5f444c92018-06-07 12:53:29 -0400117 uint32_t safeCount = ToU32(count);
Herb Derby1bf3fc72017-02-17 10:45:47 -0500118 T* array = (T*)this->commonArrayAlloc<T>(safeCount);
Herb Derby0497f082017-01-13 11:30:44 -0500119
120 // If T is primitive then the memory is initialized. For example, an array of chars will
121 // be zeroed.
Herb Derby1bf3fc72017-02-17 10:45:47 -0500122 for (size_t i = 0; i < safeCount; i++) {
Herb Derby0497f082017-01-13 11:30:44 -0500123 new (&array[i]) T();
124 }
125 return array;
126 }
127
Herb Derby6d64c542018-02-16 10:36:52 -0500128 // Only use makeBytesAlignedTo if none of the typed variants are impractical to use.
129 void* makeBytesAlignedTo(size_t size, size_t align) {
Ben Wagnere1789af2018-07-25 15:20:25 -0400130 AssertRelease(SkTFitsIn<uint32_t>(size));
Mike Klein5f444c92018-06-07 12:53:29 -0400131 auto objStart = this->allocObject(ToU32(size), ToU32(align));
Herb Derby6d64c542018-02-16 10:36:52 -0500132 fCursor = objStart + size;
133 return objStart;
134 }
135
Herb Derby0497f082017-01-13 11:30:44 -0500136 // Destroy all allocated objects, free any heap allocations.
137 void reset();
138
139private:
Mike Klein5f444c92018-06-07 12:53:29 -0400140 static void AssertRelease(bool cond) { if (!cond) { ::abort(); } }
141 static uint32_t ToU32(size_t v) {
Ben Wagnere1789af2018-07-25 15:20:25 -0400142 assert(SkTFitsIn<uint32_t>(v));
Mike Klein5f444c92018-06-07 12:53:29 -0400143 return (uint32_t)v;
144 }
145
Herb Derbyc4e6cfe2017-01-24 15:59:51 -0500146 using Footer = int64_t;
Herb Derby0497f082017-01-13 11:30:44 -0500147 using FooterAction = char* (char*);
148
Herb Derby593cb942017-01-19 14:28:49 -0500149 static char* SkipPod(char* footerEnd);
150 static void RunDtorsOnBlock(char* footerEnd);
151 static char* NextBlock(char* footerEnd);
Herb Derby0497f082017-01-13 11:30:44 -0500152
Herb Derby39728d32017-01-25 17:05:05 -0500153 void installFooter(FooterAction* releaser, uint32_t padding);
154 void installUint32Footer(FooterAction* action, uint32_t value, uint32_t padding);
155 void installPtrFooter(FooterAction* action, char* ptr, uint32_t padding);
Herb Derby0497f082017-01-13 11:30:44 -0500156
Herb Derby1bf3fc72017-02-17 10:45:47 -0500157 void ensureSpace(uint32_t size, uint32_t alignment);
Herb Derby0497f082017-01-13 11:30:44 -0500158
Mike Klein79162552017-05-24 12:19:02 -0400159 char* allocObject(uint32_t size, uint32_t alignment) {
160 uintptr_t mask = alignment - 1;
Herb Derby86183382017-08-15 17:30:58 -0400161 uintptr_t alignedOffset = (~reinterpret_cast<uintptr_t>(fCursor) + 1) & mask;
162 uintptr_t totalSize = size + alignedOffset;
Mike Klein5f444c92018-06-07 12:53:29 -0400163 AssertRelease(totalSize >= size);
Herb Derby86183382017-08-15 17:30:58 -0400164 if (totalSize > static_cast<uintptr_t>(fEnd - fCursor)) {
165 this->ensureSpace(size, alignment);
166 alignedOffset = (~reinterpret_cast<uintptr_t>(fCursor) + 1) & mask;
167 }
168 return fCursor + alignedOffset;
Mike Klein79162552017-05-24 12:19:02 -0400169 }
Herb Derby0497f082017-01-13 11:30:44 -0500170
Herb Derby1bf3fc72017-02-17 10:45:47 -0500171 char* allocObjectWithFooter(uint32_t sizeIncludingFooter, uint32_t alignment);
Herb Derby0497f082017-01-13 11:30:44 -0500172
173 template <typename T>
Herb Derby1bf3fc72017-02-17 10:45:47 -0500174 char* commonArrayAlloc(uint32_t count) {
Herb Derby0497f082017-01-13 11:30:44 -0500175 char* objStart;
Mike Klein5f444c92018-06-07 12:53:29 -0400176 AssertRelease(count <= std::numeric_limits<uint32_t>::max() / sizeof(T));
177 uint32_t arraySize = ToU32(count * sizeof(T));
178 uint32_t alignment = ToU32(alignof(T));
Herb Derby0497f082017-01-13 11:30:44 -0500179
Mike Kleind62c4bd2018-06-07 10:00:49 -0400180 if (std::is_trivially_destructible<T>::value) {
Herb Derby1bf3fc72017-02-17 10:45:47 -0500181 objStart = this->allocObject(arraySize, alignment);
Herb Derby0497f082017-01-13 11:30:44 -0500182 fCursor = objStart + arraySize;
183 } else {
Ben Wagner6229b122017-07-24 16:11:31 -0400184 constexpr uint32_t overhead = sizeof(Footer) + sizeof(uint32_t);
Mike Klein5f444c92018-06-07 12:53:29 -0400185 AssertRelease(arraySize <= std::numeric_limits<uint32_t>::max() - overhead);
Ben Wagner6229b122017-07-24 16:11:31 -0400186 uint32_t totalSize = arraySize + overhead;
Herb Derby1bf3fc72017-02-17 10:45:47 -0500187 objStart = this->allocObjectWithFooter(totalSize, alignment);
Herb Derby39728d32017-01-25 17:05:05 -0500188
189 // Can never be UB because max value is alignof(T).
Mike Klein5f444c92018-06-07 12:53:29 -0400190 uint32_t padding = ToU32(objStart - fCursor);
Herb Derby0497f082017-01-13 11:30:44 -0500191
192 // Advance to end of array to install footer.?
193 fCursor = objStart + arraySize;
Herb Derby593cb942017-01-19 14:28:49 -0500194 this->installUint32Footer(
195 [](char* footerEnd) {
196 char* objEnd = footerEnd - (sizeof(Footer) + sizeof(uint32_t));
197 uint32_t count;
198 memmove(&count, objEnd, sizeof(uint32_t));
199 char* objStart = objEnd - count * sizeof(T);
200 T* array = (T*) objStart;
201 for (uint32_t i = 0; i < count; i++) {
202 array[i].~T();
203 }
204 return objStart;
205 },
Mike Klein5f444c92018-06-07 12:53:29 -0400206 ToU32(count),
Herb Derby593cb942017-01-19 14:28:49 -0500207 padding);
Herb Derby0497f082017-01-13 11:30:44 -0500208 }
209
210 return objStart;
211 }
212
Herb Derby1bf3fc72017-02-17 10:45:47 -0500213 char* fDtorCursor;
214 char* fCursor;
215 char* fEnd;
216 char* const fFirstBlock;
217 const uint32_t fFirstSize;
218 const uint32_t fExtraSize;
Herb Derby4b32ab12017-04-27 15:22:02 -0400219
Herb Derby7dd57b62017-03-08 14:17:49 -0500220 // Use the Fibonacci sequence as the growth factor for block size. The size of the block
221 // allocated is fFib0 * fExtraSize. Using 2 ^ n * fExtraSize had too much slop for Android.
222 uint32_t fFib0 {1}, fFib1 {1};
Herb Derby0497f082017-01-13 11:30:44 -0500223};
224
Florin Malita14a64302017-05-24 14:53:44 -0400225// Helper for defining allocators with inline/reserved storage.
226// For argument declarations, stick to the base type (SkArenaAlloc).
227template <size_t InlineStorageSize>
228class SkSTArenaAlloc : public SkArenaAlloc {
229public:
Mike Klein2e361a32018-06-07 12:57:05 -0400230 explicit SkSTArenaAlloc(size_t extraSize = InlineStorageSize)
231 : INHERITED(fInlineStorage, InlineStorageSize, extraSize) {}
Florin Malita14a64302017-05-24 14:53:44 -0400232
233private:
234 char fInlineStorage[InlineStorageSize];
235
236 using INHERITED = SkArenaAlloc;
237};
238
Brian Salomoncbcb0a12017-11-19 13:20:13 -0500239#endif // SkArenaAlloc_DEFINED