Node allocators

Files CCore/inc/NodeAllocator.h CCore/src/NodeAllocator.cpp

Node allocator is used to allocate and destroy list and tree nodes. Node allocator type is a template parameter for list and tree map containers.

NodeAllocator

This is a default node allocator. Its only function is the counting allocated nodes. Node is allocated using the operator new. This class is also a model for other node allocators.


template <class Node>
class NodeAllocator : NoCopy
 {
   ulen count;
   
  public: 
   
   // constructors
   
   NodeAllocator() noexcept : count(0) {}
   
   // std move

   NodeAllocator(NodeAllocator<Node> &&obj) noexcept;

   NodeAllocator<Node> & operator = (NodeAllocator<Node> &&obj) noexcept;

   // props
   
   ulen operator + () const { return count; }
   
   bool operator ! () const { return !count; }
   
   ulen getCount() const { return count; }

   // methods
   
   template <class ... SS>
   Node * alloc(SS && ... ss);
  
   void free_nonnull(Node *node);
   
   bool free(Node *node);

   // swap/move objects
   
   void objSwap(NodeAllocator<Node> &obj);
   
   explicit NodeAllocator(ToMoveCtor<NodeAllocator<Node> > obj);
 };

Constructor has no arguments. Destructor is trivial. Allocator has a counter for allocated nodes. So you have to destroy all allocated nodes before the object destruction.

NodeAllocator is not copyable, but std movable. The original object is nullified during the move.

operator + and operator ! can be used to check, if there are allocated nodes.

getCount() returns the number of allocated nodes.

alloc() allocates a node. Arguments are forwarder to the node constructor.

free_nonnull() destroys the node, pointer must not be null.

free() destroys the node, if the node is not null. It returns true, iff the node is not null.

NodeAllocator is swappable and movable.

NodePoolAllocator

This allocator uses a memory block pool to store nodes. This approach greatly amortizes the cost of the memory allocation, but it wastes memory if there are many destroyed nodes. To avoid this problem use compact variants of lists and maps.


template <class Node>
class NodePoolAllocator : NoCopy
 {
   ....

   ulen count;

  public: 
   
   // constructors
   
   explicit NodePoolAllocator(ulen pool_count=MemBlockPool::DefaultCount) noexcept; // == 100
   
   ~NodePoolAllocator() { if( count ) NodePoolAbort(); }
   
   // std move

   NodePoolAllocator(NodePoolAllocator &&obj) noexcept;

   NodePoolAllocator & operator = (NodePoolAllocator &&obj) noexcept;

   // props
   
   ulen operator + () const { return count; }
   
   bool operator ! () const { return !count; }
   
   ulen getCount() const { return count; }
   
   // methods
   
   template <class ... SS>
   Node * alloc(SS && ... ss);
  
   void free_nonnull(Node *node);
   
   bool free(Node *node);
   
   // swap/move objects
   
   void objSwap(NodePoolAllocator<Node> &obj);
   
   explicit NodePoolAllocator(ToMoveCtor<NodePoolAllocator<Node> > obj);
 };

Constructor takes the number of nodes in the single memory block as an argument, it is 100 by default. Destructor calls abort, if not all nodes are destroyed.

NodePoolAllocator is not copyable, but std movable. The original object is nullified during the move. The target object must be empty or the execution is aborted.

All other methods are the same as for NodeAllocator.

MemBlockPool

MemBlockPool can be used to allocate memory blocks of the given length and alignment.


class MemBlockPool : NoCopy
 {
   ....

  public: 
  
   // constructors
   
   static const ulen DefaultCount = 100 ;
  
   MemBlockPool(ulen len,ulen align_of,ulen alloc_count=DefaultCount);
   
   ~MemBlockPool();
   
   // std move

   MemBlockPool(MemBlockPool &&obj) noexcept;

   MemBlockPool & operator = (MemBlockPool &&obj) noexcept;

   // methods
   
   void * alloc();
   
   void free(void *mem);
   
   // AllocGuard
   
   class AllocGuard : NoCopy
    {
      MemBlockPool &pool;
      void *mem;
      
     public:
     
      explicit AllocGuard(MemBlockPool &pool);
      
      ~AllocGuard();
      
      operator void * () const { return mem; } // allocated memory block
      
      void * disarm(); // mem is returned
    };
   
   // swap/move objects
   
   void objSwap(MemBlockPool &obj);
   
   explicit MemBlockPool(ToMoveCtor<MemBlockPool> obj);
 };

Constructor takes the following arguments: len is the memory block length, align_of is the memory block alignment, alloc_count is the number of memory blocks in the single larger memory block. Destructor frees all allocated memory, so make sure the all memory blocks are freed before the object destruction.

MemBlockPool is not copyable, but std movable. The original object is nullified during the move.

alloc() allocates a memory block. An exception is thrown in case of error.

free() frees the memory block, mem must not be null.

The inner class AllocGuard can be used to allocate and hold temporary a memory block. Destructor frees the block, unless you "disarm" the guard.

MemBlockPool is swappable and movable.