Advertisement
Guest User

PtrBumpAllocator - Pierre van Houtryve

a guest
May 18th, 2018
546
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 6.03 KB | None | 0 0
  1. #include <cstddef>
  2. #include <memory>
  3. #include <cassert>
  4. #include <cstdint>
  5.  
  6. #define sz_KB(x) x*1000
  7. #define sz_MB(x) sz_KB(x)*1000
  8. #define sz_GB(x) sz_MB(x)*1000
  9.  
  10. namespace pg
  11. {
  12.     template<std::size_t poolSize = sz_KB(64), uint16_t maxPools = 32>
  13.     class PtrBumpAllocator
  14.     {
  15.         public:
  16.             typedef uintptr_t size_type;
  17.             typedef uintptr_t ptrint_type;
  18.  
  19.             /*
  20.                 \brief Constructor. Creates the first pool and does the setup. Will throw if it can't create it.
  21.             */
  22.             PtrBumpAllocator()
  23.             {
  24.                 // Quick tests to see if someone isn't trying to fool us.
  25.                 static_assert(maxPools > 1,"Not enough space to allocate first pool !");
  26.  
  27.                 // Create the first pool
  28.                 if (!createPool())
  29.                     throw std::exception("Couldn't allocate the first pool!");
  30.             }
  31.  
  32.             /*
  33.                 \brief Allocates (size) bytes of memory, bumping the pointer by (size) bytes.
  34.                 \param size The size of the chunk of memory you want to allocate in bytes.
  35.                 \returns Your chunk of memory, nullptr if the allocator can't allocate any more memory.
  36.             */
  37.             void* allocate(const size_type &size)
  38.             {
  39.                 // if the object is too big to be allocated, return nullptr.
  40.                 if (!doesObjectFit(size))
  41.                     return nullptr;
  42.  
  43.                 // if the allocator failed to create a new pool when required, return nullptr
  44.                 if (createNewPoolIfRequired(size) < 0)
  45.                     return nullptr;
  46.  
  47.                 // Else, if everything's alright, go for it.
  48.                 assert(allocPtr && "AllocPtr cannot be null at this stage.");
  49.                 auto tmp = allocPtr;
  50.                 allocPtr = static_cast<char*>(allocPtr) + size;
  51.                 return tmp;
  52.             }
  53.  
  54.             /*
  55.                 \brief Templated version of allocate which uses sizeof() to call the base allocate. See non-templated allocate overload for more details.
  56.             */
  57.             template<typename DataTy>
  58.             DataTy* allocate()
  59.             {
  60.                 return static_cast<DataTy*>(allocate(sizeof(DataTy)));
  61.             }
  62.  
  63.             /*
  64.                 \brief Deallocates the pointer. This just zeroes the memory unless it's the last object allocated, then, it's actually freed.
  65.                 \param ptr The pointer which holds the chunk of memory you own.
  66.                 \param sz The size of the chunk of memory in bytes.
  67.             */
  68.             void deallocate(void *ptr, const size_type &sz)
  69.             {
  70.                 // set everything to zero
  71.                 memset(ptr, 0, sz);
  72.                
  73.                 // Decrease the pointer if we can.
  74.                 if (allocPtr == (static_cast<char*>(ptr) + sz))
  75.                     allocPtr = static_cast<char*>(allocPtr) - sz;
  76.             }
  77.  
  78.             /*
  79.                 \brief Templated version of deallocate which uses sizeof() to call the base deallocate.
  80.             */
  81.             template<typename DataTy>
  82.             void deallocate(DataTy *ptr)
  83.             {
  84.                 deallocate(ptr, sizeof(DataTy));
  85.             }
  86.  
  87.             /*
  88.                 \brief Destroys every pool.
  89.             */
  90.             void destroy()
  91.             {
  92.                 // Note: Due to the nature of the unique_ptrs, this "destroy" function is useless when called from the destructor, however, it's provided
  93.                 // as a mean of resetting the pool.
  94.  
  95.                 // Free the first pool, starting a chain reaction where every pool will be deleted.
  96.                 if(firstPool)
  97.                     firstPool.reset();
  98.                
  99.                 // Set all the member variables to safe values.
  100.                 curPool = nullptr;
  101.                 poolCount = 0;
  102.                 allocPtr = nullptr;
  103.             }
  104.            
  105.             /*
  106.                 \brief Destructor (just calls destroy())
  107.             */
  108.             ~PtrBumpAllocator()
  109.             {
  110.                 destroy();
  111.             }
  112.  
  113.             /*
  114.                 \returns True if the object will fit in a pool.
  115.             */
  116.             bool doesObjectFit(const size_type& sz) const
  117.             {
  118.                 return (poolSize >= sz);
  119.             }
  120.  
  121.             /*
  122.                 \brief  Displays a detailled dump to get an overview of Allocator.
  123.                         This really just displays MaxPools and PoolSize and calls smallDump()
  124.             */
  125.             void dump() const
  126.             {
  127.                 std::cout << "MaxPools: " << maxPools << "\n";
  128.                 std::cout << "Pool Size: " << poolSize << "\n";
  129.                 smallDump();
  130.             }
  131.  
  132.             /*
  133.                 \brief Displays a condensed dump to get an overview of Allocator.
  134.             */
  135.             void smallDump() const
  136.             {
  137.                 std::cout << "Pools: " << poolCount << "\n";
  138.                 std::cout << "Curpool address: " << (void*)curPool << "\n";
  139.                 std::cout << "AllocPtr address: " << (void*)allocPtr << "\n";
  140.                 std::cout << "Bytes in current pool: " << (ptrdiff_t)(((char*)allocPtr) - ((char*)curPool)) << "\n";
  141.             }
  142.         private:
  143.             /*
  144.                 \brief Creates a pool if the current pool can't support an allocation of size sz.
  145.                 \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.
  146.             */
  147.             char createNewPoolIfRequired(const size_type &sz)
  148.             {
  149.                 // If there's no pool, allocate one.
  150.                 if (!firstPool)
  151.                     return createPool() ? 1 : -1;
  152.  
  153.                 // If the current pool can't hold the data, create a new one.
  154.                 else if (((static_cast<char*>(allocPtr) + sz) > curPool->upperBound))
  155.                     return createPool() ? 1 : -1;
  156.  
  157.                 return 0;
  158.             }
  159.  
  160.             /*
  161.                 \returns True if we can't allocate any more pools.
  162.             */
  163.             bool canCreateMorePools() const
  164.             {
  165.                 return (poolCount < maxPools);
  166.             }
  167.  
  168.             /*
  169.                 \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.
  170.             */
  171.             bool createPool()
  172.             {
  173.                 if (!canCreateMorePools())
  174.                     return false;
  175.  
  176.                 if (curPool)
  177.                 {
  178.                     assert(firstPool && "curPool isn't null, but firstPool is set?");
  179.                     curPool->next = std::make_unique<Pool>();
  180.                     curPool = curPool->next.get();
  181.                 }
  182.                 else
  183.                 {
  184.                     assert(!firstPool && "curPool is null, but firstPool isn't?");
  185.                     firstPool = std::make_unique<Pool>();
  186.                     curPool = firstPool.get();
  187.                 }
  188.  
  189.                 poolCount++;
  190.                 allocPtr = curPool->data;
  191.                 return true;
  192.             }
  193.  
  194.             /*
  195.                 \brief A Single Pool.
  196.             */
  197.             struct Pool
  198.             {
  199.                 Pool() : upperBound(data + (poolSize-1))
  200.                 {
  201.                     // Set everything to zero.
  202.                     memset(data, 0, sizeof(data));
  203.                 }
  204.  
  205.                 // The data
  206.                 char data[poolSize];
  207.  
  208.                 // The upper bound of the data
  209.                 const char * const upperBound;
  210.  
  211.                 // The next pool
  212.                 std::unique_ptr<Pool> next;
  213.             };
  214.  
  215.             // Tracking variables
  216.             std::unique_ptr<Pool> firstPool;
  217.             Pool* curPool = nullptr;
  218.             size_type poolCount = 0;
  219.             void * allocPtr = nullptr;
  220.     };
  221. }
  222.  
  223. #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement