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


#ifdef __mac__
#include "unixmac.h"
#include <SIOUX.h>
#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_CTX PIOPersist::ctx;
PIOPersist::ClassList * PIOPersist::classList = NULL;   // $CHECK$ Reza 26/04/99
map<string, uint_8> * PIOPersist::typeids = NULL;

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

//++
void
PIOPersist::RegisterClass(uint_8 classId, string typname, ClassCreatorFunc f)
//
//	Register a new persistant class.
//	The classId is usually a hash of the class name, and this
//	method is called only through the PPersistRegistrar template
//	class, with the PPRegister(className) macro.
//
//--
{
  if (classList->size() && (classList->find(classId) != classList->end())) {
      cerr << "RegisterClass : Error, " << hex << classId << dec 
           << " already registered." << endl;
      throw(DuplicateIdExc("PIOPersist::RegisterClass"));
    }
  
  (*classList)[classId] = f;
  (*typeids)[typname]  = classId;
}


PIOPersist::ClassCreatorFunc
PIOPersist::FindCreatorFunc(uint_8 classId)
{
  ClassList::iterator i = classList->find(classId);
  if (i == classList->end()) throw(NotFoundExc("PIOPersist::FindCreatorFunc"));
  return (*i).second;
}


//++
// 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.PutObject(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
  char ppstype;
  s.GetTypeTag(ppstype);
  if (ppstype != PInPersist::PPS_OBJECT) {
    throw FileFormatExc("PPersist::Read : not an object in flow");
  }
  
  // Check class id
  uint_8 classId;
  s.GetRawU8(classId);
  uint_8 myClassId = PIOPersist::getTypeId(typeid(*this).name());
  if (classId != myClassId) {
    throw FileFormatExc("PPersist::Read : not the same object type");
  }

  ReadSelf(s);
}

//++
void
PPersist::Write(POutPersist& s, string const& tag) const
//
//	Ecrit l'objet dans le fichier PPersist avec un tag 
//--
{
  s.WriteTag(tag);
  s.PutObject(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);
}


//++
//  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");
  }
  version = atoi(rbuf+24);

  // 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;
  strptime(rbuf,"%d/%m/%Y %T GMT",&tm);
  creationdate = mktime(&tm);

  if (scan) Scan();
}



PInPersist::~PInPersist()
{
  delete s;
}


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

  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
  s->seekg(-(sizeof(int_8)+1), ios::end);
  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);
  objList.clear(); // $CHECK$ EA 171199
  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);
  objList.clear(); // $CHECK$ EA 171199
  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);
}

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.
//--


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);
}

void
PInPersist::GetTypeTag(char& c) 
{
  GetRawByte(c);
  while (c == PPS_TAG_MARK) {
    objList.clear();
    GetRawByte(c);
  }
}


void
PInPersist::GetRawByte(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)
{
  char ppstype;
  GetTypeTag(ppstype);
  if (ppstype != PPS_SIMPLE + datasz)
    throw FileFormatExc("PInPersist::CheckTag   bad type in ppersist file");
}

void
PInPersist::CheckArrayTag(short datasz, size_t sz)
{
  char ppstype;
  GetTypeTag(ppstype);
  size_t filesz;
  if (sz <= 0x7fff) {
    if (ppstype != PPS_SIMPLE_ARRAY + datasz)
      throw FileFormatExc("PInPersist::CheckTag   bad type in ppersist file");
    int_2 ff;
    GetRawI2(ff); filesz=ff;
  } else if (sz <= 0x7fffffff) {
    if (ppstype != PPS_SIMPLE_ARRAY4 + datasz)
      throw FileFormatExc("PInPersist::CheckTag   bad type in ppersist file");
    int_4 ff;
    GetRawI4(ff); filesz=ff;
  } else {
    if (ppstype != PPS_SIMPLE_ARRAY8 + datasz)
      throw FileFormatExc("PInPersist::CheckTag   bad type in ppersist file");
    uint_8 ff;
    GetRawU8(ff); filesz=ff;
  }
  if (filesz != sz)
    throw FileFormatExc("PInPersist::CheckTag   bad array size in ppersist file");
}

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

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


void
PInPersist::GetR4s  (r_4* tab, size_t n)
{
  CheckArrayTag(4,n);
  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);
  GetRawBytes(&result, sizeof(r_8));
  if (bigEndian != IS_BIG_ENDIAN)
    bswap8(&result);
}

void
PInPersist::GetR8s  (r_8* tab, size_t n)
{
  CheckArrayTag(8,n);
  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);
  GetRawBytes(&result, sizeof(int_2));
  if (bigEndian != IS_BIG_ENDIAN)
    bswap2(&result);
}

void
PInPersist::GetI2s  (int_2* tab, size_t n)
{
  CheckArrayTag(2,n);
  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);
  GetRawBytes(&result, sizeof(uint_2));
  if (bigEndian != IS_BIG_ENDIAN)
    bswap2(&result);
}

void
PInPersist::GetU2s  (uint_2* tab, size_t n)
{
  CheckArrayTag(2,n);
  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);
  GetRawBytes(&result, sizeof(int_4));
  if (bigEndian != IS_BIG_ENDIAN)
    bswap4(&result);
}

