2bd7290d058d233767837d3cb75504fe68ddefae
[charm.git] / src / util / pup_util.C
1 /*
2    Pack/UnPack Library for UIUC Parallel Programming Lab
3    Orion Sky Lawlor, olawlor@uiuc.edu, 4/5/2000
4
5    This library allows you to easily pack an array, structure,
6    or object into a memory buffer or disk file, and then read 
7    the object back later.  The library will also handle translating
8    between different machine representations.
9
10    This file is needed because virtual function definitions in
11    header files cause massive code bloat-- hence the PUP library
12    virtual functions are defined here.
13
14  */
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <ctype.h>
19 #include <errno.h>
20 #include <math.h>
21
22 #include "converse.h"
23 #include "pup.h"
24 #include "ckhashtable.h"
25
26 PUP::er::~er() {}
27
28 void PUP::er::operator()(able& a)
29 {a.pup(*this);}
30
31 void PUP::er::comment(const char *message)
32 { /* ignored by default */ }
33
34 const char * PUP::er::typeString() const
35 {
36   if (isSizing()) return "sizing";
37   else if (isPacking()) return "packing";
38   else if (isUnpacking()) return "unpacking";
39   return "unknown";
40 }
41
42 void PUP::er::synchronize(unsigned int m)
43 { /* ignored by default */ }
44
45 /*define CK_CHECK_PUP to get type and bounds checking during Pack and unpack.
46   This checking substantially slows down PUPing, and increases the space
47   required by packed objects. It can save hours of debugging, however.
48  */
49 #ifdef CK_CHECK_PUP
50 static int bannerDisplayed=0;
51 static void showBanner(void) {
52   bannerDisplayed=1;
53   fprintf(stderr,"CK_CHECK_PUP pup routine checking enabled\n");
54   CmiPrintf("CK_CHECK_PUP pup routine checking enabled\n");
55 }
56
57 class pupCheckRec {
58   unsigned char magic[4];//Cannot use "int" because of alignment
59   unsigned char type;
60   unsigned char length[3];
61   enum {pupMagic=0xf36c5a21,typeMask=0x75};
62   int getMagic(void) const {return (magic[3]<<24)+(magic[2]<<16)+(magic[1]<<8)+magic[0];}
63   void setMagic(int v) {for (int i=0;i<4;i++) magic[i]=(v>>(8*i));}
64   PUP::dataType getType(void) const {return (PUP::dataType)(type^typeMask);}
65   void setType(PUP::dataType v) {type=v^typeMask;}
66   int getLength(void) const {return (length[2]<<16)+(length[1]<<8)+length[0];}
67   void setLength(int v) {for (int i=0;i<3;i++) length[i]=(v>>(8*i));}
68
69   /*Compare the packed value (from us) and the unpacked value
70     (from the user).
71    */
72   void compare(const char *kind,const char *why,int packed,int unpacked) const
73   {
74     if (packed==unpacked) return;
75     //If we get here, there is an error in the user's pack/unpack routine
76     fprintf(stderr,"CK_CHECK_PUP error!\nPacked %s (%d, or %08x) does "
77         "not equal unpacked value (%d, or %08x)!\nThis means %s\n",
78         kind,packed,packed,unpacked,unpacked,why);
79     CmiPrintf("CK_CHECK_PUP error! Run with debugger for more info.\n");
80     //Invoke the debugger
81     abort();
82   }
83   public:
84   void write(PUP::dataType t,int n) {
85     if (!bannerDisplayed) showBanner();
86     setMagic(pupMagic);
87     type=t^typeMask;
88     setLength(n);
89   }
90   void check(PUP::dataType t,int n) const {
91     compare("magic number",
92         "you unpacked more than you packed, or the values were corrupted during transport",
93         getMagic(),pupMagic);
94     compare("data type",
95         "the pack and unpack paths do not match up",
96         getType(),t);
97     compare("length",
98         "you may have forgotten to pup the array length",
99         getLength(),n);
100   }
101 };
102 #endif
103
104
105 void PUP::sizer::bytes(void * /*p*/,int n,size_t itemSize,dataType /*t*/)
106 {
107 #ifdef CK_CHECK_PUP
108   nBytes+=sizeof(pupCheckRec);
109 #endif
110 #ifndef CMK_OPTIMIZE
111   if (n<0) CmiAbort("PUP::sizer> Tried to pup a negative number of items!");
112   const unsigned int maxPupBytes=1024*1024*1024; //Pup 1 GB at a time
113   if (((unsigned int)(n*itemSize))>maxPupBytes) 
114     CmiAbort("PUP::sizer> Tried to pup absurdly large number of bytes!");
115 #endif
116   nBytes+=n*itemSize;
117 }
118
119 /*Memory PUP::er's*/
120 void PUP::toMem::bytes(void *p,int n,size_t itemSize,dataType t)
121 {
122 #ifdef CK_CHECK_PUP
123   ((pupCheckRec *)buf)->write(t,n);
124   buf+=sizeof(pupCheckRec);
125 #endif
126   n*=itemSize;
127   memcpy((void *)buf,p,n); 
128   buf+=n;
129 }
130 void PUP::fromMem::bytes(void *p,int n,size_t itemSize,dataType t)
131 {
132 #ifdef CK_CHECK_PUP
133   ((pupCheckRec *)buf)->check(t,n);
134   buf+=sizeof(pupCheckRec);
135 #endif
136   n*=itemSize; 
137   memcpy(p,(const void *)buf,n); 
138   buf+=n;
139 }
140
141 void PUP::checker::bytes(void * p,int n,size_t itemSize,dataType t)
142 {
143   if(!calCheck){
144     n*=itemSize;
145     memcpy(p,(const void *)origBuf,n); 
146     if(!_skip){
147       //get the data work for certain type
148       switch(t){
149         case Tdouble:
150           {
151             double * p1;
152             double * p2;
153             p1 = (double*)p;
154             p2 = new double[n/itemSize];
155             memcpy((char *)p2,(const void *)buf,n); 
156             for(int i=0;i<n/itemSize;i++){
157               if(fabs(p1[i]-p2[i])>accuracy){
158                 if(result){
159                   printf("found incorrect double %e %e diff %e\n",p1[i],p2[i],(p1[i]-p2[i]));
160                 }        
161                 result = result && false;
162            //     fault_bytes++;
163               }
164             }
165             delete p2;
166           }     
167           break;        
168         case Tint:
169           {
170             int * p1;
171             int * p2;
172             p1 = (int *)p;
173             p2 = new int[n/itemSize];
174             memcpy((char *)p2,(const void *)buf,n); 
175             for(int i=0;i<n/itemSize;i++){
176               if(fabs(p1[i]-p2[i])>accuracy){
177                 if(result)
178                   printf("found incorrect int %d %d at %d total %d\n",p1[i],p2[i],i,n/itemSize);
179                 result = result && false;
180          //       fault_bytes++;
181               }
182             }
183             delete p2;
184           }
185           break;
186         case Tchar:
187           {
188             char * p1;
189             char * p2;
190             p1 = (char *)p;
191             p2 = new char[n/itemSize];
192             memcpy((char *)p2,(const void *)buf,n); 
193             for(int i=0;i<n/itemSize;i++){
194               if(fabs(p1[i]-p2[i])>accuracy){
195                 if(result)
196                   printf("found incorrect char %d %d at %d, total %d\n",p1[i],p2[i],i,n/itemSize);
197                 result = result && false;
198         //        fault_bytes++;
199               }
200             }
201             delete p2;
202           }
203           break;
204         default:
205           break;
206       }
207     }
208     if(reset){
209       _skip = false;
210       reset = false;
211     }
212     origBuf+=n;
213     buf+=n;
214   }else{
215     n*=itemSize;
216     memcpy((void *)buf,p,n); 
217     if(!_skip){
218       switch(t){
219         case Tint:
220           {
221           int * p1 = (int *)p;
222           for(int i=0;i<n/itemSize;i++){
223             add(p1[i]);
224           }
225           }
226           break;
227         case Tdouble:
228           {
229           double * p1 = (double *)p;
230           for(int i=0;i<n/itemSize;i++){
231             add(p1[i]);
232           }
233           }
234           break;
235         case Tchar:
236           {
237           char * p1 = (char *)p;
238           for(int i=0;i<n/itemSize;i++){
239             add(p1[i]);
240           }
241           }
242           break;
243         default:
244           break;
245       }
246     }
247     buf+=n;
248   }
249 }
250
251 // dealing with short write
252 size_t CmiFwrite(const void *ptr, size_t size, size_t nmemb, FILE *f)
253 {
254   size_t nwritten = 0;
255   const char *buf = (const char *)ptr;
256   while (nwritten < nmemb) {
257     size_t ncur = fwrite(buf+nwritten*size,size,nmemb-nwritten,f);
258     if (ncur <= 0) {
259       if  (errno == EINTR)
260         printf("Warning: CmiFwrite retrying ...\n");
261       else
262         break;
263     }
264     else
265       nwritten += ncur;
266   }
267   return nwritten;
268 }
269
270 FILE *CmiFopen(const char *path, const char *mode)
271 {
272   FILE *fp = NULL;
273   while (1) {
274     fp = fopen(path, mode);
275     if (fp == 0 && errno==EINTR) {
276       printf("Warning: CmiFopen retrying ...\n");
277       continue;
278     }
279     else
280       break;
281   }
282   return fp;
283 }
284
285 // more robust fclose that handling interrupt
286 int CmiFclose(FILE *fp)
287 {
288   int status = 0;
289   while (1) {
290     status = fflush(fp);
291     if (status != 0 && errno==EINTR) {
292       printf("Warning: CmiFclose flush retrying ...\n");
293       continue;
294     }
295     else
296       break;
297   }
298   if (status != 0) return status;
299   while (1) {
300     status = fclose(fp);
301     if (status != 0 && errno==EINTR) {
302       printf("Warning: CmiFclose retrying ...\n");
303       continue;
304     }
305     else
306       break;
307   }
308   return status;
309 }
310
311 /*Disk PUP::er's*/
312 void PUP::toDisk::bytes(void *p,int n,size_t itemSize,dataType /*t*/)
313 {/* CkPrintf("writing %d bytes\n",itemSize*n); */ CmiFwrite(p,itemSize,n,F);}
314 void PUP::fromDisk::bytes(void *p,int n,size_t itemSize,dataType /*t*/)
315 {/* CkPrintf("reading %d bytes\n",itemSize*n); */ fread(p,itemSize,n,F);}
316
317 /****************** Seek support *******************
318   For seeking:
319   Occasionally, one will need to pack and unpack items in different
320   orders (e.g., pack the user data, then the runtime support; but
321   unpack the runtime support first, then the user data).  These routines
322   support this, via the "PUP::seekBlock" class.
323
324   The abstraction is a (nestable) "seek block", which may contain
325   several "seek sections".  A typical use is:
326 //Code:
327 PUP::seekBlock s(p,2);
328 if (p.isUnpacking()) {s.seek(0); rt.pack(p); }
329 s.seek(1); ud.pack(p); 
330 if (p.isPacking()) {s.seek(0); rt.pack(p); }
331 s.endBlock();
332  */
333   PUP::seekBlock::seekBlock(PUP::er &Np,int nSections)
334 :nSec(nSections),p(Np) 
335 {
336   if (nSections<0 || nSections>maxSections)
337     CmiAbort("Invalid # of sections passed to PUP::seekBlock!");
338   p.impl_startSeek(*this);
339   if (p.isPacking()||p.isCalChecking()) 
340   //if (p.isPacking()) 
341   { //Must fabricate the section table
342     secTabOff=p.impl_tell(*this);
343     for (int i=0;i<=nSec;i++) 
344       secTab[i]=-1;
345   }
346   p(secTab,nSec+1);
347   hasEnded=CmiFalse;
348 }
349 PUP::seekBlock::~seekBlock() 
350 {
351   if (!hasEnded)
352     endBlock();
353 }
354
355 void PUP::seekBlock::seek(int toSection) 
356 {
357   if (toSection<0 || toSection>=nSec)
358     CmiAbort("Invalid section # passed to PUP::seekBlock::seek!");
359   if (p.isPacking()||p.isCalChecking()) //Build the section table
360   //if (p.isPacking()) 
361     secTab[toSection]=p.impl_tell(*this);
362   else if (p.isUnpacking()) //Extract the section table
363     p.impl_seek(*this,secTab[toSection]);
364   /*else ignore the seeking*/
365 }
366
367 void PUP::seekBlock::endBlock(void) 
368 {
369   if (p.isPacking()||p.isCalChecking()) {
370   //if (p.isPacking()) {
371     //Finish off and write out the section table
372     secTab[nSec]=p.impl_tell(*this);
373     p.impl_seek(*this,secTabOff);
374     p(secTab,nSec+1); //Write out the section table
375   }
376   //Seek to the end of the seek block
377   p.impl_seek(*this,secTab[nSec]);
378   p.impl_endSeek(*this);
379   hasEnded=CmiTrue;
380 }
381
382 /** PUP::er seek implementation routines **/
383 /*Default seek implementations are empty, which is the 
384   appropriate behavior for, e.g., sizers.
385  */
386 void PUP::er::impl_startSeek(PUP::seekBlock &s) /*Begin a seeking block*/
387 {}
388 int PUP::er::impl_tell(seekBlock &s) /*Give the current offset*/
389 {return 0;}
390 void PUP::er::impl_seek(seekBlock &s,int off) /*Seek to the given offset*/
391 {}
392 void PUP::er::impl_endSeek(seekBlock &s)/*End a seeking block*/
393 {}
394
395
396 /*Memory buffer seeking is trivial*/
397 void PUP::mem::impl_startSeek(seekBlock &s) /*Begin a seeking block*/
398 {s.data.ptr=buf;}
399 int PUP::mem::impl_tell(seekBlock &s) /*Give the current offset*/
400 {return buf-s.data.ptr;}
401 void PUP::mem::impl_seek(seekBlock &s,int off) /*Seek to the given offset*/
402 {buf=s.data.ptr+off;}
403
404 void PUP::checker::impl_startSeek(seekBlock &s) /*Begin a seeking block*/
405 {
406   if(calCheck)
407     s.data.ptr=buf;
408 }
409 int PUP::checker::impl_tell(seekBlock &s) /*Give the current offset*/
410 {
411   if(calCheck)
412     return buf-s.data.ptr;
413   else
414     return 0;
415 }
416 void PUP::checker::impl_seek(seekBlock &s,int off) /*Seek to the given offset*/
417 {
418   if(calCheck)
419     buf=s.data.ptr+off;
420 }
421
422 /*Disk buffer seeking is also simple*/
423 void PUP::disk::impl_startSeek(seekBlock &s) /*Begin a seeking block*/
424 {s.data.loff=ftell(F);}
425 int PUP::disk::impl_tell(seekBlock &s) /*Give the current offset*/
426 {return (int)(ftell(F)-s.data.loff);}
427 void PUP::disk::impl_seek(seekBlock &s,int off) /*Seek to the given offset*/
428 {fseek(F,s.data.loff+off,0);}
429
430 /*PUP::wrap_er just forwards seek calls to its wrapped PUP::er.*/
431 void PUP::wrap_er::impl_startSeek(seekBlock &s) /*Begin a seeking block*/
432 {p.impl_startSeek(s);}
433 int PUP::wrap_er::impl_tell(seekBlock &s) /*Give the current offset*/
434 {return p.impl_tell(s);}
435 void PUP::wrap_er::impl_seek(seekBlock &s,int off) /*Seek to the given offset*/
436 {p.impl_seek(s,off);}
437 void PUP::wrap_er::impl_endSeek(seekBlock &s) /*Finish a seeking block*/
438 {p.impl_endSeek(s);}
439
440
441 /**************** PUP::able support **********************
442   If a class C inherits from PUP::able, 
443   and you keep a new/delete pointer to C "C *cptr" somewhere,
444   you can call "p(cptr)" in your pup routine, and the object
445   will be saved/delete'd/new'd/restored properly with no 
446   additional effort, even if C has virtual methods or is actually
447   a subclass of C.  There is no space or time overhead for C 
448   objects other than the virtual function.
449
450   This is implemented by registering a constructor and ID
451   for each PUP::able class.  A packer can then write the ID
452   before the class; and unpacker can look up the constructor
453   from the ID.
454  */
455
456 static PUP::able::PUP_ID null_PUP_ID(0); /*ID of null object*/
457
458 PUP::able *PUP::able::clone(void) const {
459   // Make a new object to fill out
460   PUP::able *ret=get_constructor(get_PUP_ID()) ();
461
462   // Save our own state into a buffer
463   PUP::able *mthis=(PUP::able *)this; /* cast away constness */
464   int size;
465   { PUP::sizer ps; mthis->pup(ps); size=ps.size(); }
466   void *buf=malloc(size);
467   { PUP::toMem pt(buf); mthis->pup(pt); }
468
469   // Fill the new object with our values
470   { PUP::fromMem pf(buf); ret->pup(pf); }
471   free(buf);
472
473   return ret;
474 }
475
476 //Empty destructor & pup routine
477 PUP::able::~able() {}
478 void PUP::able::pup(PUP::er &p) {}
479
480 //Compute a good hash of the given string 
481 // (registration-time only-- allowed to be slow)
482 void PUP::able::PUP_ID::setName(const char *name)
483 {
484   int i,o,n=strlen(name);
485   int t[len]={0};
486   for (o=0;o<n;o++)
487     for (i=0;i<len;i++) {
488       unsigned char c=name[o];
489       int shift1=(((o+2)*(i+1)*5+4)%13);
490       int shift2=(((o+2)*(i+1)*3+2)%11)+13;
491       t[i]+=(c<<shift1)+(c<<shift2);
492     }
493   for (i=0;i<len;i++) 
494     hash[i]=(unsigned char)(t[i]%20117 + t[i]%1217 + t[i]%157);
495 }
496
497 //Registration routines-- called at global initialization time
498 class PUP_regEntry {
499   public:
500     PUP::able::PUP_ID id;
501     const char *name;
502     PUP::able::constructor_function ctor;
503     PUP_regEntry(const char *Nname,
504         const PUP::able::PUP_ID &Nid,PUP::able::constructor_function Nctor)
505       :id(Nid),name(Nname),ctor(Nctor) {}
506     PUP_regEntry(int zero) {
507       name=NULL; //For marking "not found"
508     }
509 };
510
511 typedef CkHashtableTslow<PUP::able::PUP_ID,PUP_regEntry> PUP_registry;
512
513 // FIXME: not SMP safe!    // gzheng
514 static PUP_registry *PUP_getRegistry(void) {
515   static PUP_registry *reg = NULL;
516   if (reg==NULL)
517     reg=new PUP_registry();
518   return reg;
519 }
520
521 const PUP_regEntry *PUP_getRegEntry(const PUP::able::PUP_ID &id)
522 {
523   const PUP_regEntry *cur=(const PUP_regEntry *)(
524       PUP_getRegistry()->CkHashtable::get((const void *)&id) );
525   if (cur==NULL)
526     CmiAbort("Unrecognized PUP::able::PUP_ID. is there an unregistered module?");
527   return cur;
528 }
529
530   PUP::able::PUP_ID PUP::able::register_constructor
531 (const char *className,constructor_function fn)
532 {
533   PUP::able::PUP_ID id(className);
534   PUP_getRegistry()->put(id)=PUP_regEntry(className,id,fn);
535   return id;
536 }
537
538   PUP::able::constructor_function PUP::able::get_constructor
539 (const PUP::able::PUP_ID &id)
540 {
541   return PUP_getRegEntry(id)->ctor;
542 }
543
544 //For allocatable objects: new/delete object and call pup routine
545 void PUP::er::object(able** a)
546 {
547   const PUP_regEntry *r=NULL;
548   if (isUnpacking()) 
549   { //Find the object type & create the object
550     PUP::able::PUP_ID id;//The object's id
551     id.pup(*this);
552     if (id==null_PUP_ID) {*a=NULL; return;}
553     r=PUP_getRegEntry(id);
554     //Invoke constructor (calls new)
555     *a=(r->ctor)();
556
557   } else {//Just write out the object type
558     if (*a==NULL) {
559       null_PUP_ID.pup(*this);
560       return;
561     } else {
562       const PUP::able::PUP_ID &id=(*a)->get_PUP_ID();
563       id.pup(*this);
564       r=PUP_getRegEntry(id);
565     }
566   }
567   syncComment(PUP::sync_begin_object,r->name);
568   (*a)->pup(*this);
569   syncComment(PUP::sync_end_object);
570 }
571
572 /****************** Text Pup ******************/
573
574 char *PUP::toTextUtil::beginLine(void) {
575   //Indent level tabs over:
576   for (int i=0;i<level;i++) cur[i]='\t';
577   cur[level]=0;
578   return cur+level;
579 }
580 void PUP::toTextUtil::endLine(void) {
581   cur=advance(cur);
582 }
583 void PUP::toTextUtil::beginEnv(const char *type,int n)
584 {
585   char *o=beginLine();
586   sprintf(o,"begin "); o+=strlen(o);
587   sprintf(o,type,n); o+=strlen(o);
588   sprintf(o," {\n");
589   endLine();
590   level++;
591 }
592 void PUP::toTextUtil::endEnv(const char *type)
593 {
594   level--;
595   sprintf(beginLine(),"} end %s;\n",type);
596   endLine();
597 }
598   PUP::toTextUtil::toTextUtil(unsigned int inType,char *buf)
599 :er(inType)
600 {
601   cur=buf;
602   level=0;
603 }
604
605 void PUP::toTextUtil::comment(const char *message)
606 {
607   sprintf(beginLine(),"//%s\n",message); endLine();
608 }
609
610 void PUP::toTextUtil::synchronize(unsigned int m)
611 {
612   sprintf(beginLine(),"sync=0x%08x\n",m); endLine();
613 #if 0 /* text people don't care this much about synchronization */
614   char *o=beginLine();
615   sprintf(o,"sync=");o+=strlen(o);
616   const char *consonants="bcdfgjklmprstvxz";
617   const char *vowels="aeou";
618   for (int firstBit=0;firstBit<32;firstBit+=6) {
619     sprintf(o,"%c%c%c", consonants[0xf&(m>>firstBit)],
620         vowels[0x3&(m>>(firstBit+4))], 
621         (firstBit==30)?';':'-');
622     o+=strlen(o);
623   }
624   sprintf(o,"\n"); endLine();
625 #endif
626 }
627
628   void PUP::toTextUtil::bytes(void *p,int n,size_t itemSize,dataType t) {
629     if (t==Tchar) 
630     { /*Character data is written out directly (rather than numerically)*/
631       char *o=beginLine();
632       sprintf(o,"string=");o+=strlen(o);
633       *o++='\"'; /*Leading quote*/
634       /*Copy each character, possibly escaped*/
635       const char *c=(const char *)p;
636       for (int i=0;i<n;i++) {
637         if (c[i]=='\n') {
638           sprintf(o,"\\n");o+=strlen(o);
639         } else if (iscntrl(c[i])) {
640           sprintf(o,"\\x%02X",(unsigned char)c[i]);o+=strlen(o);
641         } else if (c[i]=='\\' || c[i]=='\"') {
642           sprintf(o,"\\%c",c[i]);o+=strlen(o);
643         } else
644           *o++=c[i];
645       }
646       /*Add trailing quote and newline*/
647       sprintf(o,"\";\n");o+=strlen(o);
648       endLine();
649     } else if (t==Tbyte || t==Tuchar)
650     { /*Byte data is written out in hex (rather than decimal) */
651       beginEnv("byte %d",n);
652       const unsigned char *c=(const unsigned char *)p;
653       char *o=beginLine();
654       for (int i=0;i<n;i++) {
655         sprintf(o,"%02X ",c[i]);o+=strlen(o);
656         if (i%25==24 && (i+1!=n)) 
657         { /* This line is too long-- wrap it */
658           sprintf(o,"\n"); o+=strlen(o);
659           endLine(); o=beginLine();
660         }
661       }
662       sprintf(o,"\n");
663       endLine();
664       endEnv("byte");
665     }
666     else
667     { /*Ordinary number-- write out in decimal */
668       if (n!=1) beginEnv("array %d",n);
669       for (int i=0;i<n;i++) {
670         char *o=beginLine();
671         switch(t) {
672           case Tshort: sprintf(o,"short=%d;\n",((short *)p)[i]); break;
673           case Tushort: sprintf(o,"ushort=%u;\n",((unsigned short *)p)[i]); break;
674           case Tint: sprintf(o,"int=%d;\n",((int *)p)[i]); break;
675           case Tuint: sprintf(o,"uint=%u;\n",((unsigned int *)p)[i]); break;
676           case Tlong: sprintf(o,"long=%ld;\n",((long *)p)[i]); break;
677           case Tulong: sprintf(o,"ulong=%lu;\n",((unsigned long *)p)[i]); break;
678           case Tfloat: sprintf(o,"float=%.7g;\n",((float *)p)[i]); break;
679           case Tdouble: sprintf(o,"double=%.15g;\n",((double *)p)[i]); break;
680           case Tbool: sprintf(o,"bool=%s;\n",((CmiBool *)p)[i]?"true":"false"); break;
681 #if CMK_LONG_DOUBLE_DEFINED
682           case Tlongdouble: sprintf(o,"longdouble=%Lg;\n",((long double *)p)[i]);break;
683 #endif
684 #ifdef CMK_PUP_LONG_LONG
685           case Tlonglong: sprintf(o,"longlong=%lld;\n",((CMK_PUP_LONG_LONG *)p)[i]);break;
686           case Tulonglong: sprintf(o,"ulonglong=%llu;\n",((unsigned CMK_PUP_LONG_LONG *)p)[i]);break;
687 #endif
688           case Tpointer: sprintf(o,"pointer=%p;\n",((void **)p)[i]); break;
689           default: CmiAbort("Unrecognized pup type code!");
690         }
691         endLine();
692       }
693       if (n!=1) endEnv("array");
694     }
695   }
696 void PUP::toTextUtil::object(able** a) {
697   beginEnv("object");
698   er::object(a);
699   endEnv("object");
700 }
701
702
703 //Text sizer
704 char *PUP::sizerText::advance(char *cur) {
705   charCount+=strlen(cur);
706   return line;
707 }
708
709 PUP::sizerText::sizerText(void)
710   :toTextUtil(IS_SIZING+IS_COMMENTS,line),charCount(0) { }
711
712   //Text packer
713   char *PUP::toText::advance(char *cur) {
714     charCount+=strlen(cur);
715     return buf+charCount;
716   }
717
718 PUP::toText::toText(char *outBuf)
719   :toTextUtil(IS_PACKING+IS_COMMENTS,outBuf),buf(outBuf),charCount(0) { }
720
721   /************** To/from text FILE ****************/
722 void PUP::toTextFile::bytes(void *p,int n,size_t itemSize,dataType t)
723 {
724   for (int i=0;i<n;i++) 
725     switch(t) {
726       case Tchar: fprintf(f," '%c'",((char *)p)[i]); break;
727       case Tuchar:
728       case Tbyte: fprintf(f," %02X",((unsigned char *)p)[i]); break;
729       case Tshort: fprintf(f," %d",((short *)p)[i]); break;
730       case Tushort: fprintf(f," %u",((unsigned short *)p)[i]); break;
731       case Tint: fprintf(f," %d",((int *)p)[i]); break;
732       case Tuint: fprintf(f," %u",((unsigned int *)p)[i]); break;
733       case Tlong: fprintf(f," %ld",((long *)p)[i]); break;
734       case Tulong: fprintf(f," %lu",((unsigned long *)p)[i]); break;
735       case Tfloat: fprintf(f," %.7g",((float *)p)[i]); break;
736       case Tdouble: fprintf(f," %.15g",((double *)p)[i]); break;
737       case Tbool: fprintf(f," %s",((CmiBool *)p)[i]?"true":"false"); break;
738 #if CMK_LONG_DOUBLE_DEFINED
739       case Tlongdouble: fprintf(f," %Lg",((long double *)p)[i]);break;
740 #endif
741 #ifdef CMK_PUP_LONG_LONG
742       case Tlonglong: fprintf(f," %lld",((CMK_PUP_LONG_LONG *)p)[i]);break;
743       case Tulonglong: fprintf(f," %llu",((unsigned CMK_PUP_LONG_LONG *)p)[i]);break;
744 #endif
745       case Tpointer: fprintf(f," %p",((void **)p)[i]); break;
746       default: CmiAbort("Unrecognized pup type code!");
747     };
748   fprintf(f,"\n");
749 }
750 void PUP::toTextFile::comment(const char *message)
751 {
752   fprintf(f,"! %s\n",message);
753 }
754
755 void PUP::fromTextFile::parseError(const char *what) {
756   // find line number by counting how many returns
757   long cur = ftell(f);
758   int lineno=0;
759   rewind(f);
760   while (!feof(f)) {
761     char c;
762     fscanf(f,"%c",&c);
763     if (c=='\n') lineno++;
764     if (ftell(f) > cur) break;
765   }
766   fprintf(stderr,"Parse error during pup from text file: %s at line: %d\n",what, lineno);
767   CmiAbort("Parse error during pup from text file!\n");
768 }
769 int PUP::fromTextFile::readInt(const char *fmt) {
770   int ret=0;
771   if (1!=fscanf(f,fmt,&ret)) {
772     if (feof(f)) return 0; /* start spitting out zeros at EOF */
773     else parseError("could not match integer");
774   }
775   return ret;
776 }
777 unsigned int PUP::fromTextFile::readUint(const char *fmt) {
778   unsigned int ret=0;
779   if (1!=fscanf(f,fmt,&ret))  {
780     if (feof(f)) return 0u; /* start spitting out zeros at EOF */
781     else parseError("could not match unsigned integer");
782   }
783   return ret;  
784 }
785 CMK_TYPEDEF_INT8 PUP::fromTextFile::readLongInt(const char *fmt) {
786   CMK_TYPEDEF_INT8 ret=0;
787   if (1!=fscanf(f,fmt,&ret)) {
788     if (feof(f)) return 0u;
789     else parseError("could not match large integer");
790   }
791   return ret;
792 }
793 double PUP::fromTextFile::readDouble(void) {
794   double ret=0;
795   if (1!=fscanf(f,"%lg",&ret)) {
796     if (feof(f)) return 0.0; /* start spitting out zeros at EOF */
797     else parseError("could not match double");
798   }
799   return ret;
800 }
801 void PUP::fromTextFile::bytes(void *p,int n,size_t itemSize,dataType t)
802 {
803   for (int i=0;i<n;i++) 
804     switch(t) {
805       case Tchar: 
806         if (1!=fscanf(f," '%c'",&((char *)p)[i]))
807           parseError("Could not match character");
808         break;
809       case Tuchar:
810       case Tbyte: ((unsigned char *)p)[i]=(unsigned char)readInt("%02X"); break;
811       case Tshort:((short *)p)[i]=(short)readInt(); break;
812       case Tushort: ((unsigned short *)p)[i]=(unsigned short)readUint(); break;
813       case Tint:  ((int *)p)[i]=readInt(); break;
814       case Tuint: ((unsigned int *)p)[i]=readUint(); break;
815       case Tlong: ((long *)p)[i]=readInt(); break;
816       case Tulong:((unsigned long *)p)[i]=readUint(); break;
817       case Tfloat: ((float *)p)[i]=(float)readDouble(); break;
818       case Tdouble:((double *)p)[i]=readDouble(); break;
819 #if CMK_LONG_DOUBLE_DEFINED
820       case Tlongdouble: {
821                           long double ret=0;
822                           if (1!=fscanf(f,"%Lg",&ret)) parseError("could not match long double");
823                           ((long double *)p)[i]=ret;
824                         } break;
825 #endif
826 #ifdef CMK_PUP_LONG_LONG
827       case Tlonglong: {
828                         CMK_PUP_LONG_LONG ret=0;
829                         if (1!=fscanf(f,"%lld",&ret)) parseError("could not match long long");
830                         ((CMK_PUP_LONG_LONG *)p)[i]=ret;
831                       } break;
832       case Tulonglong: {
833                          unsigned CMK_PUP_LONG_LONG ret=0;
834                          if (1!=fscanf(f,"%llu",&ret)) parseError("could not match unsigned long long");
835                          ((unsigned CMK_PUP_LONG_LONG *)p)[i]=ret;
836                        } break;
837 #endif
838       case Tbool: {
839                     char tmp[20];
840                     if (1!=fscanf(f," %20s",tmp)) parseError("could not read boolean string");
841                     CmiBool val=CmiFalse;
842                     if (0==strcmp(tmp,"true")) val=CmiTrue;
843                     else if (0==strcmp(tmp,"false")) val=CmiFalse;
844                     else parseError("could not recognize boolean string");
845                     ((CmiBool *)p)[i]=val; 
846                   }
847                   break;
848       case Tpointer: {
849                        void *ret=0;
850                        if (1!=fscanf(f,"%p",&ret)) parseError("could not match pointer");
851                        ((void **)p)[i]=ret;
852                      } break;
853       default: CmiAbort("Unrecognized pup type code!");
854     };
855 }
856 void PUP::fromTextFile::comment(const char *message)
857 {
858   char c;
859   //Skip to the start of the message:
860   while (isspace(c=fgetc(f))) {}
861
862   if (c!='!') return; //This isn't the start of a comment
863   //Skip over the whole line containing the comment:
864   char *commentBuf=(char *)CmiTmpAlloc(1024);
865   fgets(commentBuf,1024,f);
866   CmiTmpFree(commentBuf);
867 }
868
869
870
871
872
873
874