/*      exarray.h

    Динамические массивы и указатели с 
    автоматической проверкой индекса
    (сокращенный вариант).
    http://www.imach.uran.ru/exarray

    (C) Р.Н.Шакиров, ИМаш УрО PAH, 1998 - 2000
    All Rights Reserved.
*/
//  Шаблон динамического массива.

template <class T> class expoint;
template <class T> class exarray
{
  T*        e;  // Базовый массив.
  unsigned  len;    // Число элементов.

//  Указатель статического элемента типа T
//  с фиксированными значениями, заданными
//  конструктором по умолчанию.

  static  T*    stub ();

//  Распределение памяти.

  void  realloc (unsigned size);

//  Автоматическое увеличение размера
//  при обращении по индексу i.

  void  grow    (unsigned i);

//  Конструктор копирования и операция
//  присваивания объявлены приватными,
//  поэтому при присваивании динамического
//  массива или передаче его в функцию по
//  значению выдается сообщение об ошибке.
//  Динамические массивы можно передавать в
//  функцию по ссылке, через динамический
//  указатель, а также через const T*.

  exarray        (const exarray<T>&);
  exarray<T>& operator = (const exarray<T>&);

public:

//  Конструктор динамического массива.
//  Распределение памяти будет проводиться 
//  в процессе заполнения массива, поэтому
//  начальное значение len равно 0.
//  Значение e задается методом stub() и
//  трактуется, как "сигнатура" пустого 
//  динамического массива.

  exarray ()        { e = stub(); len = 0;  }

//  Деструктор.

  ~exarray()        { realloc (0);      }

//  Доступ к массиву с проверкой индекса.
//  При выходе индекса за границы массива
//  размер массива автоматически увеличивается,
//  за исключением следующих особых ситуаций:
//  1) Переполнение разрядной сетки индекса -
//     вызывается функция abort().
//  2) Нехватка памяти - вызывается функция,
//     установленная по set_new_handler из
//     new.h, а если ее нет - то abort().
//  Отрицательный индекс интерпретируется,
//  как большое положительное число, что
//  для всех базовых типов T, кроме char,
//  приводит к вызову abort().
//  При увеличении размера проводится обнуление
//  памяти, после чего для всех новых элементов
//  выполняются конструкторы по умолчанию.

  T&    operator [] (unsigned i)
    { if (len<=i) grow(i); return e[i];  }

  T&    operator [] (int i)
    { return (operator [] ((unsigned) i));  }

  T&    operator *  ()
    { if (len<=0) grow(0); return e[0];  }

//  Доступ к массиву без проверки индекса.
//  Данная возможность реализована для целей
//  ручной оптимизации программ.

  T&    item (unsigned i) { return (e [i]); }
  T*    base ()       { return (e);     }

//  Автоматическое преобразование в const T*
//  применяется при передаче базового массива
//  в функцию через параметр const T*, а также
//  при логической проверке, сравнении и вычи-
//  тании динамических массивов и указателей.
//  Если массив пустой, то выдается указатель,
//  на статический элемент, сформированный
//  методом stub() - это позволяет передавать
//  пустой массив exarray<char> в качестве
//  параметра __src функций strcpy и strcat.
//  Операции логической проверки, сравнения и
//  вычитания выполняются для базового массива
//  по штатным правилам языка C.
//  В частности, логическая проверка любого
//  динамического массива, в том числе и
//  пустого, всегда выдает истину. 

  operator const T* ()    { return (base());    }

//  Арифметические операции.

  expoint<T>  operator +  (unsigned i)
    { return (expoint<T> (*this,  i));    }

  expoint<T>  operator +  (int i)
    { return (expoint<T> (*this,  i));    }

  expoint<T>  operator -  (unsigned i)
    { return (expoint<T> (*this, 0-i));   }

  expoint<T>  operator -  (int i)
    { return (expoint<T> (*this, 0-i));   }
};

//  Функция exmrealloc выделяет, удлиняет,
//  укорачивает и освобождает блоки памяти,
//  заполненные нулями, а также обрабатывает
//  ошибки. См. exarray.cpp.

void exmrealloc (void **p, unsigned size,
               unsigned oldsize);

//  Функция excalcsize вычисляет размер блока
//  памяти в байтах, который должен быть не
//  меньше требуемого. См. exarray.cpp.

unsigned excalcsize (unsigned size);

//  Функция exmuladd вычисляет значение
//  n * s + k, которое должно укладываться
//  в разрядную сетку int.
//  При переполнении выдается ~0u.

inline unsigned exmuladd
    (unsigned s, unsigned n, unsigned k)
{
  return ((n <= ((~0u>>1)-k)/s)? (n*s+k): ~0u);
}

//  Фиктивный класс для вызова конструкторов
//  и деструкторов без распределения памяти.

template <class T> struct __EXRELOC
{
  T value;              
  void* operator new (size_t, void* p)
                 { return p;    }
  void  operator delete (void*)  { ; }
};

//  Приватный метод stub выдает указатель
//  статического элемента с фиксированным
//  значением, заданным конструктором T.

template <class T> T* exarray<T>::stub()
{
  static T _stub;
  return (&_stub);
}

//  Приватный метод realloc устанавливает
//  размер динамического массива в байтах.
//  При увеличении размера выполняется
//  обнуление и вызываются конструкторы
//  элементов, а при уменьшении размера 
//  вызываются деструкторы элементов.

