#include "machdefs.h"
#include <stdio.h>
#include <sys/types.h>
#include <time.h>
#include "pexceptions.h"
#include "ppersist.h"
#include "anydataobj.h"
#include <fstream.h>
#include <iostream.h>
#include <typeinfo>


#ifdef __mac__
#include "unixmac.h"
#include <SIOUX.h>
#endif

//  strptime n'est pas defini sous Linux  - Reza Mars 2000
#if defined(OS_LINUX)
extern "C" {
char *strptime(char *buf, const char *format, const struct tm *tm);
}
#endif

#define MAXTAGLEN 255

//++
// Class	PIOPersist
// Lib		Outils++
// include	ppersist.h
//
//	Root class for persistant files. Handles the registration of
//	persistant classes
//--

//++
// Links	See
// PPersist
// PInPersist
// POutPersist
//--


MD5_CONTEXT PIOPersist::ctx;
PIOPersist::ClassList * PIOPersist::ppclassList = NULL;   // $CHECK$ Reza 26/04/99
map<string, uint_8> * PIOPersist::ppclassNameList = NULL;
map<string, uint_8> * PIOPersist::dobjclassNameList = NULL;

//++
void
PIOPersist::Initialize()
//  Initialisation globale (objets statiques) $CHECK$ Reza 26/04/99
//--
{
ppclassList = new PIOPersist::ClassList;
ppclassNameList = new map<string, uint_8>;
dobjclassNameList = new map<string, uint_8>;
}

//++
void
PIOPersist::RegisterPPHandlerClass(uint_8 classId, string ppclass_name, ClassCreatorFunc f)
//
//	Register a new persistence handler (PPersist) class.
//	The classId is usually a hash of the class name, and
//	ppclass_name is typeid(PPersistClass).name() .
//	This method is called only through the PPersistRegistrar template
//
//--
{
  if (ppclassList->size() && (ppclassList->find(classId) != ppclassList->end()) ) {
    cerr << "RegisterClass : Error, " << hex << classId << dec 
	 << " already registered." << endl;
    throw(DuplicateIdExc("PIOPersist::RegisterPPHandlerClass() Already registered (1)"));
  }
  if (ppclassNameList->size() && (ppclassNameList->find(ppclass_name) != ppclassNameList->end())) {
    cerr << "RegisterClass : Error (2) " <<  ppclass_name 
	 << " already registered." << endl;
      throw(DuplicateIdExc("PIOPersist::RegisterPPHandlerClass() Already registered(2)"));
    }
  
  (*ppclassList)[classId] = f;
  (*ppclassNameList)[ppclass_name]  = classId;
}

//++
void
PIOPersist::RegisterDataObjClass(uint_8 classId, string class_name)
//	Register a new DataObj class corresponding to a PPersist classId
//	class_typename should be typeid(DataObject).name() 
//--
{
  if (ppclassList->find(classId) == ppclassList->end() ) {
    cerr << "PIOPersist::RegisterDataObjClass() Error (1) " 
	 << hex << classId << dec << " Not Found !" << endl;
    throw( NotFoundExc("PIOPersist::RegisterDataObjClass() Not found classId ") );
  }
  if (dobjclassNameList->size() && (dobjclassNameList->find(class_name) != dobjclassNameList->end())) {
    cerr << "PIOPersist::RegisterDataObjClass() Error (2)" << class_name 
	 << " already registered." << endl;
      throw(DuplicateIdExc("PIOPersist::RegisterDataObjClass() - Already registered"));
  }

  (*dobjclassNameList)[class_name]  = classId;
}

//	class_typename should be typeid(DataObject).name(), to be 
//	used by POutPersist::PutDataObject() methods.

PIOPersist::ClassCreatorFunc
PIOPersist::FindCreatorFunc(uint_8 classId)
// Returns the PPersist class creator function for the specified classId
{
  ClassList::iterator i = ppclassList->find(classId);
  if (i == ppclassList->end()) throw(NotFoundExc("PIOPersist::FindCreatorFunc() Not found classId"));
  return (*i).second;
}

string 
PIOPersist::getPPClassName(uint_8 classId)
// Returns the PPersist class name for the specified classId
{
  map<string, uint_8>::iterator i;
  for (i= ppclassNameList->begin(); i != ppclassNameList->end(); i++)
    if ( (*i).second == classId ) return (*i).first;

  throw(NotFoundExc("PIOPersist::getPPClassName() Not found classId"));
}

uint_8
PIOPersist::getPPClassId(string const & typ_name)
// Returns the classId for the specified PPersist class type name
{
  map<string, uint_8>::iterator i = ppclassNameList->find(typ_name);
  if (i == ppclassNameList->end()) 
    throw(NotFoundExc("PIOPersist::getPPClassId() Not found className"));
  return (*i).second;
}

uint_8
PIOPersist::getPPClassId(PPersist const & ppo)
// Returns the classId for the specified PPersist class 
{
  string typ_name = typeid(ppo).name() ;
  return (getPPClassId(typ_name) );
}


string 
PIOPersist::getDataObjClassName(uint_8 classId)
// Returns the PPersist class name for the specified classId
{
  map<string, uint_8>::iterator i;
  for (i= dobjclassNameList->begin(); i != dobjclassNameList->end(); i++)
    if ( (*i).second == classId ) return (*i).first;

  throw(NotFoundExc("PIOPersist::getDataObjClassName() Not found classId"));
}

uint_8
PIOPersist::getDataObjClassId(string const & typ_name)
// Returns the classId for the specified PPersist class type name
{
  map<string, uint_8>::iterator i = dobjclassNameList->find(typ_name);
  if (i == dobjclassNameList->end()) 
    throw(NotFoundExc("PIOPersist::getDataObjClassId() Not found className"));
  return (*i).second;
}

uint_8
PIOPersist::getDataObjClassId(AnyDataObj const & o)
// Returns the classId for the specified PPersist class 
{
  string typ_name = typeid(o).name() ;
  return (getDataObjClassId(typ_name) );
}


static inline void bswap8(void* p)
{
  uint_8 tmp = *(uint_8*)p;
  *(uint_8*)p = ((tmp >> (7*8)) & 0x000000FF) | 
                ((tmp >> (5*8)) & 0x0000FF00) |
                ((tmp >> (3*8)) & 0x00FF0000) |
                ((tmp >> (1*8)) & 0xFF000000) |
                ((tmp & 0xFF000000) << (1*8))  |
                ((tmp & 0x00FF0000) << (3*8))  |
                ((tmp & 0x0000FF00) << (5*8))  |
                ((tmp & 0x000000FF) << (7*8));
}

static inline void bswap4(void* p)
{
  uint_4 tmp = *(uint_4*)p;
  *(uint_4*)p = ((tmp >> 24) & 0x000000FF) | 
                ((tmp >> 8)  & 0x0000FF00) |
                ((tmp & 0x0000FF00) << 8)  |
                ((tmp & 0x000000FF) << 24);
}

static inline void bswap2(void* p)
{
  uint_2 tmp = *(uint_2*)p;
  *(uint_2*)p = ((tmp >> 8) & 0x00FF) | 
                ((tmp & 0x00FF) << 8);
}


uint_8 PIOPersist::Hash(string const& typname) { 
        md5_init(&ctx); 
        md5_write(&ctx, (unsigned char*) typname.c_str(), typname.size()); 
        md5_final(&ctx); 
	uint_8 hash1 =   *((uint_8*) ctx.buf);
	uint_8 hash2 =   *((uint_8*) (ctx.buf+8));
#if IS_BIG_ENDIAN
	bswap8(&hash1);
	bswap8(&hash2);
#endif
	
        return (hash1+hash2);
}


//++
// Class	PPersist
// Lib		Outils++
// include	ppersist.h
//
//	Classe de base pour des objets persistants. Pour créer un objet
//	persistant :
//	- Hériter de PPersist.
//	- Définir un numéro d'identification de la classe, unique dans Peida
//	- Implémenter "ClassId()"
//	- Implémenter "WriteSelf" et "ReadSelf", qui doivent écrire toutes les variables
//	  membres que l'on souhaite écrire, et les relire dans le même ordre.
//	  Pour écrire une référence à un objet : l'objet doit être un PPersist,
//	  et il suffit d'appeler "Write" sur cet objet, et "PPersistMgr::ReadObject".
//	  Si plusieurs objets font référence au même, pour éviter de l'écrire plusieurs
//	  fois, il faut que cet objet soit un PShPersist.
//	- Pour que le fichier soit portable, écrire et lire les variables membres en utilisant
//	  les fonctions PutXX/GetXX de PInPersist/POutPersist.
//
//	Attention: les méthodes à redéfinir sont WriteSelf et ReadSelf, mais il ne faut jamais
//	les appeler directement. Seuls Write et Read peuvent être appelées par l'utilisateur.
//--

//++
// Links	See
// PInPersist
// POutPersist
// PIOPersist
//--
  
//++
void
PPersist::Write(string const& fn) const
//
//	Ecrit l'objet dans un nouveau fichier ppersist "fn".
//--
{
  POutPersist of(fn);
  Write(of);
}

//++
void
PPersist::Read(string const& fn) 
//
//	Relit l'objet dans le fichier ppersist "fn". Il faut connaître a priori
//	le type de l'objet. Pour une relecture avec création automatique du bon
//	objet, utiliser PPersistMgr::ReadObject.
//--
{
  PInPersist inf(fn);
  Read(inf);
}

//++
void
PPersist::Write(POutPersist& s) const
//
//	Ecrit l'objet dans le fichier PPersist.
//--
{
   s.PutPPObject(this);
}


//++
void
PPersist::Read(PInPersist& s)
//
//	Relit l'objet dans le fichier ppersist. Il faut connaître a priori
//	le type de l'objet. Pour une relecture avec création automatique du bon
//	objet, utiliser PInPersist::ReadObject.
//  Il faut qu'on soit un objet ecrit 
//--
{
  // We should be the exact type  
  // Check tag value
  unsigned char ppstype,ppstag;
  s.GetTypeTag(ppstype);
  if ( (ppstype != PInPersist::PPS_OBJECT) && ( ppstype != PInPersist::PPS_REFERENCE ) ) {
  }
  if (ppstype == PInPersist::PPS_OBJECT) {
    // Check class id
    uint_8 classId;
    s.GetRawU8(classId);
    uint_8 oid,oid2;
    s.GetRawU8(oid);
    if (classId != PIOPersist::getPPClassId(*this) )
      throw FileFormatExc("PPersist::Read (): not the same object type");  
    ReadSelf(s);
    // Read the ENDOBJECT 
    s.GetRawUByte(ppstag);
    if (ppstag != PInPersist::PPS_ENDOBJECT)
      throw FileFormatExc("PPersist::Read() No PPS_ENDOBJECT tag");
    s.GetRawU8(oid2);
    if (oid2 != oid)
      throw FileFormatExc("PPersist::Read() Inconsistent PPS-OId at PPS_ENDOBJECT ");
    s.KeepOId(oid, *this);   // Object should be kept with its PPS_OId  (if oid > 0)
  }
  else if ( ppstype == PInPersist::PPS_REFERENCE ) 
    s.ReadReference(*this);

  else  throw FileFormatExc("PPersist::Read() : not an object in flow");

}

//++
void
PPersist::Write(POutPersist& s, string const& tag) const
//
//	Ecrit l'objet dans le fichier PPersist avec un tag 
//--
{
  s.WriteTag(tag);
  s.PutPPObject(this);
}

//++
void
PPersist::ReadAtTag(PInPersist& s, string const& tag)
//
//	Lit l'objet à la position du tag numéro "tagid".
//--
{
  if (!s.GotoTag(tag))
    throw NotFoundExc("PPersist::ReadAtTag  tag not found");
  Read(s);
}

// Renvoie l'identificateur de l'objet  - par defaut=0

uint_8 
PPersist::getMemOId() const
{
  return(0);
}

// Ces deux methodes doivent etre redefinies si   getMemOId() renvoie non nul (>0)
// ShareDataReference() et CloneSharedReference()
void 
PPersist::ShareDataReference(PPersist & pcs)
{
  throw NotAvailableOperation("PPersist::ShareDataReference() - Unsupported operation !");
}

PPersist *
PPersist::CloneSharedReference()
{
  throw NotAvailableOperation("PPersist::CloneSharedReference() - Unsupported operation !");
}

//++
//  virtual void PPersist::ReadSelf(PInPersist&)=0
//	Méthode virtuelle pure à redéfinir. Elle est appelée par Read
//	et PPersistMgr::ReadObject. Il faut relire les variables membres,
//	dans l'ordre où elles ont été écrites par WriteSelf.
//  virtual void PPersist::WriteSelf(POutPersist&) const=0
//	Méthode virtuelle pure à redéfinir. Elle est appelée par Write.
//	Il faut écrire les variables membres,
//	dans l'ordre où elles seront relues par ReadSelf.
//--



//++
// Class	PInPersist
// Lib		Outils++
// include	ppersist.h
//
//	Fichier d'objets persistants, en lecture.
//--

