Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <cstddef>
- #include <memory>
- #include <cassert>
- #include <cstdint>
- #define sz_KB(x) x*1000
- #define sz_MB(x) sz_KB(x)*1000
- #define sz_GB(x) sz_MB(x)*1000
- namespace pg
- {
- template<std::size_t poolSize = sz_KB(64), uint16_t maxPools = 32>
- class PtrBumpAllocator
- {
- public:
- typedef uintptr_t size_type;
- typedef uintptr_t ptrint_type;
- /*
- \brief Constructor. Creates the first pool and does the setup. Will throw if it can't create it.
- */
- PtrBumpAllocator()
- {
- // Quick tests to see if someone isn't trying to fool us.
- static_assert(maxPools > 1,"Not enough space to allocate first pool !");
- // Create the first pool
- if (!createPool())
- throw std::exception("Couldn't allocate the first pool!");
- }
- /*
- \brief Allocates (size) bytes of memory, bumping the pointer by (size) bytes.
- \param size The size of the chunk of memory you want to allocate in bytes.
- \returns Your chunk of memory, nullptr if the allocator can't allocate any more memory.
- */
- void* allocate(const size_type &size)
- {
- // if the object is too big to be allocated, return nullptr.
- if (!doesObjectFit(size))
- return nullptr;
- // if the allocator failed to create a new pool when required, return nullptr
- if (createNewPoolIfRequired(size) < 0)
- return nullptr;
- // Else, if everything's alright, go for it.
- assert(allocPtr && "AllocPtr cannot be null at this stage.");
- auto tmp = allocPtr;
- allocPtr = static_cast<char*>(allocPtr) + size;
- return tmp;
- }
- /*
- \brief Templated version of allocate which uses sizeof() to call the base allocate. See non-templated allocate overload for more details.
- */
- template<typename DataTy>
- DataTy* allocate()
- {
- return static_cast<DataTy*>(allocate(sizeof(DataTy)));
- }
- /*
- \brief Deallocates the pointer. This just zeroes the memory unless it's the last object allocated, then, it's actually freed.
- \param ptr The pointer which holds the chunk of memory you own.
- \param sz The size of the chunk of memory in bytes.
- */
- void deallocate(void *ptr, const size_type &sz)
- {
- // set everything to zero
- memset(ptr, 0, sz);
- // Decrease the pointer if we can.
- if (allocPtr == (static_cast<char*>(ptr) + sz))
- allocPtr = static_cast<char*>(allocPtr) - sz;
- }
- /*
- \brief Templated version of deallocate which uses sizeof() to call the base deallocate.
- */
- template<typename DataTy>
- void deallocate(DataTy *ptr)
- {
- deallocate(ptr, sizeof(DataTy));
- }
- /*
- \brief Destroys every pool.
- */
- void destroy()
- {
- // Note: Due to the nature of the unique_ptrs, this "destroy" function is useless when called from the destructor, however, it's provided
- // as a mean of resetting the pool.
- // Free the first pool, starting a chain reaction where every pool will be deleted.
- if(firstPool)
- firstPool.reset();
- // Set all the member variables to safe values.
- curPool = nullptr;
- poolCount = 0;
- allocPtr = nullptr;
- }
- /*
- \brief Destructor (just calls destroy())
- */
- ~PtrBumpAllocator()
- {
- destroy();
- }
- /*
- \returns True if the object will fit in a pool.
- */
- bool doesObjectFit(const size_type& sz) const
- {
- return (poolSize >= sz);
- }
- /*
- \brief Displays a detailled dump to get an overview of Allocator.
- This really just displays MaxPools and PoolSize and calls smallDump()
- */
- void dump() const
- {
- std::cout << "MaxPools: " << maxPools << "\n";
- std::cout << "Pool Size: " << poolSize << "\n";
- smallDump();
- }
- /*
- \brief Displays a condensed dump to get an overview of Allocator.
- */
- void smallDump() const
- {
- std::cout << "Pools: " << poolCount << "\n";
- std::cout << "Curpool address: " << (void*)curPool << "\n";
- std::cout << "AllocPtr address: " << (void*)allocPtr << "\n";
- std::cout << "Bytes in current pool: " << (ptrdiff_t)(((char*)allocPtr) - ((char*)curPool)) << "\n";
- }
- private:
- /*
- \brief Creates a pool if the current pool can't support an allocation of size sz.
- \return 1 if a new pool was allocated successfully. Returns 0 if no pool was allocated. Returns -1 if allocation of a new pool failed.
- */
- char createNewPoolIfRequired(const size_type &sz)
- {
- // If there's no pool, allocate one.
- if (!firstPool)
- return createPool() ? 1 : -1;
- // If the current pool can't hold the data, create a new one.
- else if (((static_cast<char*>(allocPtr) + sz) > curPool->upperBound))
- return createPool() ? 1 : -1;
- return 0;
- }
- /*
- \returns True if we can't allocate any more pools.
- */
- bool canCreateMorePools() const
- {
- return (poolCount < maxPools);
- }
- /*
- \brief Creates a pool. Will create the first one if that is not done yet, else, it'll just add another one at the end of the list.
- */
- bool createPool()
- {
- if (!canCreateMorePools())
- return false;
- if (curPool)
- {
- assert(firstPool && "curPool isn't null, but firstPool is set?");
- curPool->next = std::make_unique<Pool>();
- curPool = curPool->next.get();
- }
- else
- {
- assert(!firstPool && "curPool is null, but firstPool isn't?");
- firstPool = std::make_unique<Pool>();
- curPool = firstPool.get();
- }
- poolCount++;
- allocPtr = curPool->data;
- return true;
- }
- /*
- \brief A Single Pool.
- */
- struct Pool
- {
- Pool() : upperBound(data + (poolSize-1))
- {
- // Set everything to zero.
- memset(data, 0, sizeof(data));
- }
- // The data
- char data[poolSize];
- // The upper bound of the data
- const char * const upperBound;
- // The next pool
- std::unique_ptr<Pool> next;
- };
- // Tracking variables
- std::unique_ptr<Pool> firstPool;
- Pool* curPool = nullptr;
- size_type poolCount = 0;
- void * allocPtr = nullptr;
- };
- }
- #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement