| Michael Ludwig | 4519134 | 2020-03-24 12:29:39 -0400 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright 2010 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 GrTAllocator_DEFINED | 
 | 9 | #define GrTAllocator_DEFINED | 
 | 10 |  | 
 | 11 | #include "src/gpu/GrBlockAllocator.h" | 
 | 12 |  | 
 | 13 | template <typename T, int StartingItems = 1> | 
 | 14 | class GrTAllocator { | 
 | 15 | public: | 
 | 16 |     /** | 
 | 17 |      * Create an allocator that defaults to using StartingItems as heap increment. | 
 | 18 |      */ | 
 | 19 |     GrTAllocator() | 
 | 20 |             : fTotalCount(0) | 
 | 21 |             , fAllocator(GrBlockAllocator::GrowthPolicy::kFixed) {} | 
 | 22 |  | 
 | 23 |     /** | 
 | 24 |      * Create an allocator | 
 | 25 |      * | 
 | 26 |      * @param   itemsPerBlock   the number of items to allocate at once | 
 | 27 |      */ | 
 | 28 |     explicit GrTAllocator(int itemsPerBlock) | 
 | 29 |             : fTotalCount(0) | 
 | 30 |             , fAllocator(GrBlockAllocator::GrowthPolicy::kFixed, | 
 | 31 |                          GrBlockAllocator::BlockOverhead<alignof(T)>() + sizeof(T)*itemsPerBlock) {} | 
 | 32 |  | 
 | 33 |     ~GrTAllocator() { this->reset(); } | 
 | 34 |  | 
 | 35 |     /** | 
 | 36 |      * Adds an item and returns it. | 
 | 37 |      * | 
 | 38 |      * @return the added item. | 
 | 39 |      */ | 
 | 40 |     T& push_back() { | 
| Ben Wagner | 14ba58f | 2020-03-30 17:53:53 -0400 | [diff] [blame] | 41 |         return *new (this->pushItem()) T; | 
| Michael Ludwig | 4519134 | 2020-03-24 12:29:39 -0400 | [diff] [blame] | 42 |     } | 
 | 43 |  | 
 | 44 |     T& push_back(const T& t) { | 
| Ben Wagner | 14ba58f | 2020-03-30 17:53:53 -0400 | [diff] [blame] | 45 |         return *new (this->pushItem()) T(t); | 
 | 46 |     } | 
 | 47 |  | 
 | 48 |     T& push_back(T&& t) { | 
 | 49 |         return *new (this->pushItem()) T(std::move(t)); | 
| Michael Ludwig | 4519134 | 2020-03-24 12:29:39 -0400 | [diff] [blame] | 50 |     } | 
 | 51 |  | 
 | 52 |     template <typename... Args> | 
 | 53 |     T& emplace_back(Args&&... args) { | 
| Ben Wagner | 14ba58f | 2020-03-30 17:53:53 -0400 | [diff] [blame] | 54 |         return *new (this->pushItem()) T(std::forward<Args>(args)...); | 
| Michael Ludwig | 4519134 | 2020-03-24 12:29:39 -0400 | [diff] [blame] | 55 |     } | 
 | 56 |  | 
 | 57 |     /** | 
 | 58 |      * Remove the last item, only call if count() != 0 | 
 | 59 |      */ | 
 | 60 |     void pop_back() { | 
 | 61 |         SkASSERT(fTotalCount > 0); | 
 | 62 |  | 
 | 63 |         GrBlockAllocator::Block* block = fAllocator->currentBlock(); | 
 | 64 |         int newCount = block->metadata() - 1; | 
 | 65 |  | 
 | 66 |         // Run dtor for the popped item | 
 | 67 |         int releaseIndex; | 
 | 68 |         GetItemAndOffset(block, newCount, &releaseIndex)->~T(); | 
 | 69 |  | 
 | 70 |         if (newCount == 0) { | 
 | 71 |             fAllocator->releaseBlock(block); | 
 | 72 |         } else { | 
 | 73 |             // Since this always follows LIFO, the block should always be able to release the memory | 
 | 74 |             SkAssertResult(block->release(releaseIndex, releaseIndex + sizeof(T))); | 
 | 75 |             block->setMetadata(newCount); | 
 | 76 |         } | 
 | 77 |  | 
 | 78 |         fTotalCount--; | 
 | 79 |     } | 
 | 80 |  | 
 | 81 |     /** | 
 | 82 |      * Removes all added items. | 
 | 83 |      */ | 
 | 84 |     void reset() { | 
 | 85 |         // Invoke destructors in reverse order | 
 | 86 |         for (const auto* b : fAllocator->rblocks()) { | 
 | 87 |             int c = b->metadata(); | 
 | 88 |             for (int i = c - 1; i >= 0; i--) { | 
 | 89 |                 GetItem(b, i)->~T(); | 
 | 90 |             } | 
 | 91 |         } | 
 | 92 |  | 
 | 93 |         fAllocator->reset(); | 
 | 94 |         fTotalCount = 0; | 
 | 95 |     } | 
 | 96 |  | 
 | 97 |     /** | 
 | 98 |      * Returns the item count. | 
 | 99 |      */ | 
 | 100 |     int count() const { | 
 | 101 | #ifdef SK_DEBUG | 
 | 102 |         // Confirm total count matches sum of block counts | 
 | 103 |         int count = 0; | 
 | 104 |         int blockCount = 0; | 
 | 105 |         for (const auto* b :fAllocator->blocks()) { | 
 | 106 |             count += b->metadata(); | 
 | 107 |             blockCount++; | 
 | 108 |         } | 
 | 109 |         SkASSERT(count == fTotalCount); | 
 | 110 | #endif | 
 | 111 |         return fTotalCount; | 
 | 112 |     } | 
 | 113 |  | 
 | 114 |     /** | 
 | 115 |      * Is the count 0? | 
 | 116 |      */ | 
 | 117 |     bool empty() const { return this->count() == 0; } | 
 | 118 |  | 
 | 119 |     /** | 
 | 120 |      * Access first item, only call if count() != 0 | 
 | 121 |      */ | 
 | 122 |     T& front() { | 
 | 123 |         // This assumes that the head block actually have room to store the first item. | 
 | 124 |         static_assert(StartingItems >= 1); | 
 | 125 |         SkASSERT(fTotalCount > 0); | 
 | 126 |         return *(GetItem(fAllocator->headBlock(), 0)); | 
 | 127 |     } | 
 | 128 |  | 
 | 129 |     /** | 
 | 130 |      * Access first item, only call if count() != 0 | 
 | 131 |      */ | 
 | 132 |     const T& front() const { | 
 | 133 |         SkASSERT(fTotalCount > 0); | 
 | 134 |         return *(GetItem(fAllocator->headBlock(), 0)); | 
 | 135 |     } | 
 | 136 |  | 
 | 137 |     /** | 
 | 138 |      * Access last item, only call if count() != 0 | 
 | 139 |      */ | 
 | 140 |     T& back() { | 
 | 141 |         SkASSERT(fTotalCount > 0); | 
 | 142 |         int blockCount = fAllocator->currentBlock()->metadata(); | 
 | 143 |         return *(GetItem(fAllocator->currentBlock(), blockCount - 1)); | 
 | 144 |     } | 
 | 145 |  | 
 | 146 |     /** | 
 | 147 |      * Access last item, only call if count() != 0 | 
 | 148 |      */ | 
 | 149 |     const T& back() const { | 
 | 150 |         SkASSERT(fTotalCount > 0); | 
 | 151 |         int blockCount = fAllocator->currentBlock()->metadata(); | 
 | 152 |         return *(GetItem(fAllocator->currentBlock(), blockCount - 1)); | 
 | 153 |     } | 
 | 154 |  | 
 | 155 |     template<bool Const> | 
 | 156 |     class Iterator { | 
 | 157 |         using BlockIter = typename GrBlockAllocator::BlockIter<true, Const>; | 
 | 158 |         using C = typename std::conditional<Const, const T, T>::type; | 
 | 159 |         using AllocatorT = typename std::conditional<Const, const GrTAllocator, GrTAllocator>::type; | 
 | 160 |     public: | 
 | 161 |         Iterator(AllocatorT* allocator) : fBlockIter(allocator->fAllocator.allocator()) {} | 
 | 162 |  | 
 | 163 |         class Item { | 
 | 164 |         public: | 
 | 165 |             bool operator!=(const Item& other) const { | 
 | 166 |                 if (other.fBlock != fBlock) { | 
 | 167 |                     // Treat an empty head block the same as the end block | 
 | 168 |                     bool blockEmpty = !(*fBlock) || (*fBlock)->metadata() == 0; | 
 | 169 |                     bool otherEmpty = !(*other.fBlock) || (*other.fBlock)->metadata() == 0; | 
 | 170 |                     return !blockEmpty || !otherEmpty; | 
 | 171 |                 } else { | 
 | 172 |                     // Same block, so differentiate by index into it (unless it's the "end" block | 
 | 173 |                     // in which case ignore index). | 
 | 174 |                     return SkToBool(*fBlock) && other.fIndex != fIndex; | 
 | 175 |                 } | 
 | 176 |             } | 
 | 177 |  | 
 | 178 |             C& operator*() const { | 
 | 179 |                 C* item = const_cast<C*>(static_cast<const C*>((*fBlock)->ptr(fIndex))); | 
 | 180 |                 SkDEBUGCODE(int offset;) | 
 | 181 |                 SkASSERT(item == GetItemAndOffset(*fBlock, fItem, &offset) && offset == fIndex); | 
 | 182 |                 return *item; | 
 | 183 |             } | 
 | 184 |  | 
 | 185 |             Item& operator++() { | 
 | 186 |                 const auto* block = *fBlock; | 
 | 187 |                 fItem++; | 
 | 188 |                 fIndex += sizeof(T); | 
 | 189 |                 if (fItem >= block->metadata()) { | 
 | 190 |                     ++fBlock; | 
 | 191 |                     fItem = 0; | 
 | 192 |                     fIndex = StartingIndex(fBlock); | 
 | 193 |                 } | 
 | 194 |                 return *this; | 
 | 195 |             } | 
 | 196 |  | 
 | 197 |         private: | 
 | 198 |             friend Iterator; | 
 | 199 |             using BlockItem = typename BlockIter::Item; | 
 | 200 |  | 
 | 201 |             Item(BlockItem block) : fBlock(block), fItem(0), fIndex(StartingIndex(block)) {} | 
 | 202 |  | 
 | 203 |             static int StartingIndex(const BlockItem& block) { | 
 | 204 |                 return *block ? (*block)->template firstAlignedOffset<alignof(T)>() : 0; | 
 | 205 |             } | 
 | 206 |  | 
 | 207 |             BlockItem fBlock; | 
 | 208 |             int       fItem; | 
 | 209 |             int       fIndex; | 
 | 210 |         }; | 
 | 211 |  | 
 | 212 |         Item begin() const { return Item(fBlockIter.begin()); } | 
 | 213 |         Item end() const { return Item(fBlockIter.end()); } | 
 | 214 |  | 
 | 215 |     private: | 
 | 216 |         const BlockIter fBlockIter; | 
 | 217 |     }; | 
 | 218 |  | 
 | 219 |     using Iter = Iterator<false>; | 
 | 220 |     using CIter = Iterator<true>; | 
 | 221 |  | 
 | 222 |     /** | 
 | 223 |      * Prefer using a for-range loop when iterating over all allocated items, vs. index access. | 
 | 224 |      */ | 
 | 225 |     Iter items() { return Iter(this); } | 
 | 226 |     CIter items() const { return CIter(this); } | 
 | 227 |  | 
 | 228 |     /** | 
 | 229 |      * Access item by index. Not an operator[] since it should not be considered constant time. | 
 | 230 |      */ | 
 | 231 |     T& item(int i) { | 
 | 232 |         // Iterate over blocks until we find the one that contains i. | 
 | 233 |         SkASSERT(i >= 0 && i < fTotalCount); | 
 | 234 |         for (const auto* b : fAllocator->blocks()) { | 
 | 235 |             int blockCount = b->metadata(); | 
 | 236 |             if (i < blockCount) { | 
 | 237 |                 return *GetItem(b, i); | 
 | 238 |             } else { | 
 | 239 |                 i -= blockCount; | 
 | 240 |             } | 
 | 241 |         } | 
 | 242 |         SkUNREACHABLE; | 
 | 243 |     } | 
 | 244 |     const T& item(int i) const { | 
 | 245 |         return const_cast<GrTAllocator<T>*>(this)->item(i); | 
 | 246 |     } | 
 | 247 |  | 
 | 248 | private: | 
 | 249 |     static constexpr size_t StartingSize = | 
 | 250 |             GrBlockAllocator::Overhead<alignof(T)>() + StartingItems * sizeof(T); | 
 | 251 |  | 
 | 252 |     static T* GetItemAndOffset(const GrBlockAllocator::Block* block, int index, int* offset) { | 
 | 253 |         SkASSERT(index >= 0 && index < block->metadata()); | 
 | 254 |         *offset = block->firstAlignedOffset<alignof(T)>() + index * sizeof(T); | 
 | 255 |         return const_cast<T*>(static_cast<const T*>(block->ptr(*offset))); | 
 | 256 |     } | 
 | 257 |  | 
 | 258 |     static T* GetItem(const GrBlockAllocator::Block* block, int index) { | 
 | 259 |         int offset; | 
 | 260 |         return GetItemAndOffset(block, index, &offset); | 
 | 261 |     } | 
 | 262 |  | 
 | 263 |     void* pushItem() { | 
 | 264 |         // 'template' required because fAllocator is a template, calling a template member | 
 | 265 |         auto br = fAllocator->template allocate<alignof(T)>(sizeof(T)); | 
 | 266 |         br.fBlock->setMetadata(br.fBlock->metadata() + 1); | 
 | 267 |         fTotalCount++; | 
 | 268 |         return br.fBlock->ptr(br.fAlignedOffset); | 
 | 269 |     } | 
 | 270 |  | 
 | 271 |     // Each Block in the allocator tracks their count of items, but it's convenient to store | 
 | 272 |     // the sum of their counts as well. | 
 | 273 |     int fTotalCount; | 
 | 274 |  | 
 | 275 |     // N represents the number of items, whereas GrSBlockAllocator takes total bytes, so must | 
 | 276 |     // account for the block allocator's size too. | 
 | 277 |     GrSBlockAllocator<StartingSize> fAllocator; | 
 | 278 | }; | 
 | 279 |  | 
 | 280 | #endif |