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 | |
| 8 | #ifndef SkFixedAlloc_DEFINED |
| 9 | #define SkFixedAlloc_DEFINED |
| 10 | |
| 11 | #include "SkTFitsIn.h" |
| 12 | #include "SkTypes.h" |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 13 | #include <cstddef> |
Mike Klein | 58b1306 | 2016-11-11 10:38:49 -0500 | [diff] [blame] | 14 | #include <new> |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 15 | #include <type_traits> |
Mike Klein | 58b1306 | 2016-11-11 10:38:49 -0500 | [diff] [blame] | 16 | #include <utility> |
| 17 | #include <vector> |
| 18 | |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 19 | // SkArenaAlloc allocates object and destroys the allocated objects when destroyed. It's designed |
| 20 | // to minimize the number of underlying block allocations. SkArenaAlloc allocates first out of an |
| 21 | // (optional) user-provided block of memory, and when that's exhausted it allocates on the heap, |
| 22 | // starting with an allocation of extraSize bytes. If your data (plus a small overhead) fits in |
| 23 | // the user-provided block, SkArenaAlloc never uses the heap, and if it fits in extraSize bytes, |
| 24 | // it'll use the heap only once. If you pass extraSize = 0, it allocates blocks for each call to |
| 25 | // make<T>. |
| 26 | // |
| 27 | // Examples: |
| 28 | // |
| 29 | // char block[mostCasesSize]; |
| 30 | // SkArenaAlloc arena(block, almostAllCasesSize); |
| 31 | // |
| 32 | // If mostCasesSize is too large for the stack, you can use the following pattern. |
| 33 | // |
| 34 | // std::unique_ptr<char[]> block{new char[mostCasesSize]}; |
| 35 | // SkArenaAlloc arena(block.get(), mostCasesSize, almostAllCasesSize); |
| 36 | // |
| 37 | // If the program only sometimes allocates memory, use the following. |
| 38 | // |
| 39 | // SkArenaAlloc arena(nullptr, 0, almostAllCasesSize); |
| 40 | // |
| 41 | // The storage does not necessarily need to be on the stack. Embedding the storage in a class also |
| 42 | // works. |
| 43 | // |
| 44 | // class Foo { |
| 45 | // char storage[mostCasesSize]; |
| 46 | // SkArenaAlloc arena (storage, almostAllCasesSize); |
| 47 | // }; |
| 48 | // |
| 49 | // In addition, the system is optimized to handle POD data including arrays of PODs (where |
| 50 | // POD is really data with no destructors). For POD data it has zero overhead per item, and a |
| 51 | // typical block overhead of 8 bytes. For non-POD objects there is a per item overhead of 4 bytes. |
| 52 | // For arrays of non-POD objects there is a per array overhead of typically 8 bytes. There is an |
| 53 | // addition overhead when switching from POD data to non-POD data of typically 8 bytes. |
| 54 | class SkArenaAlloc { |
| 55 | public: |
| 56 | SkArenaAlloc(char* block, size_t size, size_t extraSize = 0); |
| 57 | |
| 58 | template <size_t kSize> |
Herb Derby | 1517224 | 2017-01-19 03:32:23 +0000 | [diff] [blame^] | 59 | SkArenaAlloc(char (&block)[kSize], size_t extraSize = 0) |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 60 | : SkArenaAlloc(block, kSize, extraSize) |
| 61 | {} |
| 62 | |
| 63 | ~SkArenaAlloc(); |
| 64 | |
| 65 | template <typename T, typename... Args> |
| 66 | T* make(Args&&... args) { |
| 67 | char* objStart; |
| 68 | if (skstd::is_trivially_destructible<T>::value) { |
| 69 | objStart = this->allocObject(sizeof(T), alignof(T)); |
| 70 | fCursor = objStart + sizeof(T); |
| 71 | } else { |
| 72 | objStart = this->allocObjectWithFooter(sizeof(T) + sizeof(Footer), alignof(T)); |
| 73 | size_t padding = objStart - fCursor; |
| 74 | |
| 75 | // Advance to end of object to install footer. |
| 76 | fCursor = objStart + sizeof(T); |
| 77 | FooterAction* releaser = [](char* objEnd) { |
| 78 | char* objStart = objEnd - (sizeof(T) + sizeof(Footer)); |
| 79 | ((T*)objStart)->~T(); |
| 80 | return objStart; |
| 81 | }; |
| 82 | this->installFooter(releaser, padding); |
| 83 | } |
| 84 | |
| 85 | // This must be last to make objects with nested use of this allocator work. |
| 86 | return new(objStart) T(std::forward<Args>(args)...); |
| 87 | } |
| 88 | |
| 89 | template <typename T> |
| 90 | T* makeArrayDefault(size_t count) { |
| 91 | T* array = (T*)this->commonArrayAlloc<T>(count); |
| 92 | |
| 93 | // If T is primitive then no initialization takes place. |
| 94 | for (size_t i = 0; i < count; i++) { |
| 95 | new (&array[i]) T; |
| 96 | } |
| 97 | return array; |
| 98 | } |
| 99 | |
| 100 | template <typename T> |
| 101 | T* makeArray(size_t count) { |
| 102 | T* array = (T*)this->commonArrayAlloc<T>(count); |
| 103 | |
| 104 | // If T is primitive then the memory is initialized. For example, an array of chars will |
| 105 | // be zeroed. |
| 106 | for (size_t i = 0; i < count; i++) { |
| 107 | new (&array[i]) T(); |
| 108 | } |
| 109 | return array; |
| 110 | } |
| 111 | |
| 112 | // Destroy all allocated objects, free any heap allocations. |
| 113 | void reset(); |
| 114 | |
| 115 | private: |
| 116 | using Footer = int32_t; |
| 117 | using FooterAction = char* (char*); |
| 118 | |
| 119 | void installFooter(FooterAction* releaser, ptrdiff_t padding); |
| 120 | |
| 121 | // N.B. Action is different than FooterAction. FooterAction expects the end of the Footer, |
| 122 | // and returns the start of the object. An Action expects the end of the *Object* and returns |
| 123 | // the start of the object. |
| 124 | template<typename Action> |
| 125 | void installIntFooter(ptrdiff_t size, ptrdiff_t padding) { |
| 126 | if (SkTFitsIn<int32_t>(size)) { |
| 127 | int32_t smallSize = static_cast<int32_t>(size); |
| 128 | memmove(fCursor, &smallSize, sizeof(int32_t)); |
| 129 | fCursor += sizeof(int32_t); |
| 130 | this->installFooter( |
| 131 | [](char* footerEnd) { |
| 132 | char* objEnd = footerEnd - (sizeof(Footer) + sizeof(int32_t)); |
| 133 | int32_t data; |
| 134 | memmove(&data, objEnd, sizeof(int32_t)); |
| 135 | return Action()(objEnd, data); |
| 136 | }, |
| 137 | padding); |
| 138 | } else { |
| 139 | memmove(fCursor, &size, sizeof(ptrdiff_t)); |
| 140 | fCursor += sizeof(ptrdiff_t); |
| 141 | this->installFooter( |
| 142 | [](char* footerEnd) { |
| 143 | char* objEnd = footerEnd - (sizeof(Footer) + sizeof(ptrdiff_t)); |
| 144 | ptrdiff_t data; |
| 145 | memmove(&data, objEnd, sizeof(ptrdiff_t)); |
| 146 | return Action()(objEnd, data); |
| 147 | }, |
| 148 | padding); |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | void ensureSpace(size_t size, size_t alignment); |
| 153 | |
| 154 | char* allocObject(size_t size, size_t alignment); |
| 155 | |
| 156 | char* allocObjectWithFooter(size_t sizeIncludingFooter, size_t alignment); |
| 157 | |
| 158 | template <typename T> |
| 159 | char* commonArrayAlloc(size_t count) { |
| 160 | char* objStart; |
| 161 | size_t arraySize = count * sizeof(T); |
| 162 | |
| 163 | SkASSERT(arraySize > 0); |
| 164 | |
| 165 | if (skstd::is_trivially_destructible<T>::value) { |
| 166 | objStart = this->allocObject(arraySize, alignof(T)); |
| 167 | fCursor = objStart + arraySize; |
| 168 | } else { |
| 169 | size_t countSize = SkTFitsIn<int32_t>(count) ? sizeof(int32_t) : sizeof(ptrdiff_t); |
| 170 | size_t totalSize = arraySize + sizeof(Footer) + countSize; |
| 171 | objStart = this->allocObjectWithFooter(totalSize, alignof(T)); |
| 172 | size_t padding = objStart - fCursor; |
| 173 | |
| 174 | // Advance to end of array to install footer.? |
| 175 | fCursor = objStart + arraySize; |
| 176 | this->installIntFooter<ArrayDestructor<T>> (count, padding); |
| 177 | } |
| 178 | |
| 179 | return objStart; |
| 180 | } |
| 181 | |
Herb Derby | 1517224 | 2017-01-19 03:32:23 +0000 | [diff] [blame^] | 182 | char* callFooterAction(char* end); |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 183 | |
| 184 | static char* EndChain(char*); |
| 185 | |
| 186 | template<typename T> |
| 187 | struct ArrayDestructor { |
| 188 | char* operator()(char* objEnd, ptrdiff_t count) { |
| 189 | char* objStart = objEnd - count * sizeof(T); |
| 190 | T* array = (T*) objStart; |
| 191 | for (int i = 0; i < count; i++) { |
| 192 | array[i].~T(); |
| 193 | } |
| 194 | return objStart; |
| 195 | } |
| 196 | }; |
| 197 | |
Herb Derby | 1517224 | 2017-01-19 03:32:23 +0000 | [diff] [blame^] | 198 | char* fDtorCursor; |
| 199 | char* fCursor; |
| 200 | char* fEnd; |
| 201 | size_t fExtraSize; |
Herb Derby | 0497f08 | 2017-01-13 11:30:44 -0500 | [diff] [blame] | 202 | }; |
| 203 | |
Mike Klein | 58b1306 | 2016-11-11 10:38:49 -0500 | [diff] [blame] | 204 | #endif//SkFixedAlloc_DEFINED |