//++
PInPersist::PInPersist(string const& flnm, bool scan)
//
//	Constructeur. Ouvre le fichier.
//--
{
  s = new ifstream(flnm.c_str(),ios::in | IOS_BIN); 

  // Read and check header

  char rbuf[36];
  GetRawBytes(rbuf, 32);
  if (strncmp(rbuf,"SOS-SOPHYA-PPersistFile", 23) != 0)  {
    throw FileFormatExc("PInPersist::PInPersist  bad header");
  }
  rbuf[32] = '\0';
  version = atoi(rbuf+25);
  if (version < 2) {
    cerr << "PInPersist::PInPersist(" << flnm << ") Version(=" << version 
	 << ") < 2 not supported !" << endl;
    throw FileFormatExc("PInPersist::PInPersist() - Unsupported (Old) Version");
  }
  // read endianness
  GetRawBytes(rbuf, 32);
  if (strncmp(rbuf,"BIG-ENDIAN",10) == 0)
    bigEndian = true;
  else if (strncmp(rbuf,"LITTLE-ENDIAN",13) == 0)
    bigEndian = false;
  else {
    throw FileFormatExc("PInPersist::PInPersist  bad header - endianness");
  }

  // read creation date
  GetRawBytes(rbuf, 32);
  rbuf[32] = '\0'; 
  struct tm tm;
  #ifndef __MWERKS__ 
  strptime(rbuf,"%d/%m/%Y %H:%M:%S GMT",&tm);
  #else
  sscanf(rbuf,"%2d/%2d/%4d %2d:%2d:%2d GMT",&tm.tm_mday,&tm.tm_mon,&tm.tm_year,
                                            &tm.tm_hour,&tm.tm_min,&tm.tm_sec);
  tm.tm_mon  --;
  tm.tm_year -= 1900;
  #endif
  creationdate = mktime(&tm);
  filename = flnm;  // keep the filename
  seqread = true;   // To flag non sequential reads
  if (scan) Scan();
}



PInPersist::~PInPersist()
{
  ObjList::iterator i;
  for(i=objList.begin(); i!= objList.end(); i++)  
    if ((*i).second)  delete (*i).second;
  delete s;
}


string 
PInPersist::CreationDateStr()
{
  time_t cdt = CreationDate();
  string cdate = ctime(&cdt);
  return(cdate);
}

void
PInPersist::Scan()
{
  // On cherche la liste des tags, a la fin du fichier

  unsigned char ppstype;
  size_t debut;
#ifdef STREAMPOS_IS_CLASS
  debut = s->tellg().offset();
#else
  debut = s->tellg();
#endif

  // Find tag entries at end of file
#if defined(Linux) &&  defined(__GNUG__)
  // There seems to be a bug where seekg with ios::end under Linux  with g++
  // So, we use seek with ios::beg
  s->seekg(0, ios::end);
  int_8 tagpos = s->tellg() - (sizeof(int_8)+1);
  s->seekg(tagpos, ios::beg);
#else
  s->seekg(-(sizeof(int_8)+1), ios::end);
#endif

  GetTypeTag(ppstype);
  if (ppstype != PPS_EOF)
    throw FileFormatExc("PInPersist::Scan   corrupted file, no eof entry at end of file");

  int_8 pos;
  GetRawI8(pos);
  if (pos < 0) {  // no tags
    s->seekg(debut);
    return;
  }

  char buffer[MAXTAGLEN+1];
  s->seekg(pos);
  while (true) {
    GetTypeTag(ppstype);
    if (ppstype == PPS_EOF) break;
    
    if (ppstype != PPS_TAG)
      throw FileFormatExc("PInPersist::Scan   corrupted file, bad tag entry");

    GetRawI8(pos);
    int_4 len;
    GetRawI4(len);
    if (len > MAXTAGLEN) 
      throw FileFormatExc("PInPersist::Scan   corrupted file, tag name too long");
    GetRawBytes(buffer, len);
    buffer[len] = '\0';

    tags[buffer] = pos;
  }
  s->seekg(debut);
}


int
PInPersist::NbTags()
{
  return tags.size();
}

bool 
PInPersist::GotoTag(string const& name)
{
  map<string, int_8>::iterator i = tags.find(name);
  if (i == tags.end()) 
    return false;
      //    throw NotFoundExc("PInPersist::GotoTag   tag not found");
  s->seekg((*i).second);
  seqread = false;
  //  objList.clear(); $CHECK$ EA 171199 Ne pas faire ? Reza 03/2000 ?
  return(true); 
}

bool
PInPersist::GotoTagNum(int itag)
{
  if (itag<0 || itag >= (int)tags.size()) return false;
  map<string, int_8>::iterator i = tags.begin();
  for (int j=0; j<itag; j++) i++;
  s->seekg((*i).second);
  seqread = false;
  // objList.clear();  $CHECK$ EA 171199  Ne pas faire ? Reza 03/2000 ?
  return(true);
}

string
PInPersist::GetTagName(int itag)
{
  if (itag<0 || itag >= (int)tags.size()) return "";
  map<string, int_8>::iterator i = tags.begin();
  for (int j=0; j<itag; j++) i++;
  return((*i).first);
}

string
PInPersist::GetTagClassName(int itag)
{
  // A faire
//   if (itag<0 || itag >= (int)tags.size()) return "";
//   map<string, int_8>::iterator i = tags.begin();
//   for (int j=0; j<itag; j++) i++;
//   uint_8 cid = (*i).second;
//   return(GetClassName(cid));
  return("");
}

static vector<string> * ret_tag_names = NULL;
vector<string> const &
PInPersist::GetTagNames()
{
if (ret_tag_names) delete ret_tag_names;
ret_tag_names = new vector<string> ;
map<string, int_8>::iterator i;
for(i=tags.begin(); i!=tags.end(); i++) ret_tag_names->push_back((*i).first);
return(*ret_tag_names);  
}

//++
// void PInPersist::GetByte(char& c)
// void PInPersist::GetBytes(void* ptr, size_t bytes)
// void PInPersist::GetR4   (r_4& result)
// void PInPersist::GetR4s  (r_4* tab, size_t n)
// void PInPersist::GetR8   (r_8& result)
// void PInPersist::GetR8s  (r_8* tab, size_t n)
// void PInPersist::GetI2   (int_2& result)
// void PInPersist::GetI2s  (int_2* tab, size_t n)
// void PInPersist::GetU2   (uint_2& result)
// void PInPersist::GetU2s  (uint_2* tab, size_t n)
// void PInPersist::GetI4   (int_4& result)
// void PInPersist::GetI4s  (int_4* tab, size_t n)
// void PInPersist::GetU4   (uint_4& result)
// void PInPersist::GetU4s  (uint_4* tab, size_t n)
// void PInPersist::GetI8   (int_8& result)
// void PInPersist::GetI8s  (int_8* tab, size_t n)
// void PInPersist::GetU8   (uint_8& result)
// void PInPersist::GetU8s  (uint_8* tab, size_t n)
//	Lecture de données portables depuis le fichier PPersist. Pour chaque type
//	de données, on peut lire une valeur, ou un tableau de valeurs.
// void PInPersist::GetLine(char* ptr, size_t len)
//	Lecture d'une ligne de texte depuis le fichier PPersist.
//--


void
PInPersist::GetTypeTag(unsigned char& c) 
{
  c = PPS_TAG_MARK;
  while (c == PPS_TAG_MARK) GetRawUByte(c);
  //  while (c == PPS_TAG_MARK) {    Il ne faut plus faire ca !
  //    objList.clear();             $CHECK$ Reza 03/2000
  //    GetRawByte(c);
  //  }
}


void
PInPersist::GetRawByte(char& c)
{
  GetRawBytes(&c, 1);
}

