/* \file xNewDelete.h
 * \brief: header file for templated new/delete checking for non-null pointers
 */

#ifndef _XNEWDELETE_H_
#define _XNEWDELETE_H_

#include <cassert>

// memory management of types 
// T with non-trivial constructors require 
// C++ style memory management
#define USE_CXX_MEMORY_MANAGMENT_FOR_NON_POD_TYPES
// but for speed on may alternatively use C memory management
// but can do so safely only for T that are at most 
// plain old data structures (POD)
#ifndef USE_CXX_MEMORY_MANAGMENT_FOR_NON_POD_TYPES
#include <cstdlib>
#endif 

template <class T> 
T* xNew(unsigned int size) {
#ifdef USE_CXX_MEMORY_MANAGMENT_FOR_NON_POD_TYPES
  T* aT_p=new T[size];
  assert(aT_p);
  return aT_p;
#else
  T* aT_p=(T*)malloc(size*sizeof(T));
  assert(aT_p);
  return aT_p;
#endif  
}

template <class T> 
T** xNew(unsigned int dim1, unsigned int dim2) {
#ifdef USE_CXX_MEMORY_MANAGMENT_FOR_NON_POD_TYPES
  T* buf=new T[dim1*dim2];
  assert(buf );
  T** aT_pp =new T*[dim1];
  assert(aT_pp );
  for (unsigned int i=0;i<dim1;++i) {
    aT_pp [i]=buf;
    buf+=dim2;
  }
  return aT_pp ;
#else
  T* buf=(T*)malloc(dim1*dim2*sizeof(T));
  assert(buf );
  T** aT_pp =(T**)malloc(dim1*sizeof(T*));
  assert(aT_pp );
  for (unsigned int i=0;i<dim1;++i) {
    aT_pp [i]=buf;
    buf+=dim2;
  }
  return aT_pp ;
#endif
}

template <class T>
T* xNewZeroInit(unsigned int size) {
#ifdef USE_CXX_MEMORY_MANAGMENT_FOR_NON_POD_TYPES
  T* aT_p=xNew<T>(size);
  for (unsigned int i=0; i<size;++i) 
    aT_p[i]=(T)0;
  return aT_p;
#else
  T* aT_p=(T*)calloc(size,sizeof(T));
  assert(aT_p);
  return aT_p;
#endif
}

template <class T>
T** xNewZeroInit(unsigned int dim1, unsigned int dim2) {
#ifdef USE_CXX_MEMORY_MANAGMENT_FOR_NON_POD_TYPES
  T** aT_pp=xNew<T>(dim1,dim2);
  for (unsigned int i=0; i<dim1*dim2;++i)
    (*aT_pp)[i]=(T)0;
  return aT_pp;
#else
  T* buf=(T*)calloc(dim1*dim2*sizeof(T));
  assert(buf );
  T** aT_pp =(T**)malloc(dim1*sizeof(T*));
  assert(aT_pp );
  for (unsigned int i=0;i<dim1;++i) {
    aT_pp [i]=buf;
    buf+=dim2;
  }
  return aT_pp ;
#endif
}

template <class T>
void xDelete(T**& aT_pp) {
  if (aT_pp) {
#ifdef USE_CXX_MEMORY_MANAGMENT_FOR_NON_POD_TYPES
    delete [](*aT_pp);
    delete [](aT_pp);
#else
    free((void*)*aT_pp)
    free((void**)aT_pp);
#endif
  }
  aT_pp=0;
}

template <class T>
void xDelete(T*& aT_p) {
  if (aT_p) 
#ifdef USE_CXX_MEMORY_MANAGMENT_FOR_NON_POD_TYPES
    delete []aT_p;
#else
    free((void*)aT_p);
#endif
  aT_p=0;
}

template <class T> 
T* xReNew(T* old, unsigned int old_size, unsigned int size) {
#ifdef USE_CXX_MEMORY_MANAGMENT_FOR_NON_POD_TYPES
	T* aT_p=0;
	if (!old) { // no old memory
		if (size)  
		 aT_p=xNew<T>(size); // according to realloc behavior in manual page 
	}
	else { // have old memory
		if (!size)  // but 0 size
		 xDelete<T>(old); // according to realloc behavior in manual page
		else { // non-zero size
			assert(old_size); // have old memory - need to have old_size set or this call is bad
			// allocate new, delete old; ; even for the case when size is 
			// less than old_size we can't just keep the memory unchanged 
			// because otherwise classes that have ctors/dtors with side-effects 
			// may misbehave, for example classes with static instance/operations counters. 
			aT_p=xNew<T>(size);
			unsigned int iMax=(old_size<size)?old_size:size;
			for (unsigned int i=0; i<iMax;++i) { 
				// we need to copy the items by explicit assignments
				aT_p[i]=old[i];
			}
			xDelete<T>(old);
		}
	}
	return aT_p;
#else
	T* aT_p=0;
	aT_p=(T*)realloc((void*)old,size*sizeof(T));
	if (size) 
	 assert(aT_p); // according to realloc behavior in manual page
	return aT_p;
#endif 
}

#endif
