// ==========================================================================
// $Source: /var/lib/cvs/Givaro/src/kernel/memory/givaromm.h,v $
// Copyright(c)'1994-2009 by The Givaro group
// This file is part of Givaro.
// Givaro is governed by the CeCILL-B license under French law
// and abiding by the rules of distribution of free software.
// see the COPYRIGHT file for more details.
// Author: T. Gautier
// $Id: givaromm.h,v 1.7 2011-02-02 16:23:56 briceboyer Exp $
// ==========================================================================

/** @file givaromm.h
 * @ingroup memory
 * @brief Memory management in Givaro
 * two memory managers:
 * - the first one handle a set on free-list of blocs ;
 * - the second one implement a reference mecanism on the bloc.
 * .
 * The latter used method of the former.
 */
#ifndef __GIVARO_mm_H
#define __GIVARO_mm_H

#include <cstring>            // gcc 4.3
#include <stddef.h>           // size_t definition
#include <iostream>         // ostream definition
#include <new>
#include "givaro/givmodule.h"
#include "givaro/givbasictype.h"
#ifdef GIVARO_DEBUG
#include "givaro/giverror.h"
#endif

#include <givaro/givconfig.h>
namespace Givaro {

// ==================================================================== //

//!  Static informations of memory allocation
class GivMMInfo {
public:
	GivMMInfo();
	~GivMMInfo();
	size_t physalloc; // size in bytes of physical allocated bloc
	size_t logalloc;  // size in bytes of "logical" allocated bloc
	size_t  sizetab;    // length of next arrays
	size_t* tabbloc;  // size of all the blocs
	size_t* tablog;     // number of each logical allocated bloc
	size_t* tabphy;     // number of each physical allocated bloc
	std::ostream& print( std::ostream& so ) const;
};

//! IO
inline std::ostream& operator<<( std::ostream& o, const GivMMInfo& T)
{ return T.print(o); }

// --------------------------------------------------------
//! Data structure of a bloc.
//! Each bloc in TabFree[id] has a data field of size TabSize[id]
class BlocFreeList {
	union header {
		int index ;             // - index in free list
		BlocFreeList* nextfree; // - pointer to the next free bloc (of the same size)
        double dummy; 			// - here to force alignment on data on 8bytes boundary
	} u;
	int64_t data[1];     

	// -- Array of list of free bloc
	static BlocFreeList* TabFree[];
	static const size_t TabSize[] ;
	static const int lenTables;
	static int search_binary( size_t sz ) ;

	friend class GivMMInfo;
	friend class GivMMFreeList;
	friend class GivMMRefCount;
};


// --------------------------------------------------------
//! Implementation of a memory manager with free-lists.
//! All members are static methods.
class GivMMFreeList {
public:

	// -- Free all internal data structures
	static void Destroy();

	// -- Allocation of a new bloc.
	// These two next functions returns a pointer or 0.
	// The frequently required bloc is treated in a special version
	// in order to speed-up the allocation.
	static BlocFreeList* _allocate (const size_t sz);
	inline static void* allocate (const size_t sz)
	{
#ifdef GIVARO_DEBUG
		if (sz ==0) return 0 ;
#endif
		size_t index;
		BlocFreeList* tmp;
		if ((sz <= 32) && ((tmp=BlocFreeList::TabFree[index =sz-1]) !=0)) {
			BlocFreeList::TabFree[index] = tmp->u.nextfree;
			tmp->u.index = (int)index;
#ifdef GIVARO_STATMEM
			tablog[index] ++; logalloc += BlocFreeList::TabSize[index];
#endif
			return (void*) tmp->data;
		}
		tmp = _allocate(sz);
		return tmp->data ;
	}

	// -- Reallocation of a bloc.
	// The function returns a pointer to the possibly new
	// bloc. If the bloc must be moved then only oldsize are
	// recopied.
	static void* resize (void* p, const size_t oldsize, const size_t newsize);

	// -- Free the bloc pointed by p and allocated by the manager GivMMFreeList.
	inline static void desallocate(void* p, const size_t = 0)
	{
		if (p==0) return ;
		BlocFreeList* tmp = reinterpret_cast<BlocFreeList*>(((char*)p) -
						    (sizeof(BlocFreeList)-sizeof(int64_t)));
		int index = tmp->u.index;
#ifdef GIVARO_DEBUG
		if ((index <0) || (index >= BlocFreeList::lenTables))
			throw GivError("[GivMMFreeList::desallocate]: bad pointer.");
#endif
		tmp->u.nextfree = BlocFreeList::TabFree[index];
		BlocFreeList::TabFree[index] = tmp;
	}

	// -- Recopy size bytes pointed by src into bytes pointer by dest
	static void memcpy( void* dest, const void* src, const size_t size );

	// -- Returns some memory usage informations
	static const GivMMInfo& Usage();