void
PInPersist::GetRawUByte(unsigned char& c)
{
  GetRawBytes(&c, 1);
}

void
PInPersist::GetRawBytes(void* ptr, size_t bytes)
{
  s->read((char*)ptr, bytes);
}

void
PInPersist::GetRawI2   (int_2& result)
{
  GetRawBytes(&result, sizeof(int_2));
  if (bigEndian != IS_BIG_ENDIAN)
    bswap2(&result);
}

void
PInPersist::GetRawI4   (int_4& result)
{
  GetRawBytes(&result, sizeof(int_4));
  if (bigEndian != IS_BIG_ENDIAN)
    bswap4(&result);
}

void
PInPersist::GetRawI8   (int_8& result)
{
  GetRawBytes(&result, sizeof(int_8));
  if (bigEndian != IS_BIG_ENDIAN)
    bswap8(&result);
}

void
PInPersist::GetRawU8   (uint_8& result)
{
  GetRawBytes(&result, sizeof(uint_8));
  if (bigEndian != IS_BIG_ENDIAN)
    bswap8(&result);
}

void
PInPersist::CheckTag(short datasz, short datatype)
// datatype = PPS_DATATYPE_CHAR or PPS_DATATYPE_FLOAT or PPS_DATATYPE_INTEGER or PPS_DATATYPE_UNSIGNED
{
  unsigned char ppstype;
  GetTypeTag(ppstype);
  if (ppstype != PPS_SIMPLE + datasz + datatype)
    throw FileFormatExc("PInPersist::CheckTag   bad type in ppersist file");
}

void
PInPersist::CheckArrayTag(short datasz, size_t sz, short datatype)
// datatype = PPS_DATATYPE_CHAR or PPS_DATATYPE_FLOAT or PPS_DATATYPE_INTEGER or PPS_DATATYPE_UNSIGNED
{
  unsigned char ppstype;
  GetTypeTag(ppstype);
  size_t filesz;
  if (sz <= 0x7fffffff) {
    if (ppstype != PPS_SIMPLE_ARRAY4 + datasz + datatype)
      throw FileFormatExc("PInPersist::CheckArrayTag   bad type in ppersist file");
    int_4 ff;
    GetRawI4(ff); filesz=ff;
  } else {
    if (ppstype != PPS_SIMPLE_ARRAY8 + datasz + datatype)
      throw FileFormatExc("PInPersist::CheckArrayTag   bad type in ppersist file");
    uint_8 ff;
    GetRawU8(ff); filesz=ff;
  }
  if (filesz != sz)
    throw FileFormatExc("PInPersist::CheckArrayTag   bad array size in ppersist file");
}

void
PInPersist::GetByte(char& c)
{
  CheckTag(1,PPS_DATATYPE_CHAR);
  GetRawBytes(&c, 1);
}


void
PInPersist::GetBytes(void* ptr, size_t bytes)
{
  CheckArrayTag(1, bytes, PPS_DATATYPE_CHAR);
  GetRawBytes(ptr, bytes);
}
void
PInPersist::GetR4   (r_4& result)
{
  CheckTag(4,PPS_DATATYPE_FLOAT);
  GetRawBytes(&result, sizeof(r_4));
  if (bigEndian != IS_BIG_ENDIAN)
    bswap4(&result);
}


void
PInPersist::GetR4s  (r_4* tab, size_t n)
{
  CheckArrayTag(4,n,PPS_DATATYPE_FLOAT);
  GetRawBytes(tab, n*sizeof(r_4));
  if (bigEndian == IS_BIG_ENDIAN) return;

  for (unsigned int i=0; i<n; i++)
    bswap4(tab+i);

  return;
}

void
PInPersist::GetR8   (r_8& result)
{
  CheckTag(8,PPS_DATATYPE_FLOAT);
  GetRawBytes(&result, sizeof(r_8));
  if (bigEndian != IS_BIG_ENDIAN)
    bswap8(&result);
}

void
PInPersist::GetR8s  (r_8* tab, size_t n)
{
  CheckArrayTag(8,n,PPS_DATATYPE_FLOAT);
  GetRawBytes(tab, n*sizeof(r_8));
  if (bigEndian == IS_BIG_ENDIAN) return;

  for (unsigned int i=0; i<n; i++)
    bswap8(tab+i);

  return;
}

void
PInPersist::GetI2   (int_2& result)
{
  CheckTag(2,PPS_DATATYPE_INTEGER);
  GetRawBytes(&result, sizeof(int_2));
  if (bigEndian != IS_BIG_ENDIAN)
    bswap2(&result);
}

void
PInPersist::GetI2s  (int_2* tab, size_t n)
{
  CheckArrayTag(2,n,PPS_DATATYPE_INTEGER);
  GetRawBytes(tab, n*sizeof(int_2));
  if (bigEndian == IS_BIG_ENDIAN) return;

  for (unsigned int i=0; i<n; i++)
    bswap2(tab+i);

  return;
}

void
PInPersist::GetU2   (uint_2& result)
{
  CheckTag(2,PPS_DATATYPE_UNSIGNED);
  GetRawBytes(&result, sizeof(uint_2));
  if (bigEndian != IS_BIG_ENDIAN)
    bswap2(&result);
}

void
PInPersist::GetU2s  (uint_2* tab, size_t n)
{
  CheckArrayTag(2,n,PPS_DATATYPE_UNSIGNED);
  GetRawBytes(tab, n*sizeof(uint_2));
  if (bigEndian == IS_BIG_ENDIAN) return;

  for (unsigned int i=0; i<n; i++)
    bswap2(tab+i);

  return;
}

void
PInPersist::GetI4   (int_4& result)
{
  CheckTag(4,PPS_DATATYPE_INTEGER);
  GetRawBytes(&result, sizeof(int_4));
  if (bigEndian != IS_BIG_ENDIAN)
    bswap4(&result);
}

void
PInPersist::GetI4s  (int_4* tab, size_t n)
{
  CheckArrayTag(4,n,PPS_DATATYPE_INTEGER);
  GetRawBytes(tab, n*sizeof(int_4));
  if (bigEndian == IS_BIG_ENDIAN) return;

  for (unsigned int i=0; i<n; i++)
    bswap4(tab+i);

  return;
}

void
PInPersist::GetU4   (uint_4& result)
{
  CheckTag(4,PPS_DATATYPE_UNSIGNED);
  GetRawBytes(&result, sizeof(uint_4));
  if (bigEndian != IS_BIG_ENDIAN)
    bswap4(&result);
}

void
PInPersist::GetU4s  (uint_4* tab, size_t n)
{
  CheckArrayTag(4,n,PPS_DATATYPE_UNSIGNED);
  GetRawBytes(tab, n*sizeof(uint_4));
  if (bigEndian == IS_BIG_ENDIAN) return;

  for (unsigned int i=0; i<n; i++)
    bswap4(tab+i);

  return;
}


void
PInPersist::GetI8   (int_8& result)
{
  CheckTag(8,PPS_DATATYPE_INTEGER);
  GetRawBytes(&result, sizeof(int_8));
  if (bigEndian != IS_BIG_ENDIAN)
    bswap8(&result);
}

