/*
 * Copyright (C) Sergey P. Derevyago, 2003-2004.
 *
 * Permission to copy, use, modify, sell and distribute this software is granted
 * provided this copyright notice appears in all copies.
 * This software is provided "as is" without express or implied warranty, and
 * with no claim as to its suitability for any purpose.
 *
 */

/** @file
 * Определение класса fixed_alloc.
 */

#ifndef __FIX_ALLOC_HPP__
 #define __FIX_ALLOC_HPP__

#include <stddef.h>

/** личное пространство имен класса fixed_alloc, содержащее детали реализации */
namespace fixed_alloc_private {

 /**
  * Запрашивает у системы память размером (около) CHUNK_SIZE и создает в ней
  * связный список свободных кусков размера type_sz. Голова списка возвращается
  * в параметре head. Для работоспособности функции должно выполняться следующее
  * условие: type_sz>=sizeof(void*). В случае нехватки памяти возбуждает
  * std::bad_alloc. Константа CHUNK_SIZE определена в файле-реализации.
  */
 void get_mem(void*& head, size_t type_sz);

 /**
  * Вспомогательный класс для реализации fixed_alloc. Его назначение состоит в
  * выделении блоков памяти размера SIZE, в результате чего все объекты T
  * одинакового (после округления до sizeof(void*)) размера будут использовать
  * один и тот же список свободных кусков.
  */
 template <size_t SIZE>
 class void_alloc {
       /** голова списка свободных кусков памяти размера SIZE */
       static void* head;

  public:
       /** выделяет память размера SIZE */
       static void* alloc()
       {
        if (!head) get_mem(head, SIZE);

        void* ret=head;
        head=*(void**)head;

        return ret;
       }

       /** освобождает ранее выделенную память */
       static void free(void* ptr)
       {
        *(void**)ptr=head;
        head=ptr;
       }
 };

 /** определение статического члена void_alloc::head */
 template <size_t SIZE>
 void* void_alloc<SIZE>::head;

}

/**
 * Класс fixed_alloc<T> является быстрым распределителем памяти фиксированного
 * размера для элементов типа T. Он выделяет память кусками (около) CHUNK_SIZE и
 * нарезает их на части размера SIZE. Полученная аллокатором у системы память ей
 * не возвращается, а используется повторно при выделениях/освобождениях
 * элементов T. Константа CHUNK_SIZE определена в файле-реализации.
 */
template <class T>
class fixed_alloc {
 public:
      /**
       * Размер выделяемой под T памяти. Округляем sizeof(T) до sizeof(void*)
       * воизбежание проблем с выравниванием.
       */
      enum { SIZE=(sizeof(T)+sizeof(void*)-1)/sizeof(void*)*sizeof(void*) };

      /**
       * Выделяет память (размера SIZE) для объекта T. При нехватке памяти будет
       * возбуждено std::bad_alloc.
       */
      static T* alloc()
      {
       return (T*)fixed_alloc_private::void_alloc<SIZE>::alloc();
      }

      /**
       * Освобождает ранее выделенную память. При этом, она не возвращается
       * системе, а остается в списке свободных кусков.
       *
       * Вызов free(0) обрабатывается некорректно (происходит попытка
       * разыменования 0). На самом деле это не является большой проблемой т.к.
       * если с помощью free() реализовать X::operator delete(), то вызов
       * delete (X*)0 не приведет к вызову free(0) -- это гарантируется языком.
       * Проблемы будут только при непосредственном вызове вида X::operator
       * delete(0), что в реальном коде практически не встречается, так что
       * проверка на 0 была опущена для улучшения производительности (в крайнем
       * случае, проверку на 0 можно произвести самостоятельно непосредственно
       * перед вызовом X::operator delete(ptr) или прямо в теле X::operator
       * delete()).
       */
      static void free(void* ptr)
      {
       fixed_alloc_private::void_alloc<SIZE>::free(ptr);
      }
};

/**
 * Класс sized_alloc является быстрым распределителем памяти известного размера.
 * Его отличие от класса fixed_alloc состоит в том, что размер запрашиваемой
 * памяти задается при каждом вызове функций alloc() и free(), в то время как в
 * fixed_alloc он является параметром шаблона и, следовательно, жестко
 * зафиксирован (что делает fixed_alloc чуть более производительным). Класс
 * sized_alloc следует применять для реализации распределителей памяти базовых
 * классов (с обязательным виртуальным деструктором), которые наследуются
 * классами производными. Дело в том, что размер объекта производного класса,
 * как правило, больше, так что определенный в классе распределитель памяти
 * должен быть объявлен с дополнительным параметром size_t, чье значение должно
 * передаваться в alloc() и free().
 */
class sized_alloc {
  public:
      /**
       * Выделяет память указанного размера или возбуждает std::bad_alloc.
       * Алгоритм работы полностью аналогичен алгоритму класса fixed_alloc, за
       * тем исключением, что в случае size>MAX_SIZE память будет выделена с
       * помощью operator new(). Константа MAX_SIZE определена в
       * файле-реализации.
       */
      static void* alloc(size_t size);

      /**
       * Освобождает ранее выделенную память. Алгоритм работы полностью
       * аналогичен алгоритму класса fixed_alloc, за тем исключением, что в
       * случае size>MAX_SIZE память действительно освобождается (посредством
       * вызова operator delete(ptr)). Константа MAX_SIZE определена в
       * файле-реализации.
       */
      static void free(void* ptr, size_t size);
};

#endif
