//+------------------------------------------------------------------+
//| Advanced memory pool for variable-size allocations               |
//| MQL5 version without raw pointer arithmetic                      |
//+------------------------------------------------------------------+
#property strict

class CMemoryPool
{
private:
   // Usage tracking
   bool  m_blockUsage[]; 
   
   // Size settings
   int   m_totalSize;    // Total bytes in the pool
   int   m_blockSize;    // Size of each block
   
   // Statistics
   int   m_used;         // How many bytes are currently in use

public:
   // Memory buffer (dynamic array of bytes)
   uchar m_memory[];
   
   // Constructor
   CMemoryPool(const int totalSize=1024*1024, // default 1 MB
               const int blockSize=1024)      // default 1 KB blocks
   {
      m_totalSize = totalSize;
      m_blockSize = blockSize;
      m_used      = 0;
      
      // Allocate the memory pool
      ArrayResize(m_memory, m_totalSize);
      
      // Initialize block usage tracking
      int numBlocks = m_totalSize / m_blockSize;
      ArrayResize(m_blockUsage, numBlocks);
      ArrayInitialize(m_blockUsage, false);
      
      Print("Memory pool initialized: ", 
            m_totalSize, " bytes, ", 
            numBlocks, " blocks of ", 
            m_blockSize, " bytes each");
   }
   
   // Allocate memory from the pool
   // Returns an offset (>= 0) if successful, or -1 on failure
   int Allocate(const int size)
   {
      // Round up how many blocks are needed
      int blocksNeeded = (size + m_blockSize - 1) / m_blockSize;
      int consecutive  = 0;
      int startBlock   = -1;
      
      // Search for consecutive free blocks
      int numBlocks = ArraySize(m_blockUsage);
      for(int i=0; i < numBlocks; i++)
      {
         if(!m_blockUsage[i])
         {
            // Found a free block
            if(consecutive == 0)
               startBlock = i;
               
            consecutive++;
            
            // If we found enough blocks, stop
            if(consecutive >= blocksNeeded)
               break;
         }
         else
         {
            // Reset
            consecutive = 0;
            startBlock  = -1;
         }
      }
      
      // If we couldn't find enough consecutive blocks
      if(consecutive < blocksNeeded)
      {
         Print("Memory pool allocation failed: needed ", 
               blocksNeeded, " consecutive blocks");
         return -1;  // indicate failure
      }
      
      // Mark the found blocks as used
      for(int b=startBlock; b < startBlock + blocksNeeded; b++)
      {
         m_blockUsage[b] = true;
      }
      
      // Increase usage
      m_used += blocksNeeded * m_blockSize;
      
      // Return the offset in bytes where allocation starts
      return startBlock * m_blockSize;
   }
   
   // Free memory (by offset)
   void Free(const int offset)
   {
      // Validate offset
      if(offset < 0 || offset >= m_totalSize)
      {
         Print("Memory pool error: invalid offset in Free()");
         return;
      }
      
      // Determine the starting block
      int startBlock = offset / m_blockSize;
      
      // Walk forward, freeing used blocks
      int numBlocks = ArraySize(m_blockUsage);
      for(int b=startBlock; b < numBlocks; b++)
      {
         if(!m_blockUsage[b])
            break; // found an already-free block => done
         
         // Free it
         m_blockUsage[b] = false;
         m_used         -= m_blockSize;
      }
   }
   
   // Get usage statistics in %
   double GetUsagePercentage() const
   {
      return (double)m_used / (double)m_totalSize * 100.0;
   }
   
   // Destructor
   ~CMemoryPool()
   {
      // Optionally free arrays (usually automatic at script end)
      ArrayFree(m_memory);
      ArrayFree(m_blockUsage);
      
      Print("Memory pool destroyed. Final usage: ", 
            GetUsagePercentage(), "% of ", m_totalSize, " bytes");
   }
};

//+------------------------------------------------------------------+
//| Example usage in an Expert Advisor                               |
//+------------------------------------------------------------------+
int OnInit(void)
{
   // Create a memory pool
   CMemoryPool pool(1024*1024, 1024); // 1 MB total, 1 KB block size
   
   // Allocate 500 bytes from the pool
   int offset = pool.Allocate(500);
   if(offset >= 0)
   {
      // Write something in the allocated area
      pool.m_memory[offset] = 123;
      Print("Wrote 123 at offset=", offset, 
            " usage=", pool.GetUsagePercentage(), "%");
      
      // Free this block
      pool.Free(offset);
      Print("Freed offset=", offset, 
            " usage=", pool.GetUsagePercentage(), "%");
   }

   return(INIT_SUCCEEDED);
}

void OnTick(void)
{
   // ...
}