void
PInPersist::GetI8s  (int_8* tab, size_t n)
{
  CheckArrayTag(8,n,PPS_DATATYPE_INTEGER);
  GetRawBytes(tab, n*sizeof(int_8));
  if (bigEndian == IS_BIG_ENDIAN) return;

  for (unsigned int i=0; i<n; i++)
    bswap8(tab+i);

  return;
}

void
PInPersist::GetU8   (uint_8& result)
{
  CheckTag(8,PPS_DATATYPE_UNSIGNED);
  GetRawBytes(&result, sizeof(uint_8));
  if (bigEndian != IS_BIG_ENDIAN)
    bswap8(&result);
}

void
PInPersist::GetU8s  (uint_8* tab, size_t n)
{
  CheckArrayTag(8,n,PPS_DATATYPE_UNSIGNED);
  GetRawBytes(tab, n*sizeof(uint_8));
  if (bigEndian == IS_BIG_ENDIAN) return;

  for (unsigned int i=0; i<n; i++)
    bswap8(tab+i);

  return;
}


void
PInPersist::GetLine(char* ptr, size_t len)
{
  string str;
  GetStr(str);
  strncpy(ptr, str.c_str(), len);
  ptr[len] = '\0';
}

void
PInPersist::GetStr(string& str)
{
  unsigned char ppstype;
  GetTypeTag(ppstype);
  if (ppstype != PPS_STRING) 
    throw FileFormatExc("PInPersist::GetStr   bad type in ppersist file");
  int_4 len;
  GetRawI4(len);
  char * buff = new char[len+1];
  GetRawBytes(buff, len);
  buff[len] = '\0';
  str = buff;
  delete[] buff;
}


PPersist*
PInPersist::ReadObject()
{
  return(GetPPObject());
}

void 
PInPersist::GetObject(AnyDataObj & o)
{
  GetPPObject(&o);
  return;
}

void 
PInPersist::GetObject(AnyDataObj & o, string tagname)
{
  GotoTag(tagname);
  GetPPObject(&o);
  return;
}

PPersist*
PInPersist::GetPPObject(AnyDataObj * po)
{
  // Get tag
  unsigned char ppstype;
  GetTypeTag(ppstype);
  if (ppstype != PPS_OBJECT && ppstype != PPS_REFERENCE && ppstype != PPS_NULL) {
    throw FileFormatExc("PInPersist::ReadObject : not an object in flow");
  }

  if (ppstype == PPS_NULL) {
    return NULL;
  } else if (ppstype == PPS_OBJECT) {
    // Get class id
    uint_8 classId;
    GetRawU8(classId);
    uint_8 oid,oid2;
    GetRawU8(oid);
    
    // Get factory method
    ClassCreatorFunc f = FindCreatorFunc(classId);
    if (!f) {
      throw NotFoundExc("PInPersist::ReadObject   class not registered");
    }
    
    // Create object
    PPersist* object = f();
    //  If a DataObject was specified , we assign it to the PPersistObject
    if (po != NULL) object->SetDataObj(*po);

    object->ReadSelf(*this);
    unsigned char ppstag;
    // Read the ENDOBJECT 
    GetRawUByte(ppstag);
    if (ppstag != PPS_ENDOBJECT)
      throw FileFormatExc("PInPersist::ReadObject No PPS_ENDOBJECT tag");
    GetRawU8(oid2);
    if (oid2 != oid)
      throw FileFormatExc("PInPersist::ReadObject Inconsistent PPS-OId at PPS_ENDOBJECT ");

    KeepOId(oid, *object);
    return object;
  } 
  else if (ppstype == PPS_REFERENCE) 
    return ReadReference();

  else throw FileFormatExc("PInPersist::ReadObject invalide Tag Type !");  
}


void
PInPersist::AnalyseTags(int lev)
{
  unsigned char ppstag=0;
  unsigned char ppst1,ppst2,ppst3;
  uint_8 cpos,fsize;
  uint_8 ui8,cid,oid;
  int_4 i4;
  int_8 i8;
  char * buff;
  string str;
  
  cout << "\n ---------------------------------------------------------- " << endl;
  cout << " PInPersist::AnalyseTags(Level= " << lev << ")" << endl; 


#ifdef STREAMPOS_IS_CLASS
  cpos = s->tellg().offset();
#else
  cpos = s->tellg();
#endif
  s->seekg(0,ios::end);
#ifdef STREAMPOS_IS_CLASS
  fsize = s->tellg().offset();
#else
  fsize = s->tellg();
#endif
  s->seekg(cpos,ios::beg);

  cout << "   Version= " << Version() << " FileSize= " << fsize 
       << " Creation Date= " << CreationDateStr() <<  endl;
  
  uint_8 totntags = 0;
  bool eofok = false;

  while ( (ppstag != PPS_EOF) &&  (cpos < fsize) ) {
#ifdef STREAMPOS_IS_CLASS
  cpos = s->tellg().offset();
#else
  cpos = s->tellg();
#endif
    GetRawUByte(ppstag);
    totntags++;

    ppst1 = ppstag&0x0f;  // bits 0123
    ppst2 = ppstag&0x30;  // bits     45
    ppst3 = ppstag&0xc0;  // bits       67
    if ((ppst2 == 0) && (ppst3 == 0) ) {
      switch (ppst1) {

      case PPS_NULL :
	if (lev > 1)  cout << "<PPS_NULL> tag at position " << hex << cpos << dec << endl;
	break;

      case PPS_STRING :
	GetRawI4(i4);
	if (lev > 1)  cout << "<PPS_STRING> tag at position " << hex << cpos << dec 
			   << " Length=" << i4 << endl;
	s->seekg(i4,ios::cur);
	break;

      case PPS_OBJECT :
	GetRawU8(cid);
	GetRawU8(oid);
	cout << "<PPS_OBJECT> tag at position " << hex << cpos << " ClassId= " << cid 
	     << "  ObjectId= " << oid << dec << endl;
	break;

      case PPS_REFERENCE :
	GetRawU8(oid);
	GetRawI8(i8);
	cout << "<PPS_REFERENCE> tag at position " << hex << cpos << "  ObjectId= " 
	     << oid << "  OrigPos=" << i8 << dec << endl;
	break;

      case PPS_TAG_MARK :
	cout << "<PPS_TAG_MARK> tag at position " << hex << cpos << dec << endl; 
	break;
     
      case PPS_ENDOBJECT :
	GetRawU8(oid);
	cout << "<PPS_ENDOBJECT> tag at position " << hex << cpos << "  ObjectId= " 
	     << oid << dec << endl;
	break;

      case PPS_TAG :
	GetRawI8(i8);
	GetRawI4(i4);
	buff = new char[i4+1];
	GetRawBytes(buff, i4); 
        buff[i4] = '\0';  str = buff;
	delete[] buff;
	cout << "<PPS_TAG> tag at position " << hex << cpos << dec 
	     << " Name= " << str << endl; 
	break;

      case PPS_EOF :
	GetRawI8(i8);	
	cout << "<PPS_EOF> tag at position " << hex << cpos  
	     << " TagPos=" << i8 << dec << endl;
	eofok = true;
	break;

      default :
	cerr << " ERROR : Unexpected tag value " << hex << ppstag 
	     << " At position" << cpos << dec << endl;
	throw FileFormatExc("PInPersist::AnalyseTags() - Unexpected tag value !");
      }
    }
    else {
      string dtype = "???? x";
      if (ppst3 == PPS_DATATYPE_CHAR) dtype = "CHAR x";
      else if (ppst3 == PPS_DATATYPE_FLOAT) dtype = "FLOAT x";
      else if (ppst3 == PPS_DATATYPE_INTEGER) dtype = "INTEGER x";
      else if (ppst3 == PPS_DATATYPE_UNSIGNED) dtype = "UNSIGNED x";
      int_4 dsize = ppst1;
      char sb[16];
      sprintf(sb, "%d", dsize);
      dtype += sb;

      switch (ppst2) {

      case PPS_SIMPLE :
	if (lev > 2)  cout << "<PPS_SIMPLE> tag at position " << hex << cpos << dec 
			   << " DataType=" << dtype << endl;
	s->seekg(dsize, ios::cur);
	break;

      case PPS_SIMPLE_ARRAY4 :
	GetRawI4(i4);
	if (lev > 0)  cout << "<PPS_SIMPLE_ARRAY4> tag at position " << hex << cpos << dec 
			   << " DataType=" << dtype << " NElts= " << i4 << endl;
	s->seekg((uint_8)dsize*(uint_8)i4, ios::cur);
	break;

      case PPS_SIMPLE_ARRAY8 :
	GetRawU8(ui8);
	if (lev > 0)  cout << "<PPS_SIMPLE_ARRAY8> tag at position " << hex << cpos << dec 
			   << " DataType=" << dtype << " NElts= " << ui8 << endl;
	s->seekg((uint_8)dsize*ui8, ios::cur);
	break;
      }
    }
  }
  if (!eofok) 
    throw FileFormatExc("PInPersist::AnalyseTags() - Not found <PPS_EOF> tag ");

  cout << " PInPersist::AnalyseTags() - End - Total Number of Tags= " << totntags << endl; 
  cout << " ---------------------------------------------------------- \n" << endl;
  return;
}