	//private:
	// -- bug (on CW10): must comment the private part to initialize next field
	static GivMMInfo info;

#ifdef GIVARO_STATMEM
	friend class GivMMInfo;
	friend class GivMMRefCount;
	static size_t& physalloc;  // total amount of physical allocated bloc
	static size_t& logalloc;   // total amoun of "logical" allocated bloc
	static size_t*& tablog;      // number of each logical allocated bloc
	static size_t*& tabphy;      // number of each physical allocated bloc
#endif

	// -- Initialization module
public:
	static GivModule Module;
private:
	static void Init(int* argc, char***argv);
	static void End();
	friend class GivModule;
};


//! Memory management with reference counter on allocated data.
//! The memory manager uses the BlocFreeList data structure
//! and stores the refcounter in the field data[0]
class GivMMRefCount {
public:
	// -- Allocation of a new bloc of size at least s
	inline static void* allocate (const size_t s)
	{

#ifdef GIVARO_DEBUG
		if (s ==0) return 0 ;
#endif
		int index;
		BlocFreeList* tmp;
		size_t sz = s + sizeof(int64_t);
		if ((sz <= 32) && ((tmp=BlocFreeList::TabFree[index =int(sz-1)]) !=0)) {
			BlocFreeList::TabFree[index] = tmp->u.nextfree;
			tmp->u.index = index;
#ifdef GIVARO_STATMEM
			GivMMFreeList::tablog[index] ++; GivMMFreeList::logalloc += BlocFreeList::TabSize[index];
#endif
			tmp->data[0] = 1 ;
			return (void*) &(tmp->data[1]);
		}
		tmp = GivMMFreeList::_allocate(sz);
		tmp->data[0] = 1 ;


		return (void*) &(tmp->data[1]);
	}

	// -- Reallocation of a bloc. See description in GivMMFreeList's class.
	// Here, if ref count on p is >1 then a new bloc is allocated.
	static void* resize (void* p, const size_t oldsize, const size_t newsize);

	// -- Free the bloc allocated by the manager GivMMRefCount.
	inline static void desallocate(void* p, const size_t = 0)
	{
		if (p==0) return ;
		BlocFreeList* tmp = reinterpret_cast<BlocFreeList*>((char *) p - sizeof(BlocFreeList) ) ;
		if ( --(tmp->data[0]) ==0) {
			int index = tmp->u.index;
#ifdef GIVARO_DEBUG
			if ((index <0) || (index >= BlocFreeList::lenTables))
				GivError::throw_error(GivError("[GivMMRefCount::desallocate]: bad pointer."));
#endif
#ifdef GIVARO_JGD
			if ((index <0) || (index >= BlocFreeList::lenTables))
				cerr << "[GivMMRefCount::desallocate]: bad pointer with index " << index << ", doing nothing ..." << endl;
			else {
#endif
				tmp->u.nextfree = BlocFreeList::TabFree[index];
				BlocFreeList::TabFree[index] = tmp;
#ifdef GIVARO_JGD
			}
#endif
		}
	}

	// -- Assignement of pointer:
	inline static void* assign (void** dest, void*src)
	{
		if (src == *dest) return *dest ;
		if (*dest !=0) GivMMRefCount::desallocate( *dest );
		if (src ==0) return *dest=src;
		BlocFreeList* s = reinterpret_cast<BlocFreeList*>(((char*)src) - sizeof(BlocFreeList));
#ifdef GIVARO_DEBUG
		if ((s->u.index <0) || (s->u.index >= BlocFreeList::lenTables))
			GivError::throw_error(GivError("[GivMMRefCount::assign]: bad pointer 'src'."));
#endif
		++(s->data[0]);
		return *dest = src ;
	}

