/*
 *  allocator.cpp
 *
 *  This file is part of NEST.
 *
 *  Copyright (C) 2004 The NEST Initiative
 *
 *  NEST is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  NEST is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with NEST.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "allocator.h"

sli::pool::pool()
  : initial_block_size(1024),
    growth_factor(1),
    block_size(initial_block_size),
    el_size(sizeof(link)),
    instantiations(0),
    total(0),
    capacity(0),
    chunks(0), 
    head(0),
    initialized_(false)
{}

sli::pool::pool(const sli::pool &p)
  : initial_block_size(p.initial_block_size),
    growth_factor(p.growth_factor),
    block_size(initial_block_size),
    el_size(sizeof(link)),
    instantiations(0),
    total(0),
    capacity(0),
    chunks(0), 
    head(0),
    initialized_(false)
{}


sli::pool::pool(size_t n, size_t initial, size_t growth)
  : initial_block_size(initial),
    growth_factor(growth),
    block_size(initial_block_size),
    el_size( ( n < sizeof(link))? sizeof(link): n),
    instantiations(0),
    total(0),
    capacity(0),
    chunks(0), 
    head(0),
    initialized_(true)
{}

void sli::pool::init(size_t n, size_t initial, size_t growth)
{
  assert(instantiations == 0);

  initialized_=true;

  initial_block_size=initial;
  growth_factor=growth;
  block_size=initial_block_size;
  el_size =  ( n < sizeof(link))? sizeof(link): n;
  instantiations=0;
  total=0;
  capacity=0;
  chunks=0; 
  head=0;
}

sli::pool::~pool()
{
  chunk *n=chunks;
  while(n)
  {
    chunk *p=n;
    n=n->next;
    delete p;
  }
}

sli::pool & sli::pool::operator=(const sli::pool&p)
{
  if(&p == this)
    return *this;

  initial_block_size=p.initial_block_size;
  growth_factor=p.growth_factor;
  block_size=initial_block_size;
  el_size=p.el_size;
  instantiations=0;
  total=0;
  chunks=0; 
  head=0;
  initialized_=false;
  
  return *this;
}

void sli::pool::grow(size_t nelements)
{
  chunk *n = new chunk(nelements*el_size);
  total    += nelements;

  n->next = chunks;
  chunks = n;
  char *start= n->mem;
  char *last= &start[ (nelements-1)*el_size];

  for(char *p=start; p<last; p+=el_size)
    reinterpret_cast<link*>(p)->next=reinterpret_cast<link*>(p+el_size);
  reinterpret_cast<link*>(last)->next = NULL;
  head = reinterpret_cast<link*>(start);

}

void sli::pool::grow(void)
{
  grow(block_size);
  block_size *= growth_factor;
}

void sli::pool::reserve(size_t n)
{
    const size_t capacity=total-instantiations; 
    if(capacity < n)
    grow(((n-capacity)/block_size+1)*block_size);
}