add pup support
[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         n*=itemSize;
144         memcpy(p,(const void *)origBuf,n); 
145         if(!_skip){
146                 //get the data work for certain type
147                 switch(t){
148                         case Tdouble:
149                                 {
150                                         double * p1 = new double[n/itemSize];
151                                         double * p2 = new double[n/itemSize];
152                                         p1 = (double*)p;
153                                         memcpy((char *)p2,(const void *)buf,n); 
154                                         for(int i=0;i<n/itemSize;i++){
155                                                 if(fabs(p1[i]-p2[i])>accuracy){
156                                                         if(result)
157                                                                 printf("found incorrect double %lf %lf\n",p1[i],p2[i]);
158                                                         result = result && false;
159                                                 }
160                                         }
161                                 }       
162                                 break;  
163                         case Tint:
164                                 {
165                                         int * p1 = new int[n/itemSize];
166                                         int * p2 = new int[n/itemSize];
167                                         p1 = (int *)p;
168                                         memcpy((char *)p2,(const void *)buf,n); 
169                                         for(int i=0;i<n/itemSize;i++){
170                                                 if(fabs(p1[i]-p2[i])>accuracy){
171                                                         if(result)
172                                                                 printf("found incorrect int %d %d\n",p1[i],p2[i]);
173                                                         result = result && false;
174                                                 }
175                                                 //printf("p1 %d\n",p1[i]);
176                                                 //printf("p2 %d\n",p2[i]);
177                                         }
178                                         //printf("p %d\n",*(int *)p);
179                                 }
180                                 break;
181                         case Tchar:
182                                 {
183                                         char * p1 = new char[n/itemSize];
184                                         char * p2 = new char[n/itemSize];
185                                         p1 = (char *)p;
186                                         memcpy((char *)p2,(const void *)buf,n); 
187                                         for(int i=0;i<n/itemSize;i++){
188                                                 if(fabs(p1[i]-p2[i])>accuracy){
189                                                         if(result)
190                                                                 printf("found incorrect char %c %c\n",p1[i],p2[i]);
191                                                         result = result && false;
192                                                 }
193                                                 //printf("p1 %d\n",p1[i]);
194                                                 //printf("p2 %d\n",p2[i]);
195                                         }
196                                         //printf("p %d\n",*(int *)p);
197                                 }
198                                 break;
199                         default:
200                                 break;
201                 }
202         }
203         if(reset){
204                 _skip = false;
205                 reset = false;
206         }
207         origBuf+=n;
208         buf+=n;
209 }
210
211 // dealing with short write
212 size_t CmiFwrite(const void *ptr, size_t size, size_t nmemb, FILE *f)
213 {
214         size_t nwritten = 0;
215         const char *buf = (const char *)ptr;
216         while (nwritten < nmemb) {
217           size_t ncur = fwrite(buf+nwritten*size,size,nmemb-nwritten,f);
218           if (ncur <= 0) {
219             if  (errno == EINTR)
220               printf("Warning: CmiFwrite retrying ...\n");
221             else
222               break;
223           }
224           else
225             nwritten += ncur;
226         }
227         return nwritten;
228 }
229
230 FILE *CmiFopen(const char *path, const char *mode)
231 {
232         FILE *fp = NULL;
233         while (1) {
234           fp = fopen(path, mode);
235           if (fp == 0 && errno==EINTR) {
236             printf("Warning: CmiFopen retrying ...\n");
237             continue;
238           }
239           else
240             break;
241         }
242         return fp;
243 }
244
245 // more robust fclose that handling interrupt
246 int CmiFclose(FILE *fp)
247 {
248         int status = 0;
249         while (1) {
250           status = fflush(fp);
251           if (status != 0 && errno==EINTR) {
252             printf("Warning: CmiFclose flush retrying ...\n");
253             continue;
254           }
255           else
256             break;
257         }
258         if (status != 0) return status;
259         while (1) {
260           status = fclose(fp);
261           if (status != 0 && errno==EINTR) {
262             printf("Warning: CmiFclose retrying ...\n");
263             continue;
264           }
265           else
266             break;
267         }
268         return status;
269 }
270
271 /*Disk PUP::er's*/
272 void PUP::toDisk::bytes(void *p,int n,size_t itemSize,dataType /*t*/)
273 {/* CkPrintf("writing %d bytes\n",itemSize*n); */ CmiFwrite(p,itemSize,n,F);}
274 void PUP::fromDisk::bytes(void *p,int n,size_t itemSize,dataType /*t*/)
275 {/* CkPrintf("reading %d bytes\n",itemSize*n); */ fread(p,itemSize,n,F);}
276
277 /****************** Seek support *******************
278 For seeking:
279 Occasionally, one will need to pack and unpack items in different
280 orders (e.g., pack the user data, then the runtime support; but
281 unpack the runtime support first, then the user data).  These routines
282 support this, via the "PUP::seekBlock" class.
283
284 The abstraction is a (nestable) "seek block", which may contain
285 several "seek sections".  A typical use is:
286 //Code:
287         PUP::seekBlock s(p,2);
288         if (p.isUnpacking()) {s.seek(0); rt.pack(p); }
289         s.seek(1); ud.pack(p); 
290         if (p.isPacking()) {s.seek(0); rt.pack(p); }
291         s.endBlock();
292 */
293 PUP::seekBlock::seekBlock(PUP::er &Np,int nSections)
294         :nSec(nSections),p(Np) 
295 {
296         if (nSections<0 || nSections>maxSections)
297                 CmiAbort("Invalid # of sections passed to PUP::seekBlock!");
298         p.impl_startSeek(*this);
299         if (p.isPacking()) 
300         { //Must fabricate the section table
301                 secTabOff=p.impl_tell(*this);
302                 for (int i=0;i<=nSec;i++) secTab[i]=-1;
303         }
304         p(secTab,nSec+1);
305         hasEnded=CmiFalse;
306 }
307 PUP::seekBlock::~seekBlock() 
308 {
309         if (!hasEnded)
310                 endBlock();
311 }
312
313 void PUP::seekBlock::seek(int toSection) 
314 {
315         if (toSection<0 || toSection>=nSec)
316                 CmiAbort("Invalid section # passed to PUP::seekBlock::seek!");
317         if (p.isPacking()) //Build the section table
318                 secTab[toSection]=p.impl_tell(*this);
319         else if (p.isUnpacking()) //Extract the section table
320                 p.impl_seek(*this,secTab[toSection]);
321         /*else ignore the seeking*/
322 }
323
324 void PUP::seekBlock::endBlock(void) 
325 {
326         if (p.isPacking()) {
327                 //Finish off and write out the section table
328                 secTab[nSec]=p.impl_tell(*this);
329                 p.impl_seek(*this,secTabOff);
330                 p(secTab,nSec+1); //Write out the section table
331         }
332         //Seek to the end of the seek block
333         p.impl_seek(*this,secTab[nSec]);
334         p.impl_endSeek(*this);
335         hasEnded=CmiTrue;
336 }
337
338 /** PUP::er seek implementation routines **/
339 /*Default seek implementations are empty, which is the 
340 appropriate behavior for, e.g., sizers.
341 */
342 void PUP::er::impl_startSeek(PUP::seekBlock &s) /*Begin a seeking block*/
343 {}
344 int PUP::er::impl_tell(seekBlock &s) /*Give the current offset*/
345 {return 0;}
346 void PUP::er::impl_seek(seekBlock &s,int off) /*Seek to the given offset*/
347 {}
348 void PUP::er::impl_endSeek(seekBlock &s)/*End a seeking block*/
349 {}
350
351
352 /*Memory buffer seeking is trivial*/
353 void PUP::mem::impl_startSeek(seekBlock &s) /*Begin a seeking block*/
354   {s.data.ptr=buf;}
355 int PUP::mem::impl_tell(seekBlock &s) /*Give the current offset*/
356   {return buf-s.data.ptr;}
357 void PUP::mem::impl_seek(seekBlock &s,int off) /*Seek to the given offset*/
358   {buf=s.data.ptr+off;}
359
360 /*Disk buffer seeking is also simple*/
361 void PUP::disk::impl_startSeek(seekBlock &s) /*Begin a seeking block*/
362   {s.data.loff=ftell(F);}
363 int PUP::disk::impl_tell(seekBlock &s) /*Give the current offset*/
364   {return (int)(ftell(F)-s.data.loff);}
365 void PUP::disk::impl_seek(seekBlock &s,int off) /*Seek to the given offset*/
366   {fseek(F,s.data.loff+off,0);}
367
368 /*PUP::wrap_er just forwards seek calls to its wrapped PUP::er.*/
369 void PUP::wrap_er::impl_startSeek(seekBlock &s) /*Begin a seeking block*/
370   {p.impl_startSeek(s);}
371 int PUP::wrap_er::impl_tell(seekBlock &s) /*Give the current offset*/
372   {return p.impl_tell(s);}
373 void PUP::wrap_er::impl_seek(seekBlock &s,int off) /*Seek to the given offset*/
374   {p.impl_seek(s,off);}
375 void PUP::wrap_er::impl_endSeek(seekBlock &s) /*Finish a seeking block*/
376   {p.impl_endSeek(s);}
377   
378
379 /**************** PUP::able support **********************
380 If a class C inherits from PUP::able, 
381 and you keep a new/delete pointer to C "C *cptr" somewhere,
382 you can call "p(cptr)" in your pup routine, and the object
383 will be saved/delete'd/new'd/restored properly with no 
384 additional effort, even if C has virtual methods or is actually
385 a subclass of C.  There is no space or time overhead for C 
386 objects other than the virtual function.
387
388 This is implemented by registering a constructor and ID
389 for each PUP::able class.  A packer can then write the ID
390 before the class; and unpacker can look up the constructor
391 from the ID.
392  */
393
394 static PUP::able::PUP_ID null_PUP_ID(0); /*ID of null object*/
395
396 PUP::able *PUP::able::clone(void) const {
397         // Make a new object to fill out
398         PUP::able *ret=get_constructor(get_PUP_ID()) ();
399
400         // Save our own state into a buffer
401         PUP::able *mthis=(PUP::able *)this; /* cast away constness */
402         int size;
403         { PUP::sizer ps; mthis->pup(ps); size=ps.size(); }
404         void *buf=malloc(size);
405         { PUP::toMem pt(buf); mthis->pup(pt); }
406         
407         // Fill the new object with our values
408         { PUP::fromMem pf(buf); ret->pup(pf); }
409         free(buf);
410         
411         return ret;
412 }
413
414 //Empty destructor & pup routine
415 PUP::able::~able() {}
416 void PUP::able::pup(PUP::er &p) {}
417
418 //Compute a good hash of the given string 
419 // (registration-time only-- allowed to be slow)
420 void PUP::able::PUP_ID::setName(const char *name)
421 {
422         int i,o,n=strlen(name);
423         int t[len]={0};
424         for (o=0;o<n;o++)
425                 for (i=0;i<len;i++) {
426                         unsigned char c=name[o];
427                         int shift1=(((o+2)*(i+1)*5+4)%13);
428                         int shift2=(((o+2)*(i+1)*3+2)%11)+13;
429                         t[i]+=(c<<shift1)+(c<<shift2);
430                 }
431         for (i=0;i<len;i++) 
432                 hash[i]=(unsigned char)(t[i]%20117 + t[i]%1217 + t[i]%157);
433 }
434
435 //Registration routines-- called at global initialization time
436 class PUP_regEntry {
437 public:
438         PUP::able::PUP_ID id;
439         const char *name;
440         PUP::able::constructor_function ctor;
441         PUP_regEntry(const char *Nname,
442                 const PUP::able::PUP_ID &Nid,PUP::able::constructor_function Nctor)
443                 :id(Nid),name(Nname),ctor(Nctor) {}
444         PUP_regEntry(int zero) {
445                 name=NULL; //For marking "not found"
446         }
447 };
448
449 typedef CkHashtableTslow<PUP::able::PUP_ID,PUP_regEntry> PUP_registry;
450
451 // FIXME: not SMP safe!    // gzheng
452 static PUP_registry *PUP_getRegistry(void) {
453         static PUP_registry *reg = NULL;
454         if (reg==NULL)
455                 reg=new PUP_registry();
456         return reg;
457 }
458
459 const PUP_regEntry *PUP_getRegEntry(const PUP::able::PUP_ID &id)
460 {
461         const PUP_regEntry *cur=(const PUP_regEntry *)(
462                 PUP_getRegistry()->CkHashtable::get((const void *)&id) );
463         if (cur==NULL)
464                 CmiAbort("Unrecognized PUP::able::PUP_ID. is there an unregistered module?");
465         return cur;
466 }
467
468 PUP::able::PUP_ID PUP::able::register_constructor
469         (const char *className,constructor_function fn)
470 {
471         PUP::able::PUP_ID id(className);
472         PUP_getRegistry()->put(id)=PUP_regEntry(className,id,fn);
473         return id;
474 }
475
476 PUP::able::constructor_function PUP::able::get_constructor
477         (const PUP::able::PUP_ID &id)
478 {
479         return PUP_getRegEntry(id)->ctor;
480 }
481
482 //For allocatable objects: new/delete object and call pup routine
483 void PUP::er::object(able** a)
484 {
485         const PUP_regEntry *r=NULL;
486         if (isUnpacking()) 
487         { //Find the object type & create the object
488                 PUP::able::PUP_ID id;//The object's id
489                 id.pup(*this);
490                 if (id==null_PUP_ID) {*a=NULL; return;}
491                 r=PUP_getRegEntry(id);
492                 //Invoke constructor (calls new)
493                 *a=(r->ctor)();
494                 
495         } else {//Just write out the object type
496                 if (*a==NULL) {
497                         null_PUP_ID.pup(*this);
498                         return;
499                 } else {
500                         const PUP::able::PUP_ID &id=(*a)->get_PUP_ID();
501                         id.pup(*this);
502                         r=PUP_getRegEntry(id);
503                 }
504         }
505         syncComment(PUP::sync_begin_object,r->name);
506         (*a)->pup(*this);
507         syncComment(PUP::sync_end_object);
508 }
509
510 /****************** Text Pup ******************/
511
512 char *PUP::toTextUtil::beginLine(void) {
513   //Indent level tabs over:
514   for (int i=0;i<level;i++) cur[i]='\t';
515   cur[level]=0;
516   return cur+level;
517 }
518 void PUP::toTextUtil::endLine(void) {
519   cur=advance(cur);
520 }
521 void PUP::toTextUtil::beginEnv(const char *type,int n)
522 {
523   char *o=beginLine();
524   sprintf(o,"begin "); o+=strlen(o);
525   sprintf(o,type,n); o+=strlen(o);
526   sprintf(o," {\n");
527   endLine();
528   level++;
529 }
530 void PUP::toTextUtil::endEnv(const char *type)
531 {
532   level--;
533   sprintf(beginLine(),"} end %s;\n",type);
534   endLine();
535 }
536 PUP::toTextUtil::toTextUtil(unsigned int inType,char *buf)
537   :er(inType)
538 {
539   cur=buf;
540   level=0;
541 }
542
543 void PUP::toTextUtil::comment(const char *message)
544 {
545   sprintf(beginLine(),"//%s\n",message); endLine();
546 }
547
548 void PUP::toTextUtil::synchronize(unsigned int m)
549 {
550   sprintf(beginLine(),"sync=0x%08x\n",m); endLine();
551 #if 0 /* text people don't care this much about synchronization */
552   char *o=beginLine();
553   sprintf(o,"sync=");o+=strlen(o);
554   const char *consonants="bcdfgjklmprstvxz";
555   const char *vowels="aeou";
556   for (int firstBit=0;firstBit<32;firstBit+=6) {
557         sprintf(o,"%c%c%c", consonants[0xf&(m>>firstBit)],
558                 vowels[0x3&(m>>(firstBit+4))], 
559                 (firstBit==30)?';':'-');
560         o+=strlen(o);
561   }
562   sprintf(o,"\n"); endLine();
563 #endif
564 }
565
566 void PUP::toTextUtil::bytes(void *p,int n,size_t itemSize,dataType t) {
567   if (t==Tchar) 
568   { /*Character data is written out directly (rather than numerically)*/
569     char *o=beginLine();
570     sprintf(o,"string=");o+=strlen(o);
571     *o++='\"'; /*Leading quote*/
572     /*Copy each character, possibly escaped*/
573     const char *c=(const char *)p;
574     for (int i=0;i<n;i++) {
575       if (c[i]=='\n') {
576         sprintf(o,"\\n");o+=strlen(o);
577       } else if (iscntrl(c[i])) {
578         sprintf(o,"\\x%02X",(unsigned char)c[i]);o+=strlen(o);
579       } else if (c[i]=='\\' || c[i]=='\"') {
580         sprintf(o,"\\%c",c[i]);o+=strlen(o);
581       } else
582         *o++=c[i];
583     }
584     /*Add trailing quote and newline*/
585     sprintf(o,"\";\n");o+=strlen(o);
586     endLine();
587   } else if (t==Tbyte || t==Tuchar)
588   { /*Byte data is written out in hex (rather than decimal) */
589     beginEnv("byte %d",n);
590     const unsigned char *c=(const unsigned char *)p;
591     char *o=beginLine();
592     for (int i=0;i<n;i++) {
593       sprintf(o,"%02X ",c[i]);o+=strlen(o);
594       if (i%25==24 && (i+1!=n)) 
595       { /* This line is too long-- wrap it */
596         sprintf(o,"\n"); o+=strlen(o);
597         endLine(); o=beginLine();
598       }
599     }
600     sprintf(o,"\n");
601     endLine();
602     endEnv("byte");
603   }
604   else
605   { /*Ordinary number-- write out in decimal */
606     if (n!=1) beginEnv("array %d",n);
607     for (int i=0;i<n;i++) {
608       char *o=beginLine();
609       switch(t) {
610       case Tshort: sprintf(o,"short=%d;\n",((short *)p)[i]); break;
611       case Tushort: sprintf(o,"ushort=%u;\n",((unsigned short *)p)[i]); break;
612       case Tint: sprintf(o,"int=%d;\n",((int *)p)[i]); break;
613       case Tuint: sprintf(o,"uint=%u;\n",((unsigned int *)p)[i]); break;
614       case Tlong: sprintf(o,"long=%ld;\n",((long *)p)[i]); break;
615       case Tulong: sprintf(o,"ulong=%lu;\n",((unsigned long *)p)[i]); break;
616       case Tfloat: sprintf(o,"float=%.7g;\n",((float *)p)[i]); break;
617       case Tdouble: sprintf(o,"double=%.15g;\n",((double *)p)[i]); break;
618       case Tbool: sprintf(o,"bool=%s;\n",((CmiBool *)p)[i]?"true":"false"); break;
619 #if CMK_LONG_DOUBLE_DEFINED
620       case Tlongdouble: sprintf(o,"longdouble=%Lg;\n",((long double *)p)[i]);break;
621 #endif
622 #ifdef CMK_PUP_LONG_LONG
623       case Tlonglong: sprintf(o,"longlong=%lld;\n",((CMK_PUP_LONG_LONG *)p)[i]);break;
624       case Tulonglong: sprintf(o,"ulonglong=%llu;\n",((unsigned CMK_PUP_LONG_LONG *)p)[i]);break;
625 #endif
626       case Tpointer: sprintf(o,"pointer=%p;\n",((void **)p)[i]); break;
627       default: CmiAbort("Unrecognized pup type code!");
628       }
629       endLine();
630     }
631     if (n!=1) endEnv("array");
632   }
633 }
634 void PUP::toTextUtil::object(able** a) {
635   beginEnv("object");
636   er::object(a);
637   endEnv("object");
638 }
639
640
641 //Text sizer
642 char *PUP::sizerText::advance(char *cur) {
643   charCount+=strlen(cur);
644   return line;
645 }
646
647 PUP::sizerText::sizerText(void)
648   :toTextUtil(IS_SIZING+IS_COMMENTS,line),charCount(0) { }
649
650 //Text packer
651 char *PUP::toText::advance(char *cur) {
652   charCount+=strlen(cur);
653   return buf+charCount;
654 }
655
656 PUP::toText::toText(char *outBuf)
657   :toTextUtil(IS_PACKING+IS_COMMENTS,outBuf),buf(outBuf),charCount(0) { }
658
659 /************** To/from text FILE ****************/
660 void PUP::toTextFile::bytes(void *p,int n,size_t itemSize,dataType t)
661 {
662   for (int i=0;i<n;i++) 
663     switch(t) {
664     case Tchar: fprintf(f," '%c'",((char *)p)[i]); break;
665     case Tuchar:
666     case Tbyte: fprintf(f," %02X",((unsigned char *)p)[i]); break;
667     case Tshort: fprintf(f," %d",((short *)p)[i]); break;
668     case Tushort: fprintf(f," %u",((unsigned short *)p)[i]); break;
669     case Tint: fprintf(f," %d",((int *)p)[i]); break;
670     case Tuint: fprintf(f," %u",((unsigned int *)p)[i]); break;
671     case Tlong: fprintf(f," %ld",((long *)p)[i]); break;
672     case Tulong: fprintf(f," %lu",((unsigned long *)p)[i]); break;
673     case Tfloat: fprintf(f," %.7g",((float *)p)[i]); break;
674     case Tdouble: fprintf(f," %.15g",((double *)p)[i]); break;
675     case Tbool: fprintf(f," %s",((CmiBool *)p)[i]?"true":"false"); break;
676 #if CMK_LONG_DOUBLE_DEFINED
677     case Tlongdouble: fprintf(f," %Lg",((long double *)p)[i]);break;
678 #endif
679 #ifdef CMK_PUP_LONG_LONG
680     case Tlonglong: fprintf(f," %lld",((CMK_PUP_LONG_LONG *)p)[i]);break;
681     case Tulonglong: fprintf(f," %llu",((unsigned CMK_PUP_LONG_LONG *)p)[i]);break;
682 #endif
683     case Tpointer: fprintf(f," %p",((void **)p)[i]); break;
684     default: CmiAbort("Unrecognized pup type code!");
685     };
686   fprintf(f,"\n");
687 }
688 void PUP::toTextFile::comment(const char *message)
689 {
690   fprintf(f,"! %s\n",message);
691 }
692
693 void PUP::fromTextFile::parseError(const char *what) {
694   // find line number by counting how many returns
695   long cur = ftell(f);
696   int lineno=0;
697   rewind(f);
698   while (!feof(f)) {
699      char c;
700      fscanf(f,"%c",&c);
701      if (c=='\n') lineno++;
702      if (ftell(f) > cur) break;
703   }
704   fprintf(stderr,"Parse error during pup from text file: %s at line: %d\n",what, lineno);
705   CmiAbort("Parse error during pup from text file!\n");
706 }
707 int PUP::fromTextFile::readInt(const char *fmt) {
708   int ret=0;
709   if (1!=fscanf(f,fmt,&ret)) {
710         if (feof(f)) return 0; /* start spitting out zeros at EOF */
711         else parseError("could not match integer");
712   }
713   return ret;
714 }
715 unsigned int PUP::fromTextFile::readUint(const char *fmt) {
716   unsigned int ret=0;
717   if (1!=fscanf(f,fmt,&ret))  {
718         if (feof(f)) return 0u; /* start spitting out zeros at EOF */
719         else parseError("could not match unsigned integer");
720   }
721   return ret;  
722 }
723 CMK_TYPEDEF_INT8 PUP::fromTextFile::readLongInt(const char *fmt) {
724   CMK_TYPEDEF_INT8 ret=0;
725   if (1!=fscanf(f,fmt,&ret)) {
726     if (feof(f)) return 0u;
727     else parseError("could not match large integer");
728   }
729   return ret;
730 }
731 double PUP::fromTextFile::readDouble(void) {
732   double ret=0;
733   if (1!=fscanf(f,"%lg",&ret)) {
734         if (feof(f)) return 0.0; /* start spitting out zeros at EOF */
735         else parseError("could not match double");
736   }
737   return ret;
738 }
739 void PUP::fromTextFile::bytes(void *p,int n,size_t itemSize,dataType t)
740 {
741   for (int i=0;i<n;i++) 
742     switch(t) {
743     case Tchar: 
744       if (1!=fscanf(f," '%c'",&((char *)p)[i]))
745         parseError("Could not match character");
746       break;
747     case Tuchar:
748     case Tbyte: ((unsigned char *)p)[i]=(unsigned char)readInt("%02X"); break;
749     case Tshort:((short *)p)[i]=(short)readInt(); break;
750     case Tushort: ((unsigned short *)p)[i]=(unsigned short)readUint(); break;
751     case Tint:  ((int *)p)[i]=readInt(); break;
752     case Tuint: ((unsigned int *)p)[i]=readUint(); break;
753     case Tlong: ((long *)p)[i]=readInt(); break;
754     case Tulong:((unsigned long *)p)[i]=readUint(); break;
755     case Tfloat: ((float *)p)[i]=(float)readDouble(); break;
756     case Tdouble:((double *)p)[i]=readDouble(); break;
757 #if CMK_LONG_DOUBLE_DEFINED
758     case Tlongdouble: {
759       long double ret=0;
760       if (1!=fscanf(f,"%Lg",&ret)) parseError("could not match long double");
761       ((long double *)p)[i]=ret;
762     } break;
763 #endif
764 #ifdef CMK_PUP_LONG_LONG
765     case Tlonglong: {
766       CMK_PUP_LONG_LONG ret=0;
767       if (1!=fscanf(f,"%lld",&ret)) parseError("could not match long long");
768       ((CMK_PUP_LONG_LONG *)p)[i]=ret;
769     } break;
770     case Tulonglong: {
771       unsigned CMK_PUP_LONG_LONG ret=0;
772       if (1!=fscanf(f,"%llu",&ret)) parseError("could not match unsigned long long");
773       ((unsigned CMK_PUP_LONG_LONG *)p)[i]=ret;
774     } break;
775 #endif
776     case Tbool: {
777       char tmp[20];
778       if (1!=fscanf(f," %20s",tmp)) parseError("could not read boolean string");
779       CmiBool val=CmiFalse;
780       if (0==strcmp(tmp,"true")) val=CmiTrue;
781       else if (0==strcmp(tmp,"false")) val=CmiFalse;
782       else parseError("could not recognize boolean string");
783       ((CmiBool *)p)[i]=val; 
784     }
785       break;
786     case Tpointer: {
787       void *ret=0;
788       if (1!=fscanf(f,"%p",&ret)) parseError("could not match pointer");
789       ((void **)p)[i]=ret;
790     } break;
791     default: CmiAbort("Unrecognized pup type code!");
792     };
793 }
794 void PUP::fromTextFile::comment(const char *message)
795 {
796   char c;
797   //Skip to the start of the message:
798   while (isspace(c=fgetc(f))) {}
799   
800   if (c!='!') return; //This isn't the start of a comment
801   //Skip over the whole line containing the comment:
802   char *commentBuf=(char *)CmiTmpAlloc(1024);
803   fgets(commentBuf,1024,f);
804   CmiTmpFree(commentBuf);
805 }
806
807
808
809
810
811
812