void
PInPersist::GetI4s  (int_4* tab, size_t n)
{
  CheckArrayTag(4,n);
  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);
  GetRawBytes(&result, sizeof(uint_4));
  if (bigEndian != IS_BIG_ENDIAN)
    bswap4(&result);
}

void
PInPersist::GetU4s  (uint_4* tab, size_t n)
{
  CheckArrayTag(4,n);
  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);
  GetRawBytes(&result, sizeof(int_8));
  if (bigEndian != IS_BIG_ENDIAN)
    bswap8(&result);
}

void
PInPersist::GetI8s  (int_8* tab, size_t n)
{
  CheckArrayTag(8,n);
  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);
  GetRawBytes(&result, sizeof(uint_8));
  if (bigEndian != IS_BIG_ENDIAN)
    bswap8(&result);
}

void
PInPersist::GetU8s  (uint_8* tab, size_t n)
{
  CheckArrayTag(8,n);
  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)
{
  char ppstype;
  GetTypeTag(ppstype);
  if (ppstype != PPS_LINE) 
    throw FileFormatExc("PInPersist::GetLine   bad type in ppersist file");
  s->getline(ptr, len, '\n');
}

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

PPersist*
PInPersist::ReadObject()
{
  // Get tag
  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);
    
    // Get factory method
    ClassCreatorFunc f = FindCreatorFunc(classId);
    if (!f) {
      throw NotFoundExc("PInPersist::ReadObject   class not registered");
    }
    
    // Create object
    PPersist* object = f();
    object->ReadSelf(*this);
    assignObjectId(object);
    return object;
  } else {
    // Get object id
    int_4 id;
    GetRawI4(id);
    if (id <0 || id>=objList.size()) {
      char msg[200];
      sprintf(msg, "PInPersist::ReadObject    invalid object id for reference: %d/%d",id,objList.size());
      throw FileFormatExc(msg);
    }
    return objList[id];
  }
}

int_4 
PInPersist::assignObjectId(PPersist* x)
{
  objList.push_back(x);
  return objList.size()-1;
}

//++
// 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;

  // Output stream creation
  s = new ofstream(flnm.c_str(),ios::out | IOS_BIN);  

  // Header
  PutRawBytes("SOS-SOPHYA-PPersistFile V1               ",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 %T GMT",gmtime(&tm));
  for(int i=l; i<32; i++)  datestring[i] = ' ';
  datestring[32] = '\0'; 
  PutRawBytes(datestring, 32);
}

POutPersist::~POutPersist()
{
  if (tags.size() == 0) {
    PutRawByte(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;
      PutRawByte(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".
    }
    PutRawByte(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;
  PutRawByte(PPS_TAG_MARK);                       // This is a tag
  objList.clear(); // $CHECK$ EA 171199
}

//++
// 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::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)
{
  if (sz <= 0x7fff) {
    PutRawByte(PPS_SIMPLE_ARRAY + datasz);
    PutRawI2(sz);
  } else if (sz <= 0x7fffffff) {
    PutRawByte(PPS_SIMPLE_ARRAY4 + datasz);
    PutRawI4(sz);
  } else {
    PutRawByte(PPS_SIMPLE_ARRAY8 + datasz);
    PutRawU8(sz);
  } 
}

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



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

void
POutPersist::PutR4   (r_4 val)
{
  PutRawByte(PPS_SIMPLE + 4);
  
  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);

  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)
{
  PutRawByte(PPS_SIMPLE + 8);

  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);

  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)
{
  PutRawByte(PPS_SIMPLE + 2);

  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);
  
  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)
{
  PutRawByte(PPS_SIMPLE + 2);

  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);

  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)
{
  PutRawByte(PPS_SIMPLE + 4);

  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);

  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)
{
  PutRawByte(PPS_SIMPLE + 4);

  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);

  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)
{
  PutRawByte(PPS_SIMPLE + 8);

  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);

  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)
{
  PutRawByte(PPS_SIMPLE + 8);

  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);

  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) 
{
  PutRawByte(PPS_STRING);
  PutRawI2(str.length());
  PutRawBytes(str.c_str(), str.length());
}

void
POutPersist::PutLine(char const* ptr, size_t len)
{
  PutRawByte(PPS_LINE);

  if (len == 0)  len = strlen(ptr);
  PutRawBytes(ptr, len);
  PutRawByte('\n');
}

void
POutPersist::PutObject(PPersist const* obj) 
{
  if (serializeNullAndRepeat(obj)) return;

  PutRawByte(PPS_OBJECT);
  PutRawU8(getTypeId(typeid(*obj).name()));
  //PutRawU8(PIOPersist::Hash(typeid(*obj).name()));
  assignObjectId(obj);
  obj->WriteSelf(*this);
}

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

  int_4 id = findObjectId(x);
  if (id >= 0) {
    PutRawByte(PPS_REFERENCE);
    PutRawI4(id);
    return true;
  }
  
  return false;
}

int_4 
POutPersist::assignObjectId(PPersist const* x)
{
  int_4 id = objList.size();
  objList[x] = id;
  return id;
}

int_4 
POutPersist::findObjectId(PPersist const* x)
{
  ObjList::iterator i = objList.find(x);
  if (i == objList.end()) return -1;
  return (*i).second;
}