void
PInPersist::ReadReference(PPersist & ppo)
{
  PPersist * pr = ReadReference();
  ppo.ShareDataReference(*pr);
}


PPersist *
PInPersist::ReadReference()
{
  uint_8 oid;
  int_8 pos;
  GetRawU8(oid);
  GetRawI8(pos);
  //  cerr << " DBG - PInPersist::ReadReference-A "  << oid << " Pos= " << pos << endl; 
  map<uint_8, PPersist *>::iterator i = objList.find(oid);
  if (i != objList.end()) return (*i).second; 
  else  {  // We may have skeeped it !
    // Let's try to read it
    int_8 cpos;
#ifdef STREAMPOS_IS_CLASS
    cpos = s->tellg().offset();
#else
    cpos = s->tellg();
#endif
    s->seekg(pos);
    PPersist* ppo = ReadObject();
    s->seekg(cpos);
    delete ppo;
    //    cerr << " DBG - PInPersist::ReadReference-B ... " << endl;  

    map<uint_8, PPersist *>::iterator i2 = objList.find(oid);
    if (i2 == objList.end()) 
      throw FileFormatExc("PInPersist::ReadReference()   Not found PPS_OId ");
    return (*i2).second; 
  }
}


void
PInPersist::KeepOId(uint_8 oid, PPersist & ppo)
{
  if ((oid&0x1) == 0)  return; // This is not an object which can be referenced
  //  cerr << " DBG - PInPersist::KeepOId() " << oid << endl;
  if ((objList.size() > 0) && (objList.find(oid) != objList.end()) ) {
    //  Ceci ne devrait arriver que si on lit dans le desordre (avec GotoTag)
    //  et pas avec une lecture sequentielle ...   Reza 03/2000
    //  cerr << "PInPersist::KeepOId()/Warning - already present PPS_ObjectId ! " << oid << endl;
    if (seqread) throw FileFormatExc("PInPersist::KeepOId() already present PPS_ObjectId ");
    PPersist *pp = (*objList.find(oid)).second;
    ppo.ShareDataReference(*pp);
  }
  else {
    PPersist * npp = ppo.CloneSharedReference(); 
    if (npp == NULL) throw PError("PInPersist::KeepOId() NULL returned by PPersist.Clone() ! ");
    objList[oid] = npp;
  }
  return;
}

//++
// Class	POutPersist
// Lib		Outils++
// include	ppersist.h
//
//	Fichier d'objets persistants, en écriture.
//--


//++
//  POutPersist(string const& flnm, int endianness = PPersist::PPS_NATIVE)
//
//	Crée un nouveau fichier ppersist. Par défaut, il est petit=boutien
//	sur machines petit-boutiennes, et gros-boutien sur machines
//	gros-boutiennes. On peut explicitement spécifier PPersist::PPS_LITTLE_ENDIAN
//	ou PPersist::PPS_BIG_ENDIAN.
//--
POutPersist::POutPersist(string const& flnm, int endianness)
{
  if (endianness == -1)
    bigEndian = IS_BIG_ENDIAN;
  else
    bigEndian = endianness;

  // PPS (POutPersist stream) Object Id initialisation
  pps_OId = 0;
  // Output stream creation
  s = new ofstream(flnm.c_str(),ios::out | IOS_BIN);  

  // Header
  PutRawBytes("SOS-SOPHYA-PPersistFile V2               ",32);
  PutRawBytes(bigEndian 
	   ? "BIG-ENDIAN                             "
	   : "LITTLE-ENDIAN                          ",32);

// ---- GMT creation date of the file
  time_t tm = time(NULL);
  char datestring[33];
  int l=strftime(datestring,32,"%d/%m/%Y %H:%M:%S GMT",gmtime(&tm));
  for(int i=l; i<32; i++)  datestring[i] = ' ';
  datestring[32] = '\0'; 
  PutRawBytes(datestring, 32);
  filename = flnm;
}

POutPersist::~POutPersist()
{
  if (tags.size() == 0) {
    PutRawUByte(PPS_EOF);
    PutRawI8(-1);
  } else {
    int_8 tagPos;
#ifdef STREAMPOS_IS_CLASS
    tagPos = s->tellp().offset();
#else
    tagPos = s->tellp();
#endif
    for (map<string,int_8>::iterator i = tags.begin(); i != tags.end(); i++) {
      string name = (*i).first;
      int_8 pos = (*i).second;
      PutRawUByte(PPS_TAG);                       // This is a tag
      PutRawI8(pos);                             // position of previous tag
      PutRawI4(name.length());                   // length of the name
      PutRawBytes(name.c_str(), name.length());  // name, without final "0".
    }
    PutRawUByte(PPS_EOF);
    PutRawI8(tagPos);
  }

  delete s;   // Close the stream
}


