2 Pack/UnPack Library for UIUC Parallel Programming Lab
3 Orion Sky Lawlor, olawlor@uiuc.edu, 4/5/2000
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.
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.
24 #include "ckhashtable.h"
29 int pwrite(int fd, const void *buf, size_t nbytes, __int64 offset)
31 __int64 ret = _lseek(fd, offset, SEEK_SET);
36 return(_write(fd, buf, nbytes));
38 #define NO_UNISTD_NEEDED
42 // PGI compilers define funny feature flags that lead to standard
43 // headers omitting this prototype
47 extern ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
49 #define NO_UNISTD_NEEDED
52 #if !defined(NO_UNISTD_NEEDED)
58 void PUP::er::operator()(able& a)
61 void PUP::er::comment(const char *message)
62 { /* ignored by default */ }
64 const char * PUP::er::typeString() const
66 if (isSizing()) return "sizing";
67 else if (isPacking()) return "packing";
68 else if (isUnpacking()) return "unpacking";
72 void PUP::er::synchronize(unsigned int m)
73 { /* ignored by default */ }
75 /*define CK_CHECK_PUP to get type and bounds checking during Pack and unpack.
76 This checking substantially slows down PUPing, and increases the space
77 required by packed objects. It can save hours of debugging, however.
80 static int bannerDisplayed=0;
81 static void showBanner(void) {
83 fprintf(stderr,"CK_CHECK_PUP pup routine checking enabled\n");
84 CmiPrintf("CK_CHECK_PUP pup routine checking enabled\n");
88 unsigned char magic[4];//Cannot use "int" because of alignment
90 unsigned char length[8];
91 enum {pupMagic=0xf36c5a21,typeMask=0x75};
92 int getMagic(void) const {return (magic[3]<<24)+(magic[2]<<16)+(magic[1]<<8)+magic[0];}
93 void setMagic(int v) {for (int i=0;i<4;i++) magic[i]=(v>>(8*i));}
94 PUP::dataType getType(void) const {return (PUP::dataType)(type^typeMask);}
95 void setType(PUP::dataType v) {type=v^typeMask;}
96 size_t getLength(void) const {
98 for (int i=0;i<8;i++) v += (length[i]<<(8*i));
101 void setLength(size_t v) {for (int i=0;i<8;i++) length[i]=(v>>(8*i));}
103 /*Compare the packed value (from us) and the unpacked value
106 void compare(const char *kind,const char *why,int packed,int unpacked) const
108 if (packed==unpacked) return;
109 //If we get here, there is an error in the user's pack/unpack routine
110 fprintf(stderr,"CK_CHECK_PUP error!\nPacked %s (%d, or %08x) does "
111 "not equal unpacked value (%d, or %08x)!\nThis means %s\n",
112 kind,packed,packed,unpacked,unpacked,why);
113 CmiPrintf("CK_CHECK_PUP error! Run with debugger for more info.\n");
114 //Invoke the debugger
118 void write(PUP::dataType t,size_t n) {
119 if (!bannerDisplayed) showBanner();
124 void check(PUP::dataType t,size_t n) const {
125 compare("magic number",
126 "you unpacked more than you packed, or the values were corrupted during transport",
127 getMagic(),pupMagic);
129 "the pack and unpack paths do not match up",
132 "you may have forgotten to pup the array length",
139 void PUP::sizer::bytes(void * /*p*/,size_t n,size_t itemSize,dataType /*t*/)
142 nBytes+=sizeof(pupCheckRec);
148 void PUP::toMem::bytes(void *p,size_t n,size_t itemSize,dataType t)
151 ((pupCheckRec *)buf)->write(t,n);
152 buf+=sizeof(pupCheckRec);
155 memcpy((void *)buf,p,n);
158 void PUP::fromMem::bytes(void *p,size_t n,size_t itemSize,dataType t)
161 ((pupCheckRec *)buf)->check(t,n);
162 buf+=sizeof(pupCheckRec);
165 memcpy(p,(const void *)buf,n);
171 int CmiOpen(const char *pathname, int flags, int mode)
176 fd = _open(pathname, flags, mode);
178 fd = open(pathname, flags, mode);
180 if (fd == -1 && errno==EINTR) {
181 CmiError("Warning: CmiOpen retrying on %s\n", pathname);
190 // dealing with short write
191 size_t CmiFwrite(const void *ptr, size_t size, size_t nmemb, FILE *f)
194 const char *buf = (const char *)ptr;
195 double firsttime = 0;
196 while (nwritten < nmemb) {
197 size_t ncur = fwrite(buf+nwritten*size,size,nmemb-nwritten,f);
200 CmiError("Warning: CmiFwrite retrying ...\n");
201 else if(errno == ENOMEM)
203 #ifndef CMK_BIGSIM_CHARM
204 if(firsttime == 0) firsttime = CmiWallTimer();
205 if(CmiWallTimer()-firsttime > 300)
215 #ifndef CMK_BIGSIM_CHARM
217 CmiError("Warning: CmiFwrite retried for %lf ...\n", CmiWallTimer() - firsttime);
223 CmiInt8 CmiPwrite(int fd, const char *buf, size_t bytes, size_t offset)
225 size_t origBytes = bytes;
227 CmiInt8 ret = pwrite(fd, buf, bytes, offset);
229 if (errno == EINTR) {
242 size_t CmiFread(void *ptr, size_t size, size_t nmemb, FILE *f)
245 char *buf = (char *)ptr;
246 while (nread < nmemb) {
247 size_t ncur = fread(buf + nread*size, size, nmemb-nread, f);
250 CmiError("Warning: CmiFread retrying ...\n");
260 FILE *CmiFopen(const char *path, const char *mode)
264 fp = fopen(path, mode);
265 if (fp == 0 && errno==EINTR) {
266 CmiError("Warning: CmiFopen retrying on %s\n", path);
275 // more robust fclose that handling interrupt
276 int CmiFclose(FILE *fp)
281 if (status != 0 && errno==EINTR) {
282 CmiError("Warning: CmiFclose flush retrying ...\n");
288 if (status != 0) return status;
291 if (status != 0 && errno==EINTR) {
292 CmiError("Warning: CmiFclose retrying ...\n");
304 void PUP::toDisk::bytes(void *p,size_t n,size_t itemSize,dataType /*t*/)
305 {/* CkPrintf("writing %d bytes\n",itemSize*n); */
306 if(CmiFwrite(p,itemSize,n,F) != n)
311 void PUP::fromDisk::bytes(void *p,size_t n,size_t itemSize,dataType /*t*/)
312 {/* CkPrintf("reading %d bytes\n",itemSize*n); */ CmiFread(p,itemSize,n,F);}
314 /****************** Seek support *******************
316 Occasionally, one will need to pack and unpack items in different
317 orders (e.g., pack the user data, then the runtime support; but
318 unpack the runtime support first, then the user data). These routines
319 support this, via the "PUP::seekBlock" class.
321 The abstraction is a (nestable) "seek block", which may contain
322 several "seek sections". A typical use is:
324 PUP::seekBlock s(p,2);
325 if (p.isUnpacking()) {s.seek(0); rt.pack(p); }
326 s.seek(1); ud.pack(p);
327 if (p.isPacking()) {s.seek(0); rt.pack(p); }
330 PUP::seekBlock::seekBlock(PUP::er &Np,int nSections)
331 :nSec(nSections),p(Np)
333 if (nSections<0 || nSections>maxSections)
334 CmiAbort("Invalid # of sections passed to PUP::seekBlock!");
335 p.impl_startSeek(*this);
337 { //Must fabricate the section table
338 secTabOff=p.impl_tell(*this);
339 for (int i=0;i<=nSec;i++) secTab[i]=-1;
344 PUP::seekBlock::~seekBlock()
350 void PUP::seekBlock::seek(int toSection)
352 if (toSection<0 || toSection>=nSec)
353 CmiAbort("Invalid section # passed to PUP::seekBlock::seek!");
354 if (p.isPacking()) //Build the section table
355 secTab[toSection]=p.impl_tell(*this);
356 else if (p.isUnpacking()) //Extract the section table
357 p.impl_seek(*this,secTab[toSection]);
358 /*else ignore the seeking*/
361 void PUP::seekBlock::endBlock(void)
364 //Finish off and write out the section table
365 secTab[nSec]=p.impl_tell(*this);
366 p.impl_seek(*this,secTabOff);
367 p(secTab,nSec+1); //Write out the section table
369 //Seek to the end of the seek block
370 p.impl_seek(*this,secTab[nSec]);
371 p.impl_endSeek(*this);
375 /** PUP::er seek implementation routines **/
376 /*Default seek implementations are empty, which is the
377 appropriate behavior for, e.g., sizers.
379 void PUP::er::impl_startSeek(PUP::seekBlock &s) /*Begin a seeking block*/
381 size_t PUP::er::impl_tell(seekBlock &s) /*Give the current offset*/
383 void PUP::er::impl_seek(seekBlock &s,size_t off) /*Seek to the given offset*/
385 void PUP::er::impl_endSeek(seekBlock &s)/*End a seeking block*/
389 /*Memory buffer seeking is trivial*/
390 void PUP::mem::impl_startSeek(seekBlock &s) /*Begin a seeking block*/
392 size_t PUP::mem::impl_tell(seekBlock &s) /*Give the current offset*/
393 {return buf-s.data.ptr;}
394 void PUP::mem::impl_seek(seekBlock &s,size_t off) /*Seek to the given offset*/
395 {buf=s.data.ptr+off;}
397 /*Disk buffer seeking is also simple*/
398 void PUP::disk::impl_startSeek(seekBlock &s) /*Begin a seeking block*/
399 {s.data.loff=ftell(F);}
400 size_t PUP::disk::impl_tell(seekBlock &s) /*Give the current offset*/
401 {return (int)(ftell(F)-s.data.loff);}
402 void PUP::disk::impl_seek(seekBlock &s,size_t off) /*Seek to the given offset*/
403 {fseek(F,s.data.loff+off,0);}
405 /*PUP::wrap_er just forwards seek calls to its wrapped PUP::er.*/
406 void PUP::wrap_er::impl_startSeek(seekBlock &s) /*Begin a seeking block*/
407 {p.impl_startSeek(s);}
408 size_t PUP::wrap_er::impl_tell(seekBlock &s) /*Give the current offset*/
409 {return p.impl_tell(s);}
410 void PUP::wrap_er::impl_seek(seekBlock &s,size_t off) /*Seek to the given offset*/
411 {p.impl_seek(s,off);}
412 void PUP::wrap_er::impl_endSeek(seekBlock &s) /*Finish a seeking block*/
416 /**************** PUP::able support **********************
417 If a class C inherits from PUP::able,
418 and you keep a new/delete pointer to C "C *cptr" somewhere,
419 you can call "p(cptr)" in your pup routine, and the object
420 will be saved/delete'd/new'd/restored properly with no
421 additional effort, even if C has virtual methods or is actually
422 a subclass of C. There is no space or time overhead for C
423 objects other than the virtual function.
425 This is implemented by registering a constructor and ID
426 for each PUP::able class. A packer can then write the ID
427 before the class; and unpacker can look up the constructor
431 static PUP::able::PUP_ID null_PUP_ID(0); /*ID of null object*/
433 PUP::able *PUP::able::clone(void) const {
434 // Make a new object to fill out
435 PUP::able *ret=get_constructor(get_PUP_ID()) ();
437 // Save our own state into a buffer
438 PUP::able *mthis=(PUP::able *)this; /* cast away constness */
440 { PUP::sizer ps; mthis->pup(ps); size=ps.size(); }
441 void *buf=malloc(size);
442 { PUP::toMem pt(buf); mthis->pup(pt); }
444 // Fill the new object with our values
445 { PUP::fromMem pf(buf); ret->pup(pf); }
451 //Empty destructor & pup routine
452 PUP::able::~able() {}
453 void PUP::able::pup(PUP::er &p) {}
455 //Compute a good hash of the given string
456 // (registration-time only-- allowed to be slow)
457 void PUP::able::PUP_ID::setName(const char *name)
459 int i,o,n=strlen(name);
460 unsigned int t[len]={0};
462 for (i=0;i<len;i++) {
463 unsigned char c=name[o];
464 unsigned int shift1=(((o+2)*(i+1)*5+4)%13);
465 unsigned int shift2=(((o+2)*(i+1)*3+2)%11)+13;
466 t[i]+=(c<<shift1)+(c<<shift2);
469 hash[i]=(unsigned char)(t[i]%20117 + t[i]%1217 + t[i]%157);
472 //Registration routines-- called at global initialization time
475 PUP::able::PUP_ID id;
477 PUP::able::constructor_function ctor;
478 PUP_regEntry(const char *Nname,
479 const PUP::able::PUP_ID &Nid,PUP::able::constructor_function Nctor)
480 :id(Nid),name(Nname),ctor(Nctor) {}
481 PUP_regEntry(int zero) {
482 name=NULL; //For marking "not found"
486 typedef CkHashtableTslow<PUP::able::PUP_ID,PUP_regEntry> PUP_registry;
488 // FIXME: not SMP safe! // gzheng
489 static PUP_registry *PUP_getRegistry(void) {
490 static PUP_registry *reg = NULL;
492 reg=new PUP_registry();
496 const PUP_regEntry *PUP_getRegEntry(const PUP::able::PUP_ID &id)
498 const PUP_regEntry *cur=(const PUP_regEntry *)(
499 PUP_getRegistry()->CkHashtable::get((const void *)&id) );
501 CmiAbort("Unrecognized PUP::able::PUP_ID. is there an unregistered module?");
505 PUP::able::PUP_ID PUP::able::register_constructor
506 (const char *className,constructor_function fn)
508 PUP::able::PUP_ID id(className);
509 PUP_getRegistry()->put(id)=PUP_regEntry(className,id,fn);
513 PUP::able::constructor_function PUP::able::get_constructor
514 (const PUP::able::PUP_ID &id)
516 return PUP_getRegEntry(id)->ctor;
519 //For allocatable objects: new/delete object and call pup routine
520 void PUP::er::object(able** a)
522 const PUP_regEntry *r=NULL;
524 { //Find the object type & create the object
525 PUP::able::PUP_ID id;//The object's id
527 if (id==null_PUP_ID) {*a=NULL; return;}
528 r=PUP_getRegEntry(id);
529 //Invoke constructor (calls new)
532 } else {//Just write out the object type
534 null_PUP_ID.pup(*this);
537 const PUP::able::PUP_ID &id=(*a)->get_PUP_ID();
539 r=PUP_getRegEntry(id);
542 syncComment(PUP::sync_begin_object,r->name);
544 syncComment(PUP::sync_end_object);
547 /****************** Text Pup ******************/
549 char *PUP::toTextUtil::beginLine(void) {
550 //Indent level tabs over:
551 for (int i=0;i<level;i++) cur[i]='\t';
555 void PUP::toTextUtil::endLine(void) {
558 void PUP::toTextUtil::beginEnv(const char *type,int n)
561 sprintf(o,"begin "); o+=strlen(o);
562 sprintf(o,type,n); o+=strlen(o);
567 void PUP::toTextUtil::endEnv(const char *type)
570 sprintf(beginLine(),"} end %s;\n",type);
573 PUP::toTextUtil::toTextUtil(unsigned int inType,char *buf)
580 void PUP::toTextUtil::comment(const char *message)
582 sprintf(beginLine(),"//%s\n",message); endLine();
585 void PUP::toTextUtil::synchronize(unsigned int m)
587 sprintf(beginLine(),"sync=0x%08x\n",m); endLine();
588 #if 0 /* text people don't care this much about synchronization */
590 sprintf(o,"sync=");o+=strlen(o);
591 const char *consonants="bcdfgjklmprstvxz";
592 const char *vowels="aeou";
593 for (int firstBit=0;firstBit<32;firstBit+=6) {
594 sprintf(o,"%c%c%c", consonants[0xf&(m>>firstBit)],
595 vowels[0x3&(m>>(firstBit+4))],
596 (firstBit==30)?';':'-');
599 sprintf(o,"\n"); endLine();
603 void PUP::toTextUtil::bytes(void *p,size_t n,size_t itemSize,dataType t) {
605 { /*Character data is written out directly (rather than numerically)*/
607 sprintf(o,"string=");o+=strlen(o);
608 *o++='\"'; /*Leading quote*/
609 /*Copy each character, possibly escaped*/
610 const char *c=(const char *)p;
611 for (size_t i=0;i<n;i++) {
613 sprintf(o,"\\n");o+=strlen(o);
614 } else if (iscntrl(c[i])) {
615 sprintf(o,"\\x%02X",(unsigned char)c[i]);o+=strlen(o);
616 } else if (c[i]=='\\' || c[i]=='\"') {
617 sprintf(o,"\\%c",c[i]);o+=strlen(o);
621 /*Add trailing quote and newline*/
622 sprintf(o,"\";\n");o+=strlen(o);
624 } else if (t==Tbyte || t==Tuchar)
625 { /*Byte data is written out in hex (rather than decimal) */
626 beginEnv("byte %d",n);
627 const unsigned char *c=(const unsigned char *)p;
629 for (size_t i=0;i<n;i++) {
630 sprintf(o,"%02X ",c[i]);o+=strlen(o);
631 if (i%25==24 && (i+1!=n))
632 { /* This line is too long-- wrap it */
633 sprintf(o,"\n"); o+=strlen(o);
634 endLine(); o=beginLine();
642 { /*Ordinary number-- write out in decimal */
643 if (n!=1) beginEnv("array %d",n);
644 for (size_t i=0;i<n;i++) {
647 case Tshort: sprintf(o,"short=%d;\n",((short *)p)[i]); break;
648 case Tushort: sprintf(o,"ushort=%u;\n",((unsigned short *)p)[i]); break;
649 case Tint: sprintf(o,"int=%d;\n",((int *)p)[i]); break;
650 case Tuint: sprintf(o,"uint=%u;\n",((unsigned int *)p)[i]); break;
651 case Tlong: sprintf(o,"long=%ld;\n",((long *)p)[i]); break;
652 case Tulong: sprintf(o,"ulong=%lu;\n",((unsigned long *)p)[i]); break;
653 case Tfloat: sprintf(o,"float=%.7g;\n",((float *)p)[i]); break;
654 case Tdouble: sprintf(o,"double=%.15g;\n",((double *)p)[i]); break;
655 case Tbool: sprintf(o,"bool=%s;\n",((bool *)p)[i]?"true":"false"); break;
656 #if CMK_LONG_DOUBLE_DEFINED
657 case Tlongdouble: sprintf(o,"longdouble=%Lg;\n",((long double *)p)[i]);break;
659 #ifdef CMK_PUP_LONG_LONG
660 case Tlonglong: sprintf(o,"longlong=%lld;\n",((CMK_PUP_LONG_LONG *)p)[i]);break;
661 case Tulonglong: sprintf(o,"ulonglong=%llu;\n",((unsigned CMK_PUP_LONG_LONG *)p)[i]);break;
663 case Tpointer: sprintf(o,"pointer=%p;\n",((void **)p)[i]); break;
664 default: CmiAbort("Unrecognized pup type code!");
668 if (n!=1) endEnv("array");
671 void PUP::toTextUtil::object(able** a) {
679 char *PUP::sizerText::advance(char *cur) {
680 charCount+=strlen(cur);
684 PUP::sizerText::sizerText(void)
685 :toTextUtil(IS_SIZING+IS_COMMENTS,line),charCount(0) { }
688 char *PUP::toText::advance(char *cur) {
689 charCount+=strlen(cur);
690 return buf+charCount;
693 PUP::toText::toText(char *outBuf)
694 :toTextUtil(IS_PACKING+IS_COMMENTS,outBuf),buf(outBuf),charCount(0) { }
696 /************** To/from text FILE ****************/
697 void PUP::toTextFile::bytes(void *p,size_t n,size_t itemSize,dataType t)
699 for (size_t i=0;i<n;i++)
701 case Tchar: fprintf(f," '%c'",((char *)p)[i]); break;
703 case Tbyte: fprintf(f," %02X",((unsigned char *)p)[i]); break;
704 case Tshort: fprintf(f," %d",((short *)p)[i]); break;
705 case Tushort: fprintf(f," %u",((unsigned short *)p)[i]); break;
706 case Tint: fprintf(f," %d",((int *)p)[i]); break;
707 case Tuint: fprintf(f," %u",((unsigned int *)p)[i]); break;
708 case Tlong: fprintf(f," %ld",((long *)p)[i]); break;
709 case Tulong: fprintf(f," %lu",((unsigned long *)p)[i]); break;
710 case Tfloat: fprintf(f," %.7g",((float *)p)[i]); break;
711 case Tdouble: fprintf(f," %.15g",((double *)p)[i]); break;
712 case Tbool: fprintf(f," %s",((bool *)p)[i]?"true":"false"); break;
713 #if CMK_LONG_DOUBLE_DEFINED
714 case Tlongdouble: fprintf(f," %Lg",((long double *)p)[i]);break;
716 #ifdef CMK_PUP_LONG_LONG
717 case Tlonglong: fprintf(f," %lld",((CMK_PUP_LONG_LONG *)p)[i]);break;
718 case Tulonglong: fprintf(f," %llu",((unsigned CMK_PUP_LONG_LONG *)p)[i]);break;
720 case Tpointer: fprintf(f," %p",((void **)p)[i]); break;
721 default: CmiAbort("Unrecognized pup type code!");
725 void PUP::toTextFile::comment(const char *message)
727 fprintf(f,"! %s\n",message);
730 void PUP::fromTextFile::parseError(const char *what) {
731 // find line number by counting how many returns
738 if (c=='\n') lineno++;
739 if (ftell(f) > cur) break;
741 fprintf(stderr,"Parse error during pup from text file: %s at line: %d\n",what, lineno);
742 CmiAbort("Parse error during pup from text file!\n");
744 int PUP::fromTextFile::readInt(const char *fmt) {
746 if (1!=fscanf(f,fmt,&ret)) {
747 if (feof(f)) return 0; /* start spitting out zeros at EOF */
748 else parseError("could not match integer");
752 unsigned int PUP::fromTextFile::readUint(const char *fmt) {
754 if (1!=fscanf(f,fmt,&ret)) {
755 if (feof(f)) return 0u; /* start spitting out zeros at EOF */
756 else parseError("could not match unsigned integer");
760 CMK_TYPEDEF_INT8 PUP::fromTextFile::readLongInt(const char *fmt) {
761 CMK_TYPEDEF_INT8 ret=0;
762 if (1!=fscanf(f,fmt,&ret)) {
763 if (feof(f)) return 0u;
764 else parseError("could not match large integer");
768 double PUP::fromTextFile::readDouble(void) {
770 if (1!=fscanf(f,"%lg",&ret)) {
771 if (feof(f)) return 0.0; /* start spitting out zeros at EOF */
772 else parseError("could not match double");
776 void PUP::fromTextFile::bytes(void *p,size_t n,size_t itemSize,dataType t)
778 for (size_t i=0;i<n;i++)
781 if (1!=fscanf(f," '%c'",&((char *)p)[i]))
782 parseError("Could not match character");
785 case Tbyte: ((unsigned char *)p)[i]=(unsigned char)readInt("%02X"); break;
786 case Tshort:((short *)p)[i]=(short)readInt(); break;
787 case Tushort: ((unsigned short *)p)[i]=(unsigned short)readUint(); break;
788 case Tint: ((int *)p)[i]=readInt(); break;
789 case Tuint: ((unsigned int *)p)[i]=readUint(); break;
790 case Tlong: ((long *)p)[i]=readInt(); break;
791 case Tulong:((unsigned long *)p)[i]=readUint(); break;
792 case Tfloat: ((float *)p)[i]=(float)readDouble(); break;
793 case Tdouble:((double *)p)[i]=readDouble(); break;
794 #if CMK_LONG_DOUBLE_DEFINED
797 if (1!=fscanf(f,"%Lg",&ret)) parseError("could not match long double");
798 ((long double *)p)[i]=ret;
801 #ifdef CMK_PUP_LONG_LONG
803 CMK_PUP_LONG_LONG ret=0;
804 if (1!=fscanf(f,"%lld",&ret)) parseError("could not match long long");
805 ((CMK_PUP_LONG_LONG *)p)[i]=ret;
808 unsigned CMK_PUP_LONG_LONG ret=0;
809 if (1!=fscanf(f,"%llu",&ret)) parseError("could not match unsigned long long");
810 ((unsigned CMK_PUP_LONG_LONG *)p)[i]=ret;
815 if (1!=fscanf(f," %19s",tmp)) parseError("could not read boolean string");
817 if (0==strcmp(tmp,"true")) val=true;
818 else if (0==strcmp(tmp,"false")) val=false;
819 else parseError("could not recognize boolean string");
825 if (1!=fscanf(f,"%p",&ret)) parseError("could not match pointer");
828 default: CmiAbort("Unrecognized pup type code!");
831 void PUP::fromTextFile::comment(const char *message)
834 //Skip to the start of the message:
835 while (isspace(c=fgetc(f))) {}
837 if (c!='!') return; //This isn't the start of a comment
838 //Skip over the whole line containing the comment:
839 char *commentBuf=(char *)CmiTmpAlloc(1024);
840 fgets(commentBuf,1024,f);
841 CmiTmpFree(commentBuf);