template <class T> void  exarray<T>::realloc
     (unsigned size)
{
  unsigned n = size/sizeof (T); // Новая длина.

  if (len == 0)
  {
//  Если динамический массив пустой, то
//  проверяем корректность значения e.
//  Это позволяет, в частности, запретить
//  обращение по "нулевому" динамическому
//  указателю. Присваивание size = ~0u
//  обеспечивает вызов функции abort().
    if (e != stub()) size = ~0u;

//  Если динамический массив пустой, то перед
//  распределением памяти e следует обнулить.
    e = 0;
  }

//  Вызов деструкторов при укорочении массива.
  while (len > n)
    { len--; delete (__EXRELOC<T> *) (e + len); }

//  Распределение памяти.
  exmrealloc ((void **)&e, size, sizeof(T) * len);

//  Вызов конструкторов при удлинении массива.
  while (len < n)
    { new (e + len)__EXRELOC<T>; len++; }

//  Для пустого динамического массива значение
//  e задается методом stub().
  if (len == 0) e = stub();
}

//  Приватный метод grow распределяет память
//  для элементов с индексами от 0 до >= i,
//  выполняя дополнительное резервирование.

template <class T> void exarray<T>::grow (unsigned i)
{
  realloc (excalcsize (
    exmuladd (sizeof(T), i, sizeof(T))
  ));
}

//  Заглушка динамического массива,
//  Используется при присваивании динамическим
//  указателям значения 0. В отличие от
//  обычного динамического массива, логическая
//  проверка заглушки выдает 0.
//  Заглушка определена в exarray.cpp.

extern  class   { void* e; unsigned len; } __EXNULL;

//  Шаблон динамического указателя.
//  Динамический указатель содержит смещение
//  относительно начала динамического массива,
//  которое может выходить за границу массива,
//  а также может быть отрицательным.
//  Отрицательное смещение эквивалентно
//  большому положительному смещению с
//  тем же двоичным кодом.

template <class T> class expoint
{
  exarray<T>* a;  // Динамический массив.
  int           k;  // Смещение в массиве.

public:

//  Конструктор динамического указателя.
//  с начальным значением 0.
//  Если указателю не присвоен динамический
//  массив, то его логическая выдает ложь,
//  а при попытке обращения по указателю
//  вызывается функция abort().

  expoint ()      {a=(exarray<T>*)&__EXNULL; k=0;}

//  Конструктор для присваивания числового
//  значения динамическому указателю.
//  В отличие от обычного указателя,
//  динамическому указателю можно без
//  явного преобразования типа присвоить
//  ненулевое числовое значение.
//  Независимо от числового значения,
//  логическая проверка указателя выдает ложь,
//  а при попытке обращения по указателю
//  вызывается функция abort().

  expoint (int i) {a=(exarray<T>*)&__EXNULL; k=i;}

//  Конструкторы для присваивания динамического
//  массива динамическому указателю.
//  После присваивания логическая проверка
//  указателя выдает истину и становится
//  возможным обращение к элементам массива
//  с автоматическим увеличением его размера.

  expoint (exarray<T>&m)        { a = &m; k = 0; }
  expoint (exarray<T>&m, int i) { a = &m; k = i; }

//  Доступ к массиву с проверкой индекса.

  T& operator [] (unsigned i) { return (*a)[k+i];}
  T& operator [] (int i)      { return (*a)[k+i];}
  T& operator *  ()           { return (*a)[k];  }

//  Доступ к массиву без проверки индекса.

  T& item (int i) { return (a->item(k + i));     }
  T* base ()      { return (a->base() + k);      }

//  Автоматическое преобразование в const T*
//  применяется при передаче базового массива
//  в функцию через параметр const T*, а также
//  при логической проверке, сравнении и вычи-
//  тании динамических массивов и указателей.
//  НЕ гарантируется, что указатель будет
//  установлен на размещенный элемент.

  operator const T* () { return (a->base()); }

//  Арифметические операции.

  expoint<T>  operator +  (unsigned i)
    { return (expoint<T>(*a, k + i)); }

  expoint<T>  operator +  (int i)
    { return (expoint<T>(*a, k + i)); }

  expoint<T>  operator -  (unsigned i)
    { return (expoint<T>(*a, k - i)); }

  expoint<T>  operator -  (int i)
    { return (expoint<T>(*a, k - i)); }

  expoint<T>& operator += (unsigned i)
    { k += i;  return (*this);      }

  expoint<T>& operator += (int i)
    { k += i;  return (*this);      }

  expoint<T>& operator -= (unsigned i)
    { k -= i;  return (*this);      }

  expoint<T>& operator -= (int i)
    { k -= i;  return (*this);      }

  expoint<T>& operator ++ ()
    { k++;     return (*this);      }

  expoint<T>& operator -- ()
    { k--;     return (*this);      }

  expoint<T>  operator ++ (int)
    { expoint<T>t = *this; k++; return t; }

  expoint<T>  operator -- (int)
    { expoint<T>t = *this; k--; return t; }
};

//  Примеры переопределения функций string.h.

#include<string.h>
//  Обходим ошибку в Borland C++
#ifdef  __BORLANDC__
#pragma warn -ill
#pragma intrinsic -strcpy
#pragma intrinsic -strcat
#pragma warn .ill
#endif

inline  expoint<char>
strcpy  (expoint<char>__dest, const char *__src)
{
  __dest [strlen (__src)];  // Увеличиваем размер.
  strcpy (__dest.base(), __src);
  return (__dest);
}

inline  expoint<char>
strcat  (expoint<char>__dest, const char *__src)
{
  strcpy (__dest + strlen (__dest), __src);
  return (__dest);
}