1 /****************************************************************************
2 **
3 **  This file is part of GAP, a system for computational discrete algebra.
4 **
5 **  Copyright of GAP belongs to its developers, whose names are too numerous
6 **  to list here. Please refer to the COPYRIGHT file for details.
7 **
8 **  SPDX-License-Identifier: GPL-2.0-or-later
9 **
10 **  This file contains the functions concerned with saving and loading
11 **  the workspace. There are support functions in gasman.c and elsewhere
12 **  throughout the kernel
13 */
14 
15 #include "saveload.h"
16 
17 #include "bool.h"
18 #include "calls.h"
19 #include "compstat.h"
20 #include "error.h"
21 #include "finfield.h"
22 #ifdef USE_GASMAN
23 #include "gasman_intern.h"
24 #endif
25 #include "gvars.h"
26 #include "io.h"
27 #include "modules.h"
28 #include "read.h"
29 #include "streams.h"
30 #include "stringobj.h"
31 #include "sysfiles.h"
32 #include "sysopt.h"
33 
34 #include <unistd.h>
35 
36 
37 /***************************************************************************
38 **
39 ** Temporary Stuff which will probably be revised to tie in with sysfiles
40 */
41 
42 
43 static Int SaveFile;
44 static Int LoadFile = -1;
45 static UInt1 LoadBuffer[100000];
46 static UInt1* LBPointer;
47 static UInt1* LBEnd;
48 static Obj userHomeExpand;
49 
50 #ifdef USE_GASMAN
51 
OpenForSave(Obj fname)52 static Int OpenForSave( Obj fname )
53 {
54   if (SaveFile != -1)
55     {
56       Pr("Already saving\n",0L,0L);
57       return 1;
58     }
59   SaveFile = SyFopen(CONST_CSTR_STRING(fname), "wb");
60   if (SaveFile == -1)
61     {
62       Pr("Couldn't open file %s to save workspace\n",
63          (UInt)CONST_CSTR_STRING(fname),0L);
64       return 1;
65     }
66   LBPointer = LoadBuffer;
67   LBEnd = LBPointer+sizeof(LoadBuffer);
68   return 0;
69 }
70 
CloseAfterSave(void)71 static void CloseAfterSave( void )
72 {
73   if (SaveFile == -1)
74     {
75       Panic("Internal error -- this should never happen");
76     }
77 
78   if (SyWrite(SaveFile, LoadBuffer, LBPointer - LoadBuffer) < 0)
79     ErrorQuit("Cannot write to file, see 'LastSystemError();'\n", 0L, 0L);
80   SyFclose(SaveFile);
81   SaveFile = -1;
82 }
83 
84 #endif
85 
OpenForLoad(const Char * fname)86 static void OpenForLoad( const Char *fname )
87 {
88   if (LoadFile != -1)
89     {
90       Panic("Internal error -- this should never happen");
91     }
92   LoadFile = SyFopen(fname, "rb");
93   if (LoadFile == -1)
94     {
95       Pr("Couldn't open saved workspace %s\n",(Int)fname,0L);
96       SyExit(1);
97     }
98 }
99 
100 
CloseAfterLoad(void)101 static void CloseAfterLoad( void )
102 {
103   if (!LoadFile)
104     {
105       Panic("Internal error -- this should never happen");
106     }
107   SyFclose(LoadFile);
108   LoadFile = -1;
109 }
110 
SAVE_BYTE_BUF(void)111 static void SAVE_BYTE_BUF(void)
112 {
113   if (SyWrite(SaveFile, LoadBuffer, LBEnd - LoadBuffer) < 0)
114     ErrorQuit("Cannot write to file, see 'LastSystemError();'\n", 0L, 0L);
115   LBPointer = LoadBuffer;
116   return;
117 }
118 
119 #define SAVE_BYTE(byte) {if (LBPointer >= LBEnd) {SAVE_BYTE_BUF();} \
120                           *LBPointer++ = (UInt1)(byte);}
121 
122 static const Char * LoadByteErrorMessage = "Unexpected End of File in Load\n";
123 
LOAD_BYTE_BUF(void)124 static UInt1 LOAD_BYTE_BUF(void)
125 {
126   Int ret;
127   ret = SyRead(LoadFile, LoadBuffer, 100000);
128   if (ret <= 0)
129     {
130       Pr(LoadByteErrorMessage, 0L, 0L );
131       SyExit(2);
132     }
133   LBEnd = LoadBuffer + ret;
134   LBPointer = LoadBuffer;
135   return *LBPointer++;
136 }
137 
138 #define LOAD_BYTE()    (UInt1)((LBPointer >= LBEnd) ?\
139                                   (LOAD_BYTE_BUF()) : (*LBPointer++))
140 
141 /***************************************************************************
142 **
143 **  Low level saving routines
144 */
145 
SaveUInt1(UInt1 data)146 void SaveUInt1( UInt1 data )
147 {
148   SAVE_BYTE( data );
149 }
150 
LoadUInt1(void)151 UInt1 LoadUInt1( void )
152 {
153   return LOAD_BYTE( );
154 }
155 
SaveUInt2(UInt2 data)156 void SaveUInt2( UInt2 data )
157 {
158   SAVE_BYTE( (UInt1) (data & 0xFF) );
159   SAVE_BYTE( (UInt1) (data >> 8) );
160 }
161 
LoadUInt2(void)162 UInt2 LoadUInt2 ( void )
163 {
164   UInt2 res;
165   res = (UInt2)LOAD_BYTE();
166   res |= (UInt2)LOAD_BYTE()<<8;
167   return res;
168 }
169 
SaveUInt4(UInt4 data)170 void SaveUInt4( UInt4 data )
171 {
172   SAVE_BYTE( (UInt1) (data & 0xFF) );
173   SAVE_BYTE( (UInt1) ((data >> 8) &0xFF) );
174   SAVE_BYTE( (UInt1) ((data >> 16) &0xFF) );
175   SAVE_BYTE( (UInt1) ((data >> 24) &0xFF) );
176 }
177 
LoadUInt4(void)178 UInt4 LoadUInt4 ( void )
179 {
180   UInt4 res;
181   res = (UInt)LOAD_BYTE();
182   res |= (UInt)LOAD_BYTE() << 8;
183   res |= (UInt)LOAD_BYTE() << 16;
184   res |= (UInt)LOAD_BYTE() << 24;
185   return res;
186 }
187 
SaveUInt8(UInt8 data)188 void SaveUInt8( UInt8 data )
189 {
190   SAVE_BYTE( (UInt1) (data & 0xFF) );
191   SAVE_BYTE( (UInt1) ((data >> 8) &0xFF) );
192   SAVE_BYTE( (UInt1) ((data >> 16) &0xFF) );
193   SAVE_BYTE( (UInt1) ((data >> 24) &0xFF) );
194   SAVE_BYTE( (UInt1) ((data >> 32) &0xFF) );
195   SAVE_BYTE( (UInt1) ((data >> 40) &0xFF) );
196   SAVE_BYTE( (UInt1) ((data >> 48) &0xFF) );
197   SAVE_BYTE( (UInt1) ((data >> 56) &0xFF) );
198 }
199 
LoadUInt8(void)200 UInt8 LoadUInt8 ( void )
201 {
202   UInt8 res;
203   res = (UInt8)LOAD_BYTE();
204   res |= (UInt8)LOAD_BYTE() << 8;
205   res |= (UInt8)LOAD_BYTE() << 16;
206   res |= (UInt8)LOAD_BYTE() << 24;
207   res |= (UInt8)LOAD_BYTE() << 32;
208   res |= (UInt8)LOAD_BYTE() << 40;
209   res |= (UInt8)LOAD_BYTE() << 48;
210   res |= (UInt8)LOAD_BYTE() << 56;
211 
212   return res;
213 }
214 
SaveUInt(UInt data)215 void SaveUInt( UInt data )
216 {
217 #ifdef SYS_IS_64_BIT
218     SaveUInt8(data);
219 #else
220     SaveUInt4(data);
221 #endif
222 }
223 
LoadUInt(void)224 UInt LoadUInt ( void )
225 {
226 #ifdef SYS_IS_64_BIT
227     return LoadUInt8();
228 #else
229     return LoadUInt4();
230 #endif
231 }
232 
SaveCStr(const Char * str)233 void SaveCStr( const Char * str)
234 {
235   do {
236     SAVE_BYTE( (UInt1) *str);
237   } while (*(str++));
238 }
239 
240 #include <assert.h>
241 
LoadCStr(Char * buf,UInt maxsize)242 void LoadCStr( Char *buf, UInt maxsize)
243 {
244   UInt nread = 0;
245   UInt1 c = 1;
246   assert(maxsize > 0);
247   while (c != '\0' && nread < maxsize )
248     {
249       c = LOAD_BYTE();
250       *buf++ = (Char) c;
251       nread++;
252     }
253   if (c != '\0')
254     {
255       Panic("Buffer overflow reading workspace");
256     }
257 }
258 
259 
260 /****************************************************************************
261 **
262 *F  SaveString( <string> )  . . . . . . . . . . . . . . . . . . save a string
263 **
264 */
SaveString(Obj string)265 void SaveString ( Obj string )
266 {
267   UInt i, len = GET_LEN_STRING(string);
268   const UInt1 *p = CONST_CHARS_STRING(string);
269   SaveUInt(len);
270   for (i=0; i<len; i++)
271     SAVE_BYTE(p[i]);
272 }
273 
274 /****************************************************************************
275 **
276 *F  LoadString( <string> )
277 **
278 */
LoadString(Obj string)279 void LoadString ( Obj string )
280 {
281   UInt i, len;
282   UInt1 c;
283   UInt1 *p = (UInt1*)CHARS_STRING(string);
284   len = LoadUInt();
285   SET_LEN_STRING(string, len);
286   for (i=0; i<len; i++) {
287     c = LOAD_BYTE();
288     p[i] = c;
289   }
290 }
291 
SaveSubObj(Obj subobj)292 void SaveSubObj( Obj subobj )
293 {
294 #ifndef USE_GASMAN
295   // FIXME: HACK
296   assert(0);
297 #else
298   if (!subobj)
299     SaveUInt(0);
300   else if (IS_INTOBJ(subobj))
301     SaveUInt((UInt) subobj);
302   else if (IS_FFE(subobj))
303     SaveUInt((UInt) subobj);
304   else if ((((UInt)subobj & 3) != 0) ||
305            subobj < (Bag)MptrBags ||
306            subobj > (Bag)MptrEndBags ||
307            (Bag *)PTR_BAG(subobj) < MptrEndBags)
308     {
309       Pr("#W bad bag id %d found, 0 saved\n", (Int)subobj, 0L);
310       SaveUInt(0);
311     }
312   else
313     SaveUInt(((UInt)LINK_BAG(subobj)) << 2);
314 #endif
315 }
316 
LoadSubObj(void)317 Obj LoadSubObj( void )
318 {
319 #ifndef USE_GASMAN
320   // FIXME: HACK
321   assert(0);
322 #else
323   UInt word = LoadUInt();
324   if (word == 0)
325     return (Obj) 0;
326   if ((word & 0x3) == 1 || (word & 0x3) == 2)
327     return (Obj) word;
328   else
329     return (Obj)(MptrBags + (word >> 2)-1);
330 #endif
331 }
332 
333 
334 /***************************************************************************
335 **
336 **  Bag level saving routines
337 */
338 
339 #ifdef USE_GASMAN
340 
SaveBagData(Bag bag)341 static void SaveBagData (Bag bag )
342 {
343   BagHeader * header = BAG_HEADER(bag);
344   SaveUInt1(header->type);
345   SaveUInt1(header->flags);
346   SaveUInt(header->size);
347 
348   /* dispatch */
349   (*(SaveObjFuncs[ header->type]))(bag);
350 
351 }
352 
353 
354 
LoadBagData(void)355 static void LoadBagData ( void )
356 {
357   Bag bag;
358   UInt type, flags, size;
359 
360   /* Recover the size & type */
361   type = LoadUInt1();
362   flags = LoadUInt1();
363   size = LoadUInt();
364 
365   if (TNAM_TNUM(type) == NULL)
366     Panic("Bad type %d, size %d\n", (int)type, (int)size);
367 
368   /* Get GASMAN to set up the bag for me */
369   bag = NextBagRestoring( type, flags, size );
370 
371   /* dispatch */
372   (*(LoadObjFuncs[ type ]))(bag);
373 }
374 
375 #endif
376 
377 /***************************************************************************
378 **
379 *F  WriteSaveHeader() . . . . .  and utility functions, and loading functions
380 **
381 */
382 
383 #ifdef USE_GASMAN
384 
WriteEndiannessMarker(void)385 static void WriteEndiannessMarker( void )
386 {
387   UInt x;
388 #ifdef SYS_IS_64_BIT
389   x = 0x0102030405060708L;
390 #else
391   x = 0x01020304L;
392 #endif
393   SAVE_BYTE(((UInt1 *)&x)[0]);
394   SAVE_BYTE(((UInt1 *)&x)[1]);
395   SAVE_BYTE(((UInt1 *)&x)[2]);
396   SAVE_BYTE(((UInt1 *)&x)[3]);
397 #ifdef SYS_IS_64_BIT
398   SAVE_BYTE(((UInt1 *)&x)[4]);
399   SAVE_BYTE(((UInt1 *)&x)[5]);
400   SAVE_BYTE(((UInt1 *)&x)[6]);
401   SAVE_BYTE(((UInt1 *)&x)[7]);
402 #endif
403 }
404 
405 #endif
406 
407 
CheckEndiannessMarker(void)408 static void CheckEndiannessMarker( void )
409 {
410   UInt x;
411   ((UInt1 *)&x)[0] = LOAD_BYTE();
412   ((UInt1 *)&x)[1] = LOAD_BYTE();
413   ((UInt1 *)&x)[2] = LOAD_BYTE();
414   ((UInt1 *)&x)[3] = LOAD_BYTE();
415 #ifdef SYS_IS_64_BIT
416   ((UInt1 *)&x)[4] = LOAD_BYTE();
417   ((UInt1 *)&x)[5] = LOAD_BYTE();
418   ((UInt1 *)&x)[6] = LOAD_BYTE();
419   ((UInt1 *)&x)[7] = LOAD_BYTE();
420   if (x != 0x0102030405060708L)
421 #else
422   if (x != 0x01020304L)
423 #endif
424     {
425       Panic("Saved workspace with incompatible byte order");
426     }
427 }
428 
429 
430 /***************************************************************************
431 **
432 **  FuncBagStats
433 */
434 
435 #ifdef USE_GASMAN
436 
437 static FILE *file;
438 
report(Bag bag)439 static void report( Bag bag)
440 {
441   fprintf(file,"%li %li\n", (long) TNUM_BAG(bag), (long) SIZE_BAG(bag));
442 }
443 
FuncBagStats(Obj self,Obj filename)444 static Obj FuncBagStats(Obj self, Obj filename)
445 {
446   file = fopen((Char *)CHARS_STRING(filename),"w");
447   CallbackForAllBags(report);
448   fclose(file);
449   return (Obj) 0;
450 }
451 
452 #endif
453 
454 
455 /***************************************************************************
456 **
457 **  Find Bags -- a useful debugging tool -- scan for a bag of specified
458 **   type and size and return it to the GAP level. Could be a problem
459 **  if the bag is not a valid GAP object -- eg a local variables bag or
460 **  a functions body.
461 */
462 
463 #ifdef USE_GASMAN
464 
465 static UInt fb_minsize, fb_maxsize, fb_tnum;
466 static Bag hit;
467 
ScanBag(Bag bag)468 static void ScanBag( Bag bag)
469 {
470   if (hit == (Bag)0 &&
471       SIZE_BAG(bag) >= fb_minsize &&
472       SIZE_BAG(bag) <= fb_maxsize &&
473       TNUM_BAG(bag) == fb_tnum)
474     hit = bag;
475 }
476 
FuncFindBag(Obj self,Obj minsize,Obj maxsize,Obj tnum)477 static Obj FuncFindBag(Obj self, Obj minsize, Obj maxsize, Obj tnum)
478 {
479   hit = (Bag) 0;
480   fb_minsize = INT_INTOBJ(minsize);
481   fb_maxsize = INT_INTOBJ(maxsize);
482   fb_tnum = INT_INTOBJ(tnum);
483   CallbackForAllBags(ScanBag);
484   return (hit != (Bag) 0) ? hit : Fail;
485 }
486 
487 #endif
488 
489 
490 /***************************************************************************
491 **
492 *F  SaveWorkspace( <fname> )  . . . . .  save the workspace to the named file
493 **
494 **  'SaveWorkspace' is the entry point to the workspace saving. It is not
495 **  installed as a GAP function, but instead as a keyword, so that we can be
496 **  sure it is only being called from the top-most prompt level
497 **  The file saveload.tex in the dev directory describes the saved format
498 **  in more detail. Most of the work will be done from inside GASMAN, because
499 **  we need to fiddle with Bag internals somewhat
500 **
501 **  The return value is either True or Fail
502 */
503 
504 #ifdef USE_GASMAN
505 
506 static UInt NextSaveIndex = 1;
507 
AddSaveIndex(Bag bag)508 static void AddSaveIndex( Bag bag)
509 {
510   LINK_BAG(bag) = (Obj)NextSaveIndex++;
511 }
512 
RemoveSaveIndex(Bag bag)513 static void RemoveSaveIndex( Bag bag)
514 {
515   LINK_BAG(bag) = bag;
516 }
517 
518 
GetKernelDescription(void)519 static Char * GetKernelDescription(void)
520 {
521     static Char SyKernelDescription[256];
522     strcpy(SyKernelDescription, SyKernelVersion);
523     if (SyUseReadline) {
524         strcat(SyKernelDescription, " with readline");
525     }
526     return SyKernelDescription;
527 }
528 
WriteSaveHeader(void)529 static void WriteSaveHeader( void )
530 {
531   UInt i;
532 
533   SaveCStr("GAP workspace");
534   SaveCStr(GetKernelDescription());
535 
536 #ifdef SYS_IS_64_BIT
537   SaveCStr("64 bit");
538 #else
539   SaveCStr("32 bit");
540 #endif
541 
542   WriteEndiannessMarker();
543 
544   SaveCStr("Counts and Sizes");
545   for (i = 0; i < GlobalBags.nr; i++) {
546       GAP_ASSERT(GlobalBags.cookie[i] != NULL);
547   }
548   SaveUInt(GlobalBags.nr);
549   SaveUInt(NextSaveIndex-1);
550   SaveUInt(AllocBags - MptrEndBags);
551 
552   SaveCStr("Loaded Modules");
553   SaveModules();
554 
555   SaveCStr("Kernel to WS refs");
556   for (i = 0; i < GlobalBags.nr; i++)
557     {
558       GAP_ASSERT(GlobalBags.cookie[i] != NULL);
559       SaveCStr((const Char *)GlobalBags.cookie[i]);
560       SaveSubObj(*(GlobalBags.addr[i]));
561     }
562 }
563 #endif
564 
SaveWorkspace(Obj fname)565 Obj SaveWorkspace( Obj fname )
566 {
567 #ifndef USE_GASMAN
568   Pr("SaveWorkspace is only supported when GASMAN is in use",0,0);
569   return Fail;
570 
571 #else
572   Obj fullname;
573   Obj result;
574 
575   if (!IsStringConv(fname))
576     ErrorQuit("usage: SaveWorkspace( <filename> )",0,0);
577   /* maybe expand fname starting with ~/...   */
578   fullname = Call1ArgsInNewReader(userHomeExpand, fname);
579 
580   if (ModulesPreSave())
581     return Fail;
582 
583   /* Do a full garbage collection */
584   CollectBags( 0, 1);
585 
586   /* Add indices in link words of all bags, for saving inter-bag references */
587   NextSaveIndex = 1;
588   CallbackForAllBags( AddSaveIndex );
589 
590   /* Now do the work */
591   result = Fail;
592   if (!OpenForSave( fullname ))
593     {
594       result = True;
595       WriteSaveHeader();
596       SaveCStr("Bag data");
597       SortHandlers( 1 ); /* Sort by address to speed up CookieOfHandler */
598       CallbackForAllBags( SaveBagData );
599       CloseAfterSave();
600     }
601 
602   /* Finally, reset all the link words */
603   CallbackForAllBags( RemoveSaveIndex );
604 
605   /* Restore situation by calling all post-save methods */
606   ModulesPostSave();
607 
608   return result;
609 #endif
610 }
611 
612 
FuncSaveWorkspace(Obj self,Obj filename)613 static Obj FuncSaveWorkspace(Obj self, Obj filename)
614 {
615   return SaveWorkspace( filename );
616 }
617 
618 
619 /***************************************************************************
620 **
621 *F  LoadWorkspace( <fname> )  . . . . .  load the workspace to the named file
622 **
623 **  'LoadWorkspace' is the entry point to the workspace saving. It is not
624 **  installed as a GAP function, but instead called from InitGap when the
625 **  -L commad-line flag is given
626 **
627 **  The file saveload.tex in the dev directory describes the saved format
628 **  in more detail. Most of the work will be done from inside GASMAN, because
629 **  we need to fiddle with Bag internals somewhat
630 **
631 */
632 
633 
LoadWorkspace(Char * fname)634 void LoadWorkspace( Char * fname )
635 {
636 #ifndef USE_GASMAN
637   Pr("LoadWorkspace is only supported when GASMAN is in use\n",0,0);
638 
639 #else
640   UInt nGlobs, nBags, i, maxSize;
641   Char buf[256];
642   Obj * glob;
643 
644   /* Open saved workspace  */
645   OpenForLoad( fname );
646 
647   /* Check file header */
648 
649   LoadCStr(buf,256);
650   if (strncmp (buf, "GAP ", 4) != 0) {
651      Pr("File %s does not appear to be a GAP workspae.\n", (long) fname, 0L);
652      SyExit(1);
653   }
654 
655   if (strcmp (buf, "GAP workspace") == 0) {
656 
657      LoadCStr(buf,256);
658      if (strcmp(buf, GetKernelDescription()) != 0) {
659          Pr("This workspace is not compatible with GAP kernel (%s, present: "
660             "%s).\n",
661             (long)buf, (long)GetKernelDescription());
662          SyExit(1);
663      }
664 
665      LoadCStr(buf,256);
666 #ifdef SYS_IS_64_BIT
667      if (strcmp(buf,"64 bit") != 0)
668 #else
669      if (strcmp(buf,"32 bit") != 0)
670 #endif
671         {
672            Pr("This workspace was created by a %s version of GAP.\n", (long)buf, 0L);
673            SyExit(1);
674         }
675   } else {
676      Pr("File %s probably isn't a GAP workspace.\n", (long)fname, 0L);
677      SyExit(1);
678   }
679 
680   CheckEndiannessMarker();
681 
682   LoadCStr(buf,256);
683   if (strcmp(buf,"Counts and Sizes") != 0)
684     {
685       Panic("Bad divider");
686     }
687 
688   nGlobs = LoadUInt();
689   nBags = LoadUInt();
690   maxSize = LoadUInt();
691 
692   /* Make sure there is enough room, and signal GASMAN that
693      we are starting a restore */
694   StartRestoringBags(nBags, maxSize);
695 
696   /* The restoring kernel must have at least as many compiled modules
697      as the saving one. */
698   LoadCStr(buf,256);
699   if (strcmp(buf,"Loaded Modules") != 0)
700     {
701       Panic("Bad divider");
702     }
703   LoadModules();
704 
705   /* Now the kernel variables that point into the workspace */
706   LoadCStr(buf,256);
707   if (strcmp(buf,"Kernel to WS refs") != 0)
708     {
709       Panic("Bad divider");
710     }
711   SortGlobals(2);               /* globals by cookie for quick
712                                  lookup */
713   for (i = 0; i < GlobalBags.nr; i++)
714     {
715       GAP_ASSERT(GlobalBags.cookie[i] != NULL);
716     }
717     // TODO: the goal here is to stop exporting `GlobalBags` completely...
718     if (nGlobs != GlobalBags.nr) {
719         Panic("Wrong number of global bags in saved workspace %d %d",
720               (int)nGlobs, (int)GlobalBags.nr);
721     }
722     for (i = 0; i < nGlobs; i++) {
723         LoadCStr(buf, 256);
724         glob = GlobalByCookie(buf);
725         if (glob == (Obj *)0) {
726             Panic(
727                 "Global object cookie from workspace not found in kernel %s",
728                 buf);
729         }
730       *glob = LoadSubObj();
731       if (SyDebugLoading)
732           Pr("Restored global %s\n", (Int)buf, 0L);
733     }
734 
735   LoadCStr(buf,256);
736   if (strcmp(buf,"Bag data") != 0)
737     {
738       Panic("Bad divider");
739     }
740 
741   SortHandlers(2);
742   for (i = 0; i < nBags; i++)
743     LoadBagData();
744 
745   FinishedRestoringBags();
746 
747   CloseAfterLoad();
748 #endif
749 
750     ModulesPostRestore();
751 }
752 
PrSavedObj(UInt x)753 static void PrSavedObj( UInt x)
754 {
755   if ((x & 3) == 1)
756     Pr("Immediate  integer %d\n", INT_INTOBJ((Obj)x),0L);
757   else if ((x & 3) == 2)
758     Pr("Immediate FFE %d %d\n", VAL_FFE((Obj)x), SIZE_FF(FLD_FFE((Obj)x)));
759   else
760     Pr("Reference to bag number %d\n",x>>2,0L);
761 }
762 
FuncDumpWorkspace(Obj self,Obj fname)763 static Obj FuncDumpWorkspace(Obj self, Obj fname)
764 {
765   UInt nMods, nGlobs, nBags, i, relative;
766   Char buf[256];
767   OpenForLoad( CONST_CSTR_STRING(fname) );
768   LoadCStr(buf,256);
769   Pr("Header string: %s\n",(Int) buf, 0L);
770   LoadCStr(buf,256);
771   Pr("GAP Version: %s\n",(Int)buf, 0L);
772   LoadCStr(buf,256);
773   Pr("Word length: %s\n",(Int)buf, 0L);
774   CheckEndiannessMarker();
775   LoadCStr(buf,256);
776   Pr("Divider string: %s\n",(Int)buf,0L);
777   if (strcmp(buf,"Counts and Sizes") != 0)
778     ErrorQuit("Bad divider",0L,0L);
779   Pr("Loaded modules: %d\n",nMods = LoadUInt(), 0L);
780   Pr("Global Bags   : %d\n",nGlobs = LoadUInt(), 0L);
781   Pr("Total Bags    : %d\n",nBags = LoadUInt(), 0L);
782   Pr("Maximum Size  : %d\n",sizeof(Bag)*LoadUInt(), 0L);
783   LoadCStr(buf,256);
784   Pr("Divider string: %s\n",(Int)buf, 0L);
785   if (strcmp(buf,"Loaded Modules") != 0)
786     ErrorQuit("Bad divider",0L,0L);
787   for (i = 0; i < nMods; i++)
788     {
789       UInt type;
790       type = LoadUInt();
791       Pr("Type: %d ",type,0);
792       relative = LoadUInt();
793       if (relative)
794         Pr("GAP root relative ", 0L, 0L);
795       else
796         Pr("absolute ", 0L, 0L);
797       LoadCStr(buf,256);
798       Pr("  %s\n",(Int)buf,0L);
799     }
800   LoadCStr(buf,256);
801   Pr("Divider string: %s\n",(Int)buf,0L);
802   if (strcmp(buf,"Kernel to WS refs") != 0)
803     ErrorQuit("Bad divider",0L,0L);
804   for (i = 0; i < nGlobs; i++)
805     {
806       LoadCStr(buf,256);
807       Pr("  %s ",(Int)buf,0L);
808       PrSavedObj(LoadUInt());
809     }
810   LoadCStr(buf,256);
811   Pr("Divider string: %s\n",(Int)buf,0L);
812   if (strcmp(buf,"Bag data") != 0)
813     ErrorQuit("Bad divider",0L,0L);
814   CloseAfterLoad();
815   return (Obj) 0;
816 }
817 
818 
819 /****************************************************************************
820 **
821 *F * * * * * * * * * * * * * initialize module * * * * * * * * * * * * * * *
822 */
823 
824 
825 /****************************************************************************
826 **
827 *V  GVarFuncs . . . . . . . . . . . . . . . . . . list of functions to export
828 */
829 static StructGVarFunc GVarFuncs [] = {
830 
831     GVAR_FUNC(SaveWorkspace, 1, "fname"),
832     GVAR_FUNC(DumpWorkspace, 1, "fname"),
833 #ifdef USE_GASMAN
834     GVAR_FUNC(FindBag, 3, "minsize, maxsize, tnum"),
835     GVAR_FUNC(BagStats, 1, "filename"),
836 #endif
837     { 0, 0, 0, 0, 0 }
838 
839 };
840 
841 
842 /****************************************************************************
843 **
844 *F  InitKernel( <module> )  . . . . . . . . initialise kernel data structures
845 */
InitKernel(StructInitInfo * module)846 static Int InitKernel (
847     StructInitInfo *    module )
848 {
849     SaveFile = -1;
850     LBPointer = LoadBuffer;
851     LBEnd = LoadBuffer;
852 
853     /* init filters and functions                                          */
854     InitHdlrFuncsFromTable( GVarFuncs );
855     /* allow ~/... expansion in SaveWorkspace                              */
856     ImportFuncFromLibrary("UserHomeExpand", &userHomeExpand);
857 
858     /* return success                                                      */
859     return 0;
860 }
861 
862 
863 /****************************************************************************
864 **
865 *F  InitLibrary( <module> ) . . . . . . .  initialise library data structures
866 */
InitLibrary(StructInitInfo * module)867 static Int InitLibrary (
868     StructInitInfo *    module )
869 {
870     /* init filters and functions                                          */
871     InitGVarFuncsFromTable( GVarFuncs );
872 
873     /* return success                                                      */
874     return 0;
875 }
876 
877 
878 /****************************************************************************
879 **
880 *F  InitInfoSaveLoad()  . . . . . . . . . . . . . . . table of init functions
881 */
882 static StructInitInfo module = {
883     // init struct using C99 designated initializers; for a full list of
884     // fields, please refer to the definition of StructInitInfo
885     .type = MODULE_BUILTIN,
886     .name = "saveload",
887     .initKernel = InitKernel,
888     .initLibrary = InitLibrary,
889 };
890 
InitInfoSaveLoad(void)891 StructInitInfo * InitInfoSaveLoad ( void )
892 {
893     return &module;
894 }
895