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