IterableArena.h (5037B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef MOZILLA_GFX_ITERABLEARENA_H_ 8 #define MOZILLA_GFX_ITERABLEARENA_H_ 9 10 #include <stdint.h> 11 #include <string.h> 12 13 #include <utility> 14 15 #include "mozilla/Assertions.h" 16 #include "mozilla/gfx/Logging.h" 17 18 namespace mozilla { 19 namespace gfx { 20 21 /// A simple pool allocator for plain data structures. 22 /// 23 /// Beware that the pool will not attempt to run the destructors. It is the 24 /// responsibility of the user of this class to either use objects with no 25 /// destructor or to manually call the allocated objects destructors. 26 /// If the pool is growable, its allocated objects must be safely moveable in 27 /// in memory (through memcpy). 28 class IterableArena { 29 protected: 30 struct Header { 31 size_t mBlocSize; 32 }; 33 34 public: 35 enum ArenaType { FIXED_SIZE, GROWABLE }; 36 37 IterableArena(ArenaType aType, size_t aStorageSize) 38 : mSize(aStorageSize), mCursor(0), mIsGrowable(aType == GROWABLE) { 39 if (mSize == 0) { 40 mSize = 128; 41 } 42 43 mStorage = (uint8_t*)malloc(mSize); 44 if (mStorage == nullptr) { 45 gfxCriticalError() << "Not enough Memory allocate a memory pool of size " 46 << aStorageSize; 47 MOZ_CRASH("GFX: Out of memory IterableArena"); 48 } 49 } 50 51 ~IterableArena() { free(mStorage); } 52 53 /// Constructs a new item in the pool and returns a positive offset in case of 54 /// success. 55 /// 56 /// The offset never changes even if the storage is reallocated, so users 57 /// of this class should prefer storing offsets rather than direct pointers 58 /// to the allocated objects. 59 /// Alloc can cause the storage to be reallocated if the pool was initialized 60 /// with IterableArena::GROWABLE. 61 /// If for any reason the pool fails to allocate enough space for the new item 62 /// Alloc returns a negative offset and the object's constructor is not 63 /// called. 64 template <typename T, typename... Args> 65 ptrdiff_t Alloc(Args&&... aArgs) { 66 void* storage = nullptr; 67 auto offset = AllocRaw(sizeof(T), &storage); 68 if (offset < 0) { 69 return offset; 70 } 71 new (storage) T(std::forward<Args>(aArgs)...); 72 return offset; 73 } 74 75 ptrdiff_t AllocRaw(size_t aSize, void** aOutPtr = nullptr) { 76 const size_t blocSize = AlignedSize(sizeof(Header) + aSize); 77 78 if (AlignedSize(mCursor + blocSize) > mSize) { 79 if (!mIsGrowable) { 80 return -1; 81 } 82 83 size_t newSize = mSize * 2; 84 while (AlignedSize(mCursor + blocSize) > newSize) { 85 newSize *= 2; 86 } 87 88 uint8_t* newStorage = (uint8_t*)realloc(mStorage, newSize); 89 if (!newStorage) { 90 gfxCriticalError() 91 << "Not enough Memory to grow the memory pool, size: " << newSize; 92 return -1; 93 } 94 95 mStorage = newStorage; 96 mSize = newSize; 97 } 98 ptrdiff_t offset = mCursor; 99 GetHeader(offset)->mBlocSize = blocSize; 100 mCursor += blocSize; 101 if (aOutPtr) { 102 *aOutPtr = GetStorage(offset); 103 } 104 return offset; 105 } 106 107 /// Get access to an allocated item at a given offset (only use offsets 108 /// returned by Alloc or AllocRaw). 109 /// 110 /// If the pool is growable, the returned pointer is only valid temporarily. 111 /// The underlying storage can be reallocated in Alloc or AllocRaw, so do not 112 /// keep these pointers around and store the offset instead. 113 void* GetStorage(ptrdiff_t offset = 0) { 114 MOZ_ASSERT(offset >= 0); 115 MOZ_ASSERT(offset < mCursor); 116 return offset >= 0 ? mStorage + offset + sizeof(Header) : nullptr; 117 } 118 119 /// Clears the storage without running any destructor and without deallocating 120 /// it. 121 void Clear() { mCursor = 0; } 122 123 /// Iterate over the elements allocated in this pool. 124 /// 125 /// Takes a lambda or function object accepting a void* as parameter. 126 template <typename Func> 127 void ForEach(Func cb) { 128 Iterator it; 129 while (void* ptr = it.Next(this)) { 130 cb(ptr); 131 } 132 } 133 134 /// A simple iterator over an arena. 135 class Iterator { 136 public: 137 Iterator() : mCursor(0) {} 138 139 void* Next(IterableArena* aArena) { 140 if (mCursor >= aArena->mCursor) { 141 return nullptr; 142 } 143 void* result = aArena->GetStorage(mCursor); 144 const size_t blocSize = aArena->GetHeader(mCursor)->mBlocSize; 145 MOZ_ASSERT(blocSize != 0); 146 mCursor += blocSize; 147 return result; 148 } 149 150 private: 151 ptrdiff_t mCursor; 152 }; 153 154 protected: 155 Header* GetHeader(ptrdiff_t offset) { return (Header*)(mStorage + offset); } 156 157 size_t AlignedSize(size_t aSize) const { 158 const size_t alignment = sizeof(uintptr_t); 159 return aSize + (alignment - (aSize % alignment)) % alignment; 160 } 161 162 uint8_t* mStorage; 163 uint32_t mSize; 164 ptrdiff_t mCursor; 165 bool mIsGrowable; 166 167 friend class Iterator; 168 }; 169 170 } // namespace gfx 171 } // namespace mozilla 172 173 #endif