void
POutPersist::WriteTag(string const& name)
{
  if (name.length() > MAXTAGLEN) 
    throw ParmError("POutPersist::WriteTag   tag name too long");

  if (tags.find(name) != tags.end())
    throw DuplicateIdExc("POutPersist::WriteTag   duplicate tag name");

  // Get current file position
  int_8 tagPos;

  #ifdef STREAMPOS_IS_CLASS
  tagPos = s->tellp().offset();
  #else
  tagPos = s->tellp();
  #endif

  tags[name] = tagPos;
  PutRawUByte(PPS_TAG_MARK);                       // This is a tag
  //  objList.clear(); // $CHECK$ EA 171199  - Ne pas faire ? Reza 03/2000
}

//++
// void POutPersist::PutByte(char& c)
// void POutPersist::PutBytes(void const* ptr, size_t bytes)
// void POutPersist::PutR4   (r_4 result)
// void POutPersist::PutR4s  (r_4 const* tab, size_t n)
// void POutPersist::PutR8   (r_8 result)
// void POutPersist::PutR8s  (r_8 const* tab, size_t n)
// void POutPersist::PutI2   (int_2 result)
// void POutPersist::PutI2s  (int_2 const* tab, size_t n)
// void POutPersist::PutU2   (uint_2 result)
// void POutPersist::PutU2s  (uint_2 const* tab, size_t n)
// void POutPersist::PutI4   (int_4 result)
// void POutPersist::PutI4s  (int_4 const* tab, size_t n)
// void POutPersist::PutU4   (uint_4 result)
// void POutPersist::PutU4s  (uint_4 const* tab, size_t n)
// void POutPersist::PutI8   (int_8 result)
// void POutPersist::PutI8s  (int_8 const* tab, size_t n)
// void POutPersist::PutU8   (uint_8 result)
// void POutPersist::PutU8s  (uint_8 const* tab, size_t n)
// void POutPersist::PutStr  (string const&)
//	Ecriture de données portables.. Pour chaque type
//	de données, on peut écrire une valeur, ou un tableau de valeurs.
// void POutPersist::PutLine(char const* ptr, size_t len)
//	Ecriture d'une ligne de texte dans le fichier PPersist.
//--




void
POutPersist::PutRawBytes(void const* ptr, size_t bytes)
{
  s->write((char const*)ptr, bytes);
}

void
POutPersist::PutRawByte(char c)
{
  PutRawBytes(&c, 1);
}

void
POutPersist::PutRawUByte(unsigned char c)
{
  PutRawBytes(&c, 1);
}

void
POutPersist::PutRawI2   (int_2 val)
{
  if (bigEndian != IS_BIG_ENDIAN)
    bswap2(&val);

  PutRawBytes(&val, sizeof(int_2));
}

void
POutPersist::PutRawI4   (int_4 val)
{
  if (bigEndian != IS_BIG_ENDIAN)
    bswap4(&val);

  PutRawBytes(&val, sizeof(int_4));
}

void
POutPersist::PutRawI8   (int_8 val)
{
  if (bigEndian != IS_BIG_ENDIAN)
    bswap8(&val);

  PutRawBytes(&val, sizeof(int_8));
}

void
POutPersist::PutRawU8   (uint_8 val)
{
  if (bigEndian != IS_BIG_ENDIAN)
    bswap8(&val);

  PutRawBytes(&val, sizeof(uint_8));
}

void
POutPersist::PutArrayTag(short datasz, size_t sz, short datatype) 
// datatype = PPS_DATATYPE_CHAR or PPS_DATATYPE_FLOAT or PPS_DATATYPE_INTEGER or PPS_DATATYPE_UNSIGNED
{
  if (sz <= 0x7fffffff) {
    PutRawUByte(PPS_SIMPLE_ARRAY4 + datasz + datatype);
    PutRawI4(sz);
  } else {
    PutRawUByte(PPS_SIMPLE_ARRAY8 + datasz + datatype);
    PutRawU8(sz);
  } 
}

void
POutPersist::PutByte(char c)
{
  PutRawByte(PPS_SIMPLE + 1 + PPS_DATATYPE_CHAR);
  PutRawBytes(&c, 1);
}



void
POutPersist::PutBytes(void const* ptr, size_t bytes)
{
  PutArrayTag(1, bytes, PPS_DATATYPE_CHAR);
  PutRawBytes(ptr, bytes);
}

void
POutPersist::PutR4   (r_4 val)
{
  PutRawUByte(PPS_SIMPLE + 4 + PPS_DATATYPE_FLOAT);
  
  if (bigEndian != IS_BIG_ENDIAN)
    bswap4(&val);

  PutRawBytes(&val, sizeof(r_4));
}

void
POutPersist::PutR4s  (r_4 const* tab, size_t n)
{
  PutArrayTag(4, n, PPS_DATATYPE_FLOAT);

  if (bigEndian == IS_BIG_ENDIAN) {
    PutRawBytes(tab, n*sizeof(r_4));
  } else {
    for (unsigned int i=0; i<n; i++) {
      r_4 val = tab[i];
      bswap4(&val);
      PutRawBytes(&val, sizeof(r_4));
    }
  }
}

void
POutPersist::PutR8   (r_8 val)
{
  PutRawUByte(PPS_SIMPLE + 8 + PPS_DATATYPE_FLOAT);

  if (bigEndian != IS_BIG_ENDIAN)
    bswap8(&val);

  PutRawBytes(&val, sizeof(r_8));
}

void
POutPersist::PutR8s  (r_8 const* tab, size_t n)
{
  PutArrayTag(8, n, PPS_DATATYPE_FLOAT);

  if (bigEndian == IS_BIG_ENDIAN) {
    PutRawBytes(tab, n*sizeof(r_8));
  } else {
    for (unsigned int i=0; i<n; i++) {
      r_8 val = tab[i];
      bswap8(&val);
      PutRawBytes(&val, sizeof(r_8));
    }
  }
}

void
POutPersist::PutI2   (int_2 val)
{
  PutRawUByte(PPS_SIMPLE + 2 + PPS_DATATYPE_INTEGER);

  if (bigEndian != IS_BIG_ENDIAN)
    bswap2(&val);

  PutRawBytes(&val, sizeof(int_2));
}

void
POutPersist::PutI2s  (int_2 const* tab, size_t n)
{
  PutArrayTag(2, n, PPS_DATATYPE_INTEGER);
  
  if (bigEndian == IS_BIG_ENDIAN) {
    PutRawBytes(tab, n*sizeof(int_2));
  } else {
    for (unsigned int i=0; i<n; i++) {
      int_2 val = tab[i];
      bswap2(&val);
      PutRawBytes(&val, sizeof(int_2));
    }
  }
}

void
POutPersist::PutU2   (uint_2 val)
{
  PutRawUByte(PPS_SIMPLE + 2 + PPS_DATATYPE_UNSIGNED);

  if (bigEndian != IS_BIG_ENDIAN)
    bswap2(&val);

  PutRawBytes(&val, sizeof(uint_2));
}

void
POutPersist::PutU2s  (uint_2 const* tab, size_t n)
{
  PutArrayTag(2, n, PPS_DATATYPE_UNSIGNED);

  if (bigEndian == IS_BIG_ENDIAN) {
    PutRawBytes(tab, n*sizeof(uint_2));
  } else {
    for (unsigned int i=0; i<n; i++) {
      uint_2 val = tab[i];
      bswap2(&val);
      PutRawBytes(&val, sizeof(uint_2));
    }
  }
}

void
POutPersist::PutI4   (int_4 val)
{
  PutRawUByte(PPS_SIMPLE + 4 + PPS_DATATYPE_INTEGER);

  if (bigEndian != IS_BIG_ENDIAN)
    bswap4(&val);

  PutRawBytes(&val, sizeof(int_4));
}

void
POutPersist::PutI4s  (int_4 const* tab, size_t n)
{
  PutArrayTag(4, n, PPS_DATATYPE_INTEGER);

  if (bigEndian == IS_BIG_ENDIAN) {
    PutRawBytes(tab, n*sizeof(int_4));
  } else {
    for (unsigned int i=0; i<n; i++) {
      int_4 val = tab[i];
      bswap4(&val);
      PutRawBytes(&val, sizeof(int_4));
    }
  }
}

void
POutPersist::PutU4   (uint_4 val)
{
  PutRawUByte(PPS_SIMPLE + 4 + PPS_DATATYPE_UNSIGNED);

  if (bigEndian != IS_BIG_ENDIAN)
    bswap4(&val);

  PutRawBytes(&val, sizeof(uint_4));
}

void
POutPersist::PutU4s  (uint_4 const* tab, size_t n)
{
  PutArrayTag(4, n, PPS_DATATYPE_UNSIGNED);

  if (bigEndian == IS_BIG_ENDIAN) {
    PutRawBytes(tab, n*sizeof(uint_4));
  } else {
    for (unsigned int i=0; i<n; i++) {
      uint_4 val = tab[i];
      bswap4(&val);
      PutRawBytes(&val, sizeof(uint_4));
    }
  }
}

void
POutPersist::PutI8   (int_8 val)
{
  PutRawUByte(PPS_SIMPLE + 8 + PPS_DATATYPE_INTEGER);

  if (bigEndian != IS_BIG_ENDIAN)
    bswap8(&val);

  PutRawBytes(&val, sizeof(int_8));
}

void
POutPersist::PutI8s  (int_8 const* tab, size_t n)
{
  PutArrayTag(8, n, PPS_DATATYPE_INTEGER);

  if (bigEndian == IS_BIG_ENDIAN) {
    PutRawBytes(tab, n*sizeof(int_8));
  } else {
    for (unsigned int i=0; i<n; i++) {
      int_8 val = tab[i];
      bswap8(&val);
      PutRawBytes(&val, sizeof(int_8));
    }
  }
}

void
POutPersist::PutU8   (uint_8 val)
{
  PutRawUByte(PPS_SIMPLE + 8 + PPS_DATATYPE_UNSIGNED);

  if (bigEndian != IS_BIG_ENDIAN)
    bswap8(&val);

  PutRawBytes(&val, sizeof(uint_8));
}

void
POutPersist::PutU8s  (uint_8 const* tab, size_t n)
{
  PutArrayTag(8, n, PPS_DATATYPE_UNSIGNED);

  if (bigEndian == IS_BIG_ENDIAN) {
    PutRawBytes(tab, n*sizeof(uint_8));
  } else {
    for (unsigned int i=0; i<n; i++) {
      uint_8 val = tab[i];
      bswap8(&val);
      PutRawBytes(&val, sizeof(uint_8));
    }
  }
}

void
POutPersist::PutStr(string const& str) 
{
  PutRawUByte(PPS_STRING);
  PutRawI4(str.length());
  PutRawBytes(str.c_str(), str.length());
}

void
POutPersist::PutLine(char const* ptr, size_t len)
{
  string str = ptr;
  PutStr(str);
}


void
POutPersist::PutObject(AnyDataObj & o)
{
  ClassCreatorFunc f = FindCreatorFunc(getDataObjClassId(o));
  if (!f) 
      throw NotFoundExc("PInPersist::PutObject()   class not registered");
  PPersist* ppo = f();
  ppo->SetDataObj(o);
  PutPPObject(ppo);
}

void
POutPersist::PutObject(AnyDataObj & o, string tagname)
{
  WriteTag(tagname);
  PutObject(o);
}


void
POutPersist::PutPPObject(PPersist const* obj) 
{
  if (serializeNullAndRepeat(obj)) return;  // NULL object or already written in stream

  //   We have to write the object 
  uint_8 oid = assignObjectId(obj);       // We assing a PPS Object Id 
  PutRawUByte(PPS_OBJECT);         // We write the Object Tag
  PutRawU8(getPPClassId(*obj));    // Writing the PPersist ClassId
  PutRawU8(oid);                   // Write the PPS Object Id 
  obj->WriteSelf(*this);
  PutRawUByte(PPS_ENDOBJECT);      // We write the End-Of-Object Tag
  PutRawU8(oid);                   // and again its PPS Object Id 
}

bool
POutPersist::serializeNullAndRepeat(PPersist const* x)
{
  if (x == NULL) {
    PutRawUByte(PPS_NULL);
    return true;
  }

  int_8 pos;
  uint_8 id = findObjectId(x, pos);
  if (id > 0) {
    PutRawUByte(PPS_REFERENCE);
    PutRawU8(id);      // Writing the corresponding object Id
    PutRawI8(pos);     // The original object position 
    return true;
  }
  
  return false;  // Object have to be written in stream ...
}

uint_8 
POutPersist::assignObjectId(PPersist const* x)
{
  pps_OId += 16;  // We keep the three first bytes for future usage
                  // Bit 1 non zero -> Object can be referenced 
  uint_8 id = pps_OId;
  uint_8 mid = x->getMemOId();
  if (mid > 0) {
    int_8 pos;
    if (findObjectId(x,pos) > 0)  
      throw PError("POutPersist::assignObjectId() Error - Already serialized object ! ");
    id += 1;  // Bit 1 non zero -> Object can be referenced 
    objreftag rt;
    rt.ppsoid = id;
#ifdef STREAMPOS_IS_CLASS
    rt.ppspos = s->tellp().offset();
#else
    rt.ppspos = s->tellp();
#endif
    objList[mid] = rt;
  }
  return id;
}

uint_8 
POutPersist::findObjectId(PPersist const* x, int_8 & pos)
{
  pos = -1;
  uint_8 mid = x->getMemOId();
  if (mid == 0)   return(0);
  ObjList::iterator i = objList.find(mid);
  if (i == objList.end()) return 0;
  pos = (*i).second.ppspos;
  return (*i).second.ppsoid;
}


