Mike Klein | 58b1306 | 2016-11-11 10:38:49 -0500 | [diff] [blame] | 1 | /* |
| 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 Salomon | cbcb0a1 | 2017-11-19 13:20:13 -0500 | [diff] [blame] | 8 | #ifndef SkArenaAlloc_DEFINED |
| 9 | #define SkArenaAlloc_DEFINED |
Mike Klein | 58b1306 | 2016-11-11 10:38:49 -0500 | [diff] [blame] | 10 | |
Ben Wagner | e1789af | 2018-07-25 15:20:25 -0400 | [diff] [blame] | 11 | #include "../private/SkTFitsIn.h" |
| 12 | |
Mike Klein | 5f444c9 | 2018-06-07 12:53:29 -0400 | [diff] [blame] | 13 | #include <cassert> |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 14 | #include <cstddef> |
Mike Klein | 5f444c9 | 2018-06-07 12:53:29 -0400 | [diff] [blame] | 15 | #include <cstdint> |
| 16 | #include <cstdlib> |
| 17 | #include <cstring> |
| 18 | #include <limits> |
Mike Klein | 58b1306 | 2016-11-11 10:38:49 -0500 | [diff] [blame] | 19 | #include <new> |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 20 | #include <type_traits> |
Mike Klein | 58b1306 | 2016-11-11 10:38:49 -0500 | [diff] [blame] | 21 | #include <utility> |
| 22 | #include <vector> |
| 23 | |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 24 | // 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 Klein | 9f752aa | 2018-06-07 13:13:54 -0400 | [diff] [blame] | 35 | // SkArenaAlloc arena(block, mostCasesSize); |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 36 | // |
| 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 Klein | 9f752aa | 2018-06-07 13:13:54 -0400 | [diff] [blame] | 42 | // If the program only sometimes allocates memory, use the following pattern. |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 43 | // |
| 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 Klein | 9f752aa | 2018-06-07 13:13:54 -0400 | [diff] [blame] | 51 | // SkArenaAlloc arena (storage, mostCasesSize); |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 52 | // }; |
| 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 Derby | 01254bc | 2017-03-03 15:09:43 -0500 | [diff] [blame] | 59 | // |
| 60 | // If additional blocks are needed they are increased exponentially. This strategy bounds the |
Herb Derby | 7dd57b6 | 2017-03-08 14:17:49 -0500 | [diff] [blame] | 61 | // 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 Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 64 | class SkArenaAlloc { |
| 65 | public: |
Mike Klein | 9f752aa | 2018-06-07 13:13:54 -0400 | [diff] [blame] | 66 | SkArenaAlloc(char* block, size_t blockSize, size_t extraSize); |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 67 | |
Mike Klein | 2e361a3 | 2018-06-07 12:57:05 -0400 | [diff] [blame] | 68 | SkArenaAlloc(size_t extraSize) |
| 69 | : SkArenaAlloc(nullptr, 0, extraSize) |
Herb Derby | 2a0daee | 2017-01-20 16:58:06 -0500 | [diff] [blame] | 70 | {} |
| 71 | |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 72 | ~SkArenaAlloc(); |
| 73 | |
| 74 | template <typename T, typename... Args> |
| 75 | T* make(Args&&... args) { |
Mike Klein | 5f444c9 | 2018-06-07 12:53:29 -0400 | [diff] [blame] | 76 | uint32_t size = ToU32(sizeof(T)); |
| 77 | uint32_t alignment = ToU32(alignof(T)); |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 78 | char* objStart; |
Mike Klein | d62c4bd | 2018-06-07 10:00:49 -0400 | [diff] [blame] | 79 | if (std::is_trivially_destructible<T>::value) { |
Herb Derby | 1bf3fc7 | 2017-02-17 10:45:47 -0500 | [diff] [blame] | 80 | objStart = this->allocObject(size, alignment); |
| 81 | fCursor = objStart + size; |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 82 | } else { |
Herb Derby | 1bf3fc7 | 2017-02-17 10:45:47 -0500 | [diff] [blame] | 83 | objStart = this->allocObjectWithFooter(size + sizeof(Footer), alignment); |
Herb Derby | 39728d3 | 2017-01-25 17:05:05 -0500 | [diff] [blame] | 84 | // Can never be UB because max value is alignof(T). |
Mike Klein | 5f444c9 | 2018-06-07 12:53:29 -0400 | [diff] [blame] | 85 | uint32_t padding = ToU32(objStart - fCursor); |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 86 | |
| 87 | // Advance to end of object to install footer. |
Herb Derby | 1bf3fc7 | 2017-02-17 10:45:47 -0500 | [diff] [blame] | 88 | fCursor = objStart + size; |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 89 | 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 Wagner | e1789af | 2018-07-25 15:20:25 -0400 | [diff] [blame] | 103 | AssertRelease(SkTFitsIn<uint32_t>(count)); |
Mike Klein | 5f444c9 | 2018-06-07 12:53:29 -0400 | [diff] [blame] | 104 | uint32_t safeCount = ToU32(count); |
Herb Derby | 1bf3fc7 | 2017-02-17 10:45:47 -0500 | [diff] [blame] | 105 | T* array = (T*)this->commonArrayAlloc<T>(safeCount); |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 106 | |
| 107 | // If T is primitive then no initialization takes place. |
Herb Derby | 1bf3fc7 | 2017-02-17 10:45:47 -0500 | [diff] [blame] | 108 | for (size_t i = 0; i < safeCount; i++) { |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 109 | new (&array[i]) T; |
| 110 | } |
| 111 | return array; |
| 112 | } |
| 113 | |
| 114 | template <typename T> |
| 115 | T* makeArray(size_t count) { |
Ben Wagner | e1789af | 2018-07-25 15:20:25 -0400 | [diff] [blame] | 116 | AssertRelease(SkTFitsIn<uint32_t>(count)); |
Mike Klein | 5f444c9 | 2018-06-07 12:53:29 -0400 | [diff] [blame] | 117 | uint32_t safeCount = ToU32(count); |
Herb Derby | 1bf3fc7 | 2017-02-17 10:45:47 -0500 | [diff] [blame] | 118 | T* array = (T*)this->commonArrayAlloc<T>(safeCount); |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 119 | |
| 120 | // If T is primitive then the memory is initialized. For example, an array of chars will |
| 121 | // be zeroed. |
Herb Derby | 1bf3fc7 | 2017-02-17 10:45:47 -0500 | [diff] [blame] | 122 | for (size_t i = 0; i < safeCount; i++) { |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 123 | new (&array[i]) T(); |
| 124 | } |
| 125 | return array; |
| 126 | } |
| 127 | |
Herb Derby | 6d64c54 | 2018-02-16 10:36:52 -0500 | [diff] [blame] | 128 | // Only use makeBytesAlignedTo if none of the typed variants are impractical to use. |
| 129 | void* makeBytesAlignedTo(size_t size, size_t align) { |
Ben Wagner | e1789af | 2018-07-25 15:20:25 -0400 | [diff] [blame] | 130 | AssertRelease(SkTFitsIn<uint32_t>(size)); |
Mike Klein | 5f444c9 | 2018-06-07 12:53:29 -0400 | [diff] [blame] | 131 | auto objStart = this->allocObject(ToU32(size), ToU32(align)); |
Herb Derby | 6d64c54 | 2018-02-16 10:36:52 -0500 | [diff] [blame] | 132 | fCursor = objStart + size; |
| 133 | return objStart; |
| 134 | } |
| 135 | |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 136 | // Destroy all allocated objects, free any heap allocations. |
| 137 | void reset(); |
| 138 | |
| 139 | private: |
Mike Klein | 5f444c9 | 2018-06-07 12:53:29 -0400 | [diff] [blame] | 140 | static void AssertRelease(bool cond) { if (!cond) { ::abort(); } } |
| 141 | static uint32_t ToU32(size_t v) { |
Ben Wagner | e1789af | 2018-07-25 15:20:25 -0400 | [diff] [blame] | 142 | assert(SkTFitsIn<uint32_t>(v)); |
Mike Klein | 5f444c9 | 2018-06-07 12:53:29 -0400 | [diff] [blame] | 143 | return (uint32_t)v; |
| 144 | } |
| 145 | |
Herb Derby | c4e6cfe | 2017-01-24 15:59:51 -0500 | [diff] [blame] | 146 | using Footer = int64_t; |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 147 | using FooterAction = char* (char*); |
| 148 | |
Herb Derby | 593cb94 | 2017-01-19 14:28:49 -0500 | [diff] [blame] | 149 | static char* SkipPod(char* footerEnd); |
| 150 | static void RunDtorsOnBlock(char* footerEnd); |
| 151 | static char* NextBlock(char* footerEnd); |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 152 | |
Herb Derby | 39728d3 | 2017-01-25 17:05:05 -0500 | [diff] [blame] | 153 | 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 Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 156 | |
Herb Derby | 1bf3fc7 | 2017-02-17 10:45:47 -0500 | [diff] [blame] | 157 | void ensureSpace(uint32_t size, uint32_t alignment); |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 158 | |
Mike Klein | 7916255 | 2017-05-24 12:19:02 -0400 | [diff] [blame] | 159 | char* allocObject(uint32_t size, uint32_t alignment) { |
| 160 | uintptr_t mask = alignment - 1; |
Herb Derby | 8618338 | 2017-08-15 17:30:58 -0400 | [diff] [blame] | 161 | uintptr_t alignedOffset = (~reinterpret_cast<uintptr_t>(fCursor) + 1) & mask; |
| 162 | uintptr_t totalSize = size + alignedOffset; |
Mike Klein | 5f444c9 | 2018-06-07 12:53:29 -0400 | [diff] [blame] | 163 | AssertRelease(totalSize >= size); |
Herb Derby | 8618338 | 2017-08-15 17:30:58 -0400 | [diff] [blame] | 164 | 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 Klein | 7916255 | 2017-05-24 12:19:02 -0400 | [diff] [blame] | 169 | } |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 170 | |
Herb Derby | 1bf3fc7 | 2017-02-17 10:45:47 -0500 | [diff] [blame] | 171 | char* allocObjectWithFooter(uint32_t sizeIncludingFooter, uint32_t alignment); |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 172 | |
| 173 | template <typename T> |
Herb Derby | 1bf3fc7 | 2017-02-17 10:45:47 -0500 | [diff] [blame] | 174 | char* commonArrayAlloc(uint32_t count) { |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 175 | char* objStart; |
Mike Klein | 5f444c9 | 2018-06-07 12:53:29 -0400 | [diff] [blame] | 176 | 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 Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 179 | |
Mike Klein | d62c4bd | 2018-06-07 10:00:49 -0400 | [diff] [blame] | 180 | if (std::is_trivially_destructible<T>::value) { |
Herb Derby | 1bf3fc7 | 2017-02-17 10:45:47 -0500 | [diff] [blame] | 181 | objStart = this->allocObject(arraySize, alignment); |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 182 | fCursor = objStart + arraySize; |
| 183 | } else { |
Ben Wagner | 6229b12 | 2017-07-24 16:11:31 -0400 | [diff] [blame] | 184 | constexpr uint32_t overhead = sizeof(Footer) + sizeof(uint32_t); |
Mike Klein | 5f444c9 | 2018-06-07 12:53:29 -0400 | [diff] [blame] | 185 | AssertRelease(arraySize <= std::numeric_limits<uint32_t>::max() - overhead); |
Ben Wagner | 6229b12 | 2017-07-24 16:11:31 -0400 | [diff] [blame] | 186 | uint32_t totalSize = arraySize + overhead; |
Herb Derby | 1bf3fc7 | 2017-02-17 10:45:47 -0500 | [diff] [blame] | 187 | objStart = this->allocObjectWithFooter(totalSize, alignment); |
Herb Derby | 39728d3 | 2017-01-25 17:05:05 -0500 | [diff] [blame] | 188 | |
| 189 | // Can never be UB because max value is alignof(T). |
Mike Klein | 5f444c9 | 2018-06-07 12:53:29 -0400 | [diff] [blame] | 190 | uint32_t padding = ToU32(objStart - fCursor); |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 191 | |
| 192 | // Advance to end of array to install footer.? |
| 193 | fCursor = objStart + arraySize; |
Herb Derby | 593cb94 | 2017-01-19 14:28:49 -0500 | [diff] [blame] | 194 | 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 Klein | 5f444c9 | 2018-06-07 12:53:29 -0400 | [diff] [blame] | 206 | ToU32(count), |
Herb Derby | 593cb94 | 2017-01-19 14:28:49 -0500 | [diff] [blame] | 207 | padding); |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 208 | } |
| 209 | |
| 210 | return objStart; |
| 211 | } |
| 212 | |
Herb Derby | 1bf3fc7 | 2017-02-17 10:45:47 -0500 | [diff] [blame] | 213 | char* fDtorCursor; |
| 214 | char* fCursor; |
| 215 | char* fEnd; |
| 216 | char* const fFirstBlock; |
| 217 | const uint32_t fFirstSize; |
| 218 | const uint32_t fExtraSize; |
Herb Derby | 4b32ab1 | 2017-04-27 15:22:02 -0400 | [diff] [blame] | 219 | |
Herb Derby | 7dd57b6 | 2017-03-08 14:17:49 -0500 | [diff] [blame] | 220 | // 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 Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 223 | }; |
| 224 | |
Florin Malita | 14a6430 | 2017-05-24 14:53:44 -0400 | [diff] [blame] | 225 | // Helper for defining allocators with inline/reserved storage. |
| 226 | // For argument declarations, stick to the base type (SkArenaAlloc). |
| 227 | template <size_t InlineStorageSize> |
| 228 | class SkSTArenaAlloc : public SkArenaAlloc { |
| 229 | public: |
Mike Klein | 2e361a3 | 2018-06-07 12:57:05 -0400 | [diff] [blame] | 230 | explicit SkSTArenaAlloc(size_t extraSize = InlineStorageSize) |
| 231 | : INHERITED(fInlineStorage, InlineStorageSize, extraSize) {} |
Florin Malita | 14a6430 | 2017-05-24 14:53:44 -0400 | [diff] [blame] | 232 | |
| 233 | private: |
| 234 | char fInlineStorage[InlineStorageSize]; |
| 235 | |
| 236 | using INHERITED = SkArenaAlloc; |
| 237 | }; |
| 238 | |
Brian Salomon | cbcb0a1 | 2017-11-19 13:20:13 -0500 | [diff] [blame] | 239 | #endif // SkArenaAlloc_DEFINED |