	// -- Increment, decrement and getvalue of the refcount.
	// Returns the value after increment/decrment.
	inline static int incrc(void* p)
	{
		if (p ==0) return 0;
		BlocFreeList* bp = reinterpret_cast<BlocFreeList*>(((char*)p) - sizeof(BlocFreeList));
#ifdef GIVARO_DEBUG
		if ((bp->u.index <0) || (bp->u.index >= BlocFreeList::lenTables))
			throw GivError("[GivMMRefCount::incrc]: bad pointer.");
#endif
		return int(++(bp->data[0]));
	}
	inline static int decrc(void* p)
	{
		if (p ==0) return 0;
		BlocFreeList* bp = reinterpret_cast<BlocFreeList*>(((char*)p) - sizeof(BlocFreeList));
#ifdef GIVARO_DEBUG
		if ((bp->u.index <0) || (bp->u.index >= BlocFreeList::lenTables))
			throw GivError("[GivMMRefCount::incrc]: bad pointer.");
#endif
		return int(--(bp->data[0]));
	}
	inline static int getrc(void* p)
	{
		if (p ==0) return 0;
		BlocFreeList* bp = reinterpret_cast<BlocFreeList*>(((char*)p) - sizeof(BlocFreeList));
#ifdef GIVARO_DEBUG
		if ((bp->u.index <0) || (bp->u.index >= BlocFreeList::lenTables))
			throw GivError("[GivMMRefCount::incrc]: bad pointer.");
#endif
		return int(bp->data[0]);
	}
};


//! Memory manager that allocates array of object of type T for
template<class T>
class GivaroMM {
public:
	typedef T* ptT;
	// -- Allocation of a new bloc contains s unintialized elements
	static T* allocate (const size_t s);
	// -- Desallocation of bloc
	static void desallocate ( T* bloc, const size_t sz =0 );
	// -- Initialize each element pointed by bloc with value V
	static void initone( T*p, const T& V = T());
	static void initialize(T* bloc, const size_t s, const T& V =T());
	// -- Call destructor on each elements pointed by bloc
	static void destroy(T* bloc, const size_t s);
};


//! @bug implem does not belong here
//@{
template<class T>
inline T* GivaroMM<T>::allocate (const size_t s)
{ return (T*)GivMMFreeList::allocate(s*sizeof(T)); }
template<class T>
inline void GivaroMM<T>::desallocate(T* bloc, const size_t sz)
{ GivMMFreeList::desallocate((void*)bloc,sz); }
template<class T>
inline void GivaroMM<T>::initone( T* p, const T& V)
{ new (p) T(V); }
template<class T>
inline void GivaroMM<T>::initialize(T* bloc, const size_t s, const T& V)
{ for (size_t i=0; i<s; i++) GivaroMM<T>::initone(&(bloc[i]), V); }
template<class T>
inline void GivaroMM<T>::destroy(T* bloc, const size_t s)
{ for (size_t i=0; i<s; i++) bloc[i].~T(); }
//@}

// -- specialized version: for basic C++ type and pointer on this type
#define GIVARO_MM_SPECIALIZED(TYPE) \
template<> class GivaroMM<TYPE> {\
public: \
	typedef TYPE* ptType;\
	typedef TYPE Type;\
	static ptType allocate (const size_t s);\
	static void desallocate ( ptType bloc, const size_t sz =0 );\
	static void initone( ptType p, const Type V = 0);\
	static void initialize(ptType bloc, const size_t s, const Type V =0);\
	static void destroy(GivaroMM<TYPE>::ptType bloc, const size_t s);\
};\
inline GivaroMM<TYPE>::ptType GivaroMM<TYPE>::allocate (const size_t s)\
{ return (GivaroMM<TYPE>::ptType)GivMMFreeList::allocate(s*sizeof(GivaroMM<TYPE>::Type)); }\
inline void GivaroMM<TYPE>::desallocate(GivaroMM<TYPE>::ptType bloc, const size_t sz)\
{ GivMMFreeList::desallocate((void*)bloc,sz); }\
inline void GivaroMM<TYPE>::initone( GivaroMM<TYPE>::ptType p, const GivaroMM<TYPE>::Type v) \
{ *p = v; }\
inline void GivaroMM<TYPE>::initialize(GivaroMM<TYPE>::ptType bloc, const size_t s, const GivaroMM<TYPE>::Type V)\
{ for (size_t i=0; i<s; i++) bloc[i] = V; }\
inline void GivaroMM<TYPE>::destroy(GivaroMM<TYPE>::ptType bloc, const size_t s){}

#if 0
   GIVARO_MM_SPECIALIZED(char)
   GIVARO_MM_SPECIALIZED(short)
   GIVARO_MM_SPECIALIZED(int)
   GIVARO_MM_SPECIALIZED(long)
   GIVARO_MM_SPECIALIZED(float)
   GIVARO_MM_SPECIALIZED(double)

   GIVARO_MM_SPECIALIZED(unsigned char)
   GIVARO_MM_SPECIALIZED(unsigned short)
   GIVARO_MM_SPECIALIZED(unsigned int)
   GIVARO_MM_SPECIALIZED(unsigned long)

   GIVARO_MM_SPECIALIZED(char*)
   GIVARO_MM_SPECIALIZED(short*)
   GIVARO_MM_SPECIALIZED(int*)
   GIVARO_MM_SPECIALIZED(long*)
   GIVARO_MM_SPECIALIZED(float*)
   GIVARO_MM_SPECIALIZED(double*)

   GIVARO_MM_SPECIALIZED(unsigned char*)
   GIVARO_MM_SPECIALIZED(unsigned short*)
   GIVARO_MM_SPECIALIZED(unsigned int*)
   GIVARO_MM_SPECIALIZED(unsigned long*)
#endif

} // namespace Givaro

#endif // __GIVARO_mm_H

/* -*- mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
// vim:sts=8:sw=8:ts=8:noet:sr:cino=>s,f0,{0,g0,(0,\:0,t0,+0,=s:syntax=cpp.doxygen
