pastel/sys/allocator/arena_allocator/
#ifndef PASTELSYS_ARENA_ALLOCATOR_HPP
#define PASTELSYS_ARENA_ALLOCATOR_HPP
#include "pastel/sys/allocator/arena_allocator.h"
#include "pastel/sys/ensure.h"
namespace Pastel
{
    inline ArenaAllocator::ArenaAllocator(
        integer unitSize, integer blockSize)
        : blocks_()
        , unitsAllocated_(0)
        , unitsReserved_(0)
        , unitSize_(unitSize)
        , blockSize_(blockSize)
    {
        ENSURE_OP(unitSize, >=, 1);
        ENSURE_OP(blockSize, >=, 1);
    }
    inline ArenaAllocator::~ArenaAllocator()
    {
        REPORT1(unitsAllocated_ != 0, unitsAllocated_);
        clear();
    }
    inline void ArenaAllocator::swap(ArenaAllocator& that)
    {
        blocks_.swap(that.blocks_);
        std::swap(unitsAllocated_, that.unitsAllocated_);
        std::swap(unitsReserved_, that.unitsReserved_);
        std::swap(unitSize_, that.unitSize_);
        std::swap(blockSize_, that.blockSize_);
    }
    inline void ArenaAllocator::clear()
    {
        // For every block..
        Iterator iter(blocks_.begin());
        Iterator iterEnd(blocks_.end());
        while (iter != iterEnd)
        {
            // ..Deallocate the block..
            deallocateBlock(&*iter);
            ++iter;
        }
        // And clear the list of blocks.
        blocks_.clear();
        ASSERT1(unitsReserved_ == 0, unitsReserved_);
        unitsAllocated_ = 0;
        unitsReserved_ = 0;
    }
    inline integer ArenaAllocator::unitSize() const
    {
        return unitSize_;
    }
    inline integer ArenaAllocator::allocated() const
    {
        return unitsAllocated_;
    }
    inline integer ArenaAllocator::capacity() const
    {
        return unitsReserved_;
    }
    inline void* ArenaAllocator::allocate(integer units)
    {
        ENSURE_OP(units, >, 0);
        Block* block = 0;
        if (!blocks_.empty())
        {
            block = &blocks_.back();
        }
        if (!block ||
            block->unitsReserved_ - block->unitsAllocated_ < units)
        {
            // Not enough space on this block,
            // allocate another one.
            allocateBlock(units);
            block = &blocks_.back();
        }
        // Calculate the memory address of the first
        // free unit inside the block.
        integer firstFreeIndex = block->unitsAllocated_;
        uint8* const memAddress = block->data_ + firstFreeIndex * unitSize_;
        // Keep up book-keeping.
        block->unitsAllocated_ += units;
        unitsAllocated_ += units;
        // Return the allocated memory address.
        return (void*)memAddress;
    }
    inline void ArenaAllocator::deallocate(const void* memoryIgnored, integer units)
    {
        unused(memoryIgnored);
        unitsAllocated_ -= units;
        REPORT1(unitsAllocated_ < 0, unitsAllocated_);
    }
    // Private
    inline void ArenaAllocator::allocateBlock(integer minUnits)
    {
        ASSERT1(minUnits >= 0, minUnits);
        // The block must be at least of this size.
        integer blockSize = minUnits;
        // Requests larger than or equal to blockSize_ 
        // are satisfied exactly.
        if (blockSize < blockSize_)
        {
            // Otherwise, we seek to possibly allocate 
            // a larger block.
            // The block size must be at least 16.
            integer MinBlockSize = 16;
            blockSize = std::max(blockSize, MinBlockSize);
            // Choose the block size by an 1.5-exponential rule,
            // if it makes the block larger. This gives blocks
            // smoothly increasing sizes at the beginning,
            // rather than allocating 'blockSize_' blocks right
            // from the start. The intent here is to save
            // memory when only a small number of elements
            // is needed.
            blockSize = std::max(unitsAllocated_ >> 1, blockSize);
            // But limit that exponential rule so that it
            // does not exceed blockSize_. 
            blockSize = std::min(blockSize, blockSize_);
        }
        // Allocate and initialize a new
        // block.
        Block block;
        block.data_= (uint8*)allocateRaw(blockSize * unitSize_);
        block.unitsAllocated_ = 0;
        block.unitsReserved_ = blockSize;
        try
        {
            blocks_.push_back(block);
        }
        catch(...)
        {
            deallocateRaw((void*)block.data_);
            throw;
        }
        // Keep up the book-keeping.
        unitsReserved_ += blockSize;
    }
    inline void ArenaAllocator::deallocateBlock(
        Block* block)
    {
        ASSERT(block);
        // Keep up book-keeping.
        unitsReserved_ -= block->unitsReserved_;
        // Delete block's raw memory.
        deallocateRaw((void*)block->data_);
    }
    inline void swap(
        ArenaAllocator& left,
        ArenaAllocator& right)
    {
        left.swap(right);
    }
}
#endif