/*
Pack/UnPack Library for UIUC Parallel Programming Lab
Orion Sky Lawlor, olawlor@uiuc.edu, 4/5/2000

Definitions to support PUP::able objects.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "charm.h"
#include "pup.h"
#include "ckhashtable.h"

/**************** PUP::able support **********************
If a class C inherits from PUP::able, 
and you keep a new/delete pointer to C "C *cptr" somewhere,
you can call "p(cptr)" in your pup routine, and the object
will be saved/delete'd/new'd/restored properly with no 
additional effort, even if C has virtual methods or is actually
a subclass of C.  There is no space or time overhead for C 
objects other than the virtual function.

This is implemented by registering a constructor and ID
for each PUP::able class.  A packer can then write the ID
before the class; and unpacker can look up the constructor
from the ID.
 */

static PUP::able::PUP_ID null_PUP_ID(0); /*ID of null object*/

//For allocatable objects: new/delete object and call pup routine
void PUP::er::object(able** a)
{
	if (isUnpacking()) 
	{ //Find the object type & create the object
		PUP::able::PUP_ID id;//The object's id
		id.pup(*this);
		if (id==null_PUP_ID) {*a=NULL; return;}
		//Find the object's constructor and invoke it (calls new)
		*a=PUP::able::get_constructor(id) ();
	} else {//Just write out the object type
		if (*a==NULL) {
			null_PUP_ID.pup(*this);
			return;
		} else
			(*a)->get_PUP_ID().pup(*this);
	}
	(*a)->pup(*this);
}

//Empty destructor & pup routine
PUP::able::~able() {}
void PUP::able::pup(PUP::er &p) {}

//Compute a good hash of the given string 
// (registration-time only-- allowed to be slow)
void PUP::able::PUP_ID::setName(const char *name)
{
	int i,o,n=strlen(name);
	int t[len]={0};
	for (o=0;o<n;o++)
		for (i=0;i<len;i++) {
			unsigned char c=name[o];
			int shift1=(((o+2)*(i+1)*5+4)%13);
			int shift2=(((o+2)*(i+1)*3+2)%11)+13;
			t[i]+=(c<<shift1)+(c<<shift2);
		}
	for (i=0;i<len;i++) 
		hash[i]=(unsigned char)(t[i]%20117 + t[i]%1217 + t[i]%157);
}

//Registration routines-- called at global initialization time
class PUP_regEntry {
public:
	PUP::able::PUP_ID id;
	const char *name;
	PUP::able::constructor_function ctor;
	PUP_regEntry(const char *Nname,
		const PUP::able::PUP_ID &Nid,PUP::able::constructor_function Nctor)
		:name(Nname),id(Nid),ctor(Nctor) {}
	PUP_regEntry(int zero) {
		name=NULL; //For marking "not found"
	}
};

typedef CkHashtableTslow<PUP::able::PUP_ID,PUP_regEntry> PUP_registry;

static PUP_registry *PUP_getRegistry(void) {
	static PUP_registry *reg=NULL;
	if (reg==NULL)
		reg=new PUP_registry();
	return reg;
}

PUP::able::PUP_ID PUP::able::register_constructor
	(const char *className,constructor_function fn)
{
	PUP::able::PUP_ID id(className);
	PUP_getRegistry()->put(id)=PUP_regEntry(className,id,fn);
	return id;
}
PUP::able::constructor_function PUP::able::get_constructor
	(const PUP::able::PUP_ID &id)
{
	const PUP_regEntry &cur=PUP_getRegistry()->get(id);
	if (cur.name!=NULL)
		return cur.ctor; 
	//Error! ID not in list-- unknown class
	CmiAbort("Unrecognzied PUP::able::PUP_ID passed to get_constructor!");
	return NULL;
}






