/****************************************************************************
* *
* * This file is part of GAP , a system for computational discrete algebra .
* *
* * Copyright of GAP belongs to its developers , whose names are too numerous
* * to list here . Please refer to the COPYRIGHT file for details .
* *
* * SPDX - License - Identifier : GPL - 2 . 0 - or - later
* *
* * This file contains the functions concerned with saving and loading
* * the workspace . There are support functions in gasman . c and elsewhere
* * throughout the kernel
*/
#include "saveload.h"
#include "bool.h"
#include "calls.h"
#include "error.h"
#include "finfield.h"
#ifdef USE_GASMAN
#include "gasman_intern.h"
#endif
#include "gvars.h"
#include "io.h"
#include "modules.h"
#include "read.h"
#include "streams.h"
#include "stringobj.h"
#include "sysfiles.h"
#include "sysopt.h"
#include "sysstr.h"
#include "version.h"
#include "config.h"
#include <stdio.h>
#include <unistd.h>
#ifdef GAP_ENABLE_SAVELOAD
/***************************************************************************
* *
* * Temporary Stuff which will probably be revised to tie in with sysfiles
*/
static Int SaveFile;
static Int LoadFile = -1 ;
static UInt1 LoadBuffer[100000 ];
static UInt1* LBPointer;
static UInt1* LBEnd;
static Obj userHomeExpand;
static Int OpenForSave( Obj fname )
{
if (SaveFile != -1 )
{
Pr("Already saving\n" , 0 , 0 );
return 1 ;
}
SaveFile = SyFopen(CONST_CSTR_STRING(fname), "wb" , TRUE );
if (SaveFile == -1 ) {
Pr("Couldn't open file %s to save workspace\n" ,
(UInt)CONST_CSTR_STRING(fname), 0 );
return 1 ;
}
LBPointer = LoadBuffer;
LBEnd = LBPointer+sizeof (LoadBuffer);
return 0 ;
}
static void CloseAfterSave( void )
{
if (SaveFile == -1 )
{
Panic("Internal error -- this should never happen" );
}
if (SyWrite(SaveFile, LoadBuffer, LBPointer - LoadBuffer) < 0 )
ErrorQuit("Cannot write to file, see 'LastSystemError();'" , 0 , 0 );
SyFclose(SaveFile);
SaveFile = -1 ;
}
static void OpenForLoad( const Char *fname )
{
if (LoadFile != -1 )
{
Panic("Internal error -- this should never happen" );
}
LoadFile = SyFopen(fname, "rb" , TRUE );
if (LoadFile == -1 ) {
Pr("Couldn't open saved workspace %s\n" , (Int )fname, 0 );
SyExit(1 );
}
}
static void CloseAfterLoad( void )
{
if (!LoadFile)
{
Panic("Internal error -- this should never happen" );
}
SyFclose(LoadFile);
LoadFile = -1 ;
}
static void SAVE_BYTE_BUF(void )
{
if (SyWrite(SaveFile, LoadBuffer, LBEnd - LoadBuffer) < 0 )
ErrorQuit("Cannot write to file, see 'LastSystemError();'" , 0 , 0 );
LBPointer = LoadBuffer;
return ;
}
#define SAVE_BYTE(byte) {if (LBPointer >= LBEnd) {SAVE_BYTE_BUF();} \
*LBPointer++ = (UInt1)(byte);}
static UInt1 LOAD_BYTE_BUF(void )
{
Int ret;
ret = SyRead(LoadFile, LoadBuffer, sizeof (LoadBuffer));
if (ret <= 0 )
{
Panic("Unexpected End of File in Load" );
}
LBEnd = LoadBuffer + ret;
LBPointer = LoadBuffer;
return *LBPointer++;
}
#define LOAD_BYTE() (UInt1)((LBPointer >= LBEnd) ?\
(LOAD_BYTE_BUF()) : (*LBPointer++))
/***************************************************************************
* *
* * Low level saving routines
*/
void SaveUInt1( UInt1 data )
{
SAVE_BYTE( data );
}
UInt1 LoadUInt1( void )
{
return LOAD_BYTE( );
}
void SaveUInt2( UInt2 data )
{
SAVE_BYTE( (UInt1) (data & 0 xFF) );
SAVE_BYTE( (UInt1) (data >> 8 ) );
}
UInt2 LoadUInt2 ( void )
{
UInt2 res;
res = (UInt2)LOAD_BYTE();
res |= (UInt2)LOAD_BYTE()<<8 ;
return res;
}
void SaveUInt4( UInt4 data )
{
SAVE_BYTE( (UInt1) (data & 0 xFF) );
SAVE_BYTE( (UInt1) ((data >> 8 ) &0 xFF) );
SAVE_BYTE( (UInt1) ((data >> 16 ) &0 xFF) );
SAVE_BYTE( (UInt1) ((data >> 24 ) &0 xFF) );
}
UInt4 LoadUInt4 ( void )
{
UInt4 res;
res = (UInt)LOAD_BYTE();
res |= (UInt)LOAD_BYTE() << 8 ;
res |= (UInt)LOAD_BYTE() << 16 ;
res |= (UInt)LOAD_BYTE() << 24 ;
return res;
}
void SaveUInt8( UInt8 data )
{
SAVE_BYTE( (UInt1) (data & 0 xFF) );
SAVE_BYTE( (UInt1) ((data >> 8 ) &0 xFF) );
SAVE_BYTE( (UInt1) ((data >> 16 ) &0 xFF) );
SAVE_BYTE( (UInt1) ((data >> 24 ) &0 xFF) );
SAVE_BYTE( (UInt1) ((data >> 32 ) &0 xFF) );
SAVE_BYTE( (UInt1) ((data >> 40 ) &0 xFF) );
SAVE_BYTE( (UInt1) ((data >> 48 ) &0 xFF) );
SAVE_BYTE( (UInt1) ((data >> 56 ) &0 xFF) );
}
UInt8 LoadUInt8 ( void )
{
UInt8 res;
res = (UInt8)LOAD_BYTE();
res |= (UInt8)LOAD_BYTE() << 8 ;
res |= (UInt8)LOAD_BYTE() << 16 ;
res |= (UInt8)LOAD_BYTE() << 24 ;
res |= (UInt8)LOAD_BYTE() << 32 ;
res |= (UInt8)LOAD_BYTE() << 40 ;
res |= (UInt8)LOAD_BYTE() << 48 ;
res |= (UInt8)LOAD_BYTE() << 56 ;
return res;
}
void SaveUInt( UInt data )
{
#ifdef SYS_IS_64_BIT
SaveUInt8(data);
#else
SaveUInt4(data);
#endif
}
UInt LoadUInt ( void )
{
#ifdef SYS_IS_64_BIT
return LoadUInt8();
#else
return LoadUInt4();
#endif
}
void SaveCStr( const Char * str)
{
do {
SAVE_BYTE( (UInt1) *str);
} while (*(str++));
}
void LoadCStr( Char *buf, UInt maxsize)
{
UInt nread = 0 ;
UInt1 c = 1 ;
assert(maxsize > 0 );
while (c != '\0' && nread < maxsize )
{
c = LOAD_BYTE();
*buf++ = (Char ) c;
nread++;
}
if (c != '\0' )
{
Panic("Buffer overflow reading workspace" );
}
}
/****************************************************************************
* *
* F SaveString ( < string > ) . . . . . . . . . . . . . . . . . . save a string
* *
*/
void SaveString ( Obj string )
{
UInt i, len = GET_LEN_STRING(string);
const UInt1 *p = CONST_CHARS_STRING(string);
SaveUInt(len);
for (i=0 ; i<len; i++)
SAVE_BYTE(p[i]);
}
/****************************************************************************
* *
* F LoadString ( < string > )
* *
*/
void LoadString ( Obj string )
{
UInt i, len;
UInt1 c;
UInt1 *p = (UInt1*)CHARS_STRING(string);
len = LoadUInt();
SET_LEN_STRING(string, len);
for (i=0 ; i<len; i++) {
c = LOAD_BYTE();
p[i] = c;
}
}
#ifdef USE_GASMAN
void SaveSubObj( Obj subobj )
{
if (!subobj)
SaveUInt(0 );
else if (IS_INTOBJ(subobj))
SaveUInt((UInt) subobj);
else if (IS_FFE(subobj))
SaveUInt((UInt) subobj);
else if (IS_VALID_BAG_ID(subobj))
SaveUInt(((UInt)LINK_BAG(subobj)) << 2 );
else
{
Pr("#W bad bag id %d found, 0 saved\n" , (Int )subobj, 0 );
GAP_ASSERT(0 );
SaveUInt(0 );
}
}
Obj LoadSubObj( void )
{
UInt word = LoadUInt();
if (word == 0 )
return (Obj) 0 ;
if ((word & 0 x3) != 0 )
return (Obj) word;
else
return (Obj)RESTORE_BAG_CONTENT_POINTER(word >> 2 );
}
#endif
/***************************************************************************
* *
* * Bag level saving routines
*/
#ifdef USE_GASMAN
static void SaveBagData (Bag bag )
{
BagHeader * header = BAG_HEADER(bag);
SaveUInt1(header->type);
SaveUInt1(header->flags);
SaveUInt(header->size);
// dispatch
(*(SaveObjFuncs[ header->type]))(bag);
}
static void LoadBagData ( void )
{
Bag bag;
UInt type, flags, size;
// Recover the size & type
type = LoadUInt1();
flags = LoadUInt1();
size = LoadUInt();
if (TNAM_TNUM(type) == NULL)
Panic("Bad type %d, size %d\n" , (int )type, (int )size);
// Get GASMAN to set up the bag for me
bag = NextBagRestoring( type, flags, size );
// dispatch
(*(LoadObjFuncs[ type ]))(bag);
}
#endif
/***************************************************************************
* *
* F WriteSaveHeader ( ) . . . . . and utility functions , and loading functions
* *
*/
static void WriteEndiannessMarker( void )
{
UInt x;
#ifdef SYS_IS_64_BIT
x = 0 x0102030405060708L;
#else
x = 0 x01020304L;
#endif
SAVE_BYTE(((UInt1 *)&x)[0 ]);
SAVE_BYTE(((UInt1 *)&x)[1 ]);
SAVE_BYTE(((UInt1 *)&x)[2 ]);
SAVE_BYTE(((UInt1 *)&x)[3 ]);
#ifdef SYS_IS_64_BIT
SAVE_BYTE(((UInt1 *)&x)[4 ]);
SAVE_BYTE(((UInt1 *)&x)[5 ]);
SAVE_BYTE(((UInt1 *)&x)[6 ]);
SAVE_BYTE(((UInt1 *)&x)[7 ]);
#endif
}
static void CheckEndiannessMarker( void )
{
UInt x;
((UInt1 *)&x)[0 ] = LOAD_BYTE();
((UInt1 *)&x)[1 ] = LOAD_BYTE();
((UInt1 *)&x)[2 ] = LOAD_BYTE();
((UInt1 *)&x)[3 ] = LOAD_BYTE();
#ifdef SYS_IS_64_BIT
((UInt1 *)&x)[4 ] = LOAD_BYTE();
((UInt1 *)&x)[5 ] = LOAD_BYTE();
((UInt1 *)&x)[6 ] = LOAD_BYTE();
((UInt1 *)&x)[7 ] = LOAD_BYTE();
if (x != 0 x0102030405060708L)
#else
if (x != 0 x01020304L)
#endif
{
Panic("Saved workspace with incompatible byte order" );
}
}
/***************************************************************************
* *
* * FuncBagStats
*/
#ifdef USE_GASMAN
static FILE *file;
static void report( Bag bag)
{
fprintf(file,"%li %li\n" , (long ) TNUM_BAG(bag), (long ) SIZE_BAG(bag));
}
static Obj FuncBagStats(Obj self, Obj filename)
{
file = fopen((Char *)CHARS_STRING(filename),"w" );
CallbackForAllBags(report);
fclose(file);
return (Obj) 0 ;
}
#endif
/***************************************************************************
* *
* * Find Bags - - a useful debugging tool - - scan for a bag of specified
* * type and size and return it to the GAP level . Could be a problem
* * if the bag is not a valid GAP object - - eg a local variables bag or
* * a functions body .
*/
#ifdef USE_GASMAN
static UInt fb_minsize, fb_maxsize, fb_tnum;
static Bag hit;
static void ScanBag( Bag bag)
{
if (hit == (Bag)0 &&
SIZE_BAG(bag) >= fb_minsize &&
SIZE_BAG(bag) <= fb_maxsize &&
TNUM_BAG(bag) == fb_tnum)
hit = bag;
}
static Obj FuncFindBag(Obj self, Obj minsize, Obj maxsize, Obj tnum)
{
hit = (Bag) 0 ;
fb_minsize = INT_INTOBJ(minsize);
fb_maxsize = INT_INTOBJ(maxsize);
fb_tnum = INT_INTOBJ(tnum);
CallbackForAllBags(ScanBag);
return (hit != (Bag) 0 ) ? hit : Fail;
}
#endif
/***************************************************************************
* *
* F SaveWorkspace ( < fname > ) . . . . . save the workspace to the named file
* *
* * ' SaveWorkspace ' is the entry point to the workspace saving . It is not
* * installed as a GAP function , but instead as a keyword , so that we can be
* * sure it is only being called from the top - most prompt level
* * The file saveload . tex in the dev directory describes the saved format
* * in more detail . Most of the work will be done from inside GASMAN , because
* * we need to fiddle with Bag internals somewhat
* *
* * The return value is either True or Fail
*/
#ifdef USE_GASMAN
static UInt NextSaveIndex = 1 ;
static void AddSaveIndex( Bag bag)
{
LINK_BAG(bag) = (Obj)NextSaveIndex++;
}
static void RemoveSaveIndex( Bag bag)
{
LINK_BAG(bag) = bag;
}
#endif
static Char * GetKernelDescription(void )
{
static Char SyKernelDescription[256 ];
strcpy(SyKernelDescription, SyKernelVersion);
#ifdef HAVE_LIBREADLINE
if (SyUseReadline) {
strcat(SyKernelDescription, " with readline" );
}
#endif
return SyKernelDescription;
}
static void WriteSaveHeader( void )
{
UInt i;
SaveCStr("GAP workspace" );
SaveCStr(GetKernelDescription());
#ifdef SYS_IS_64_BIT
SaveCStr("64 bit" );
#else
SaveCStr("32 bit" );
#endif
WriteEndiannessMarker();
SaveCStr("Counts and Sizes" );
for (i = 0 ; i < GlobalBags.nr; i++) {
GAP_ASSERT(GlobalBags.cookie[i] != NULL);
}
SaveUInt(GlobalBags.nr);
SaveUInt(NextSaveIndex-1 );
SaveUInt(GASMAN_USED_MEMORY());
SaveCStr("Loaded Modules" );
SaveModules();
SaveCStr("Kernel to WS refs" );
for (i = 0 ; i < GlobalBags.nr; i++)
{
GAP_ASSERT(GlobalBags.cookie[i] != NULL);
SaveCStr((const Char *)GlobalBags.cookie[i]);
SaveSubObj(*(GlobalBags.addr[i]));
}
}
Obj SaveWorkspace( Obj fname )
{
Obj fullname;
Obj result;
if (!IsStringConv(fname))
ErrorQuit("usage: SaveWorkspace( <filename> )" ,0 ,0 );
// maybe expand fname starting with ~/...
fullname = Call1ArgsInNewReader(userHomeExpand, fname);
if (ModulesPreSave())
return Fail;
// Do a full garbage collection
CollectBags( 0 , 1 );
// Add indices in link words of all bags, for saving inter-bag references
NextSaveIndex = 1 ;
CallbackForAllBags( AddSaveIndex );
// Now do the work
result = Fail;
if (!OpenForSave( fullname ))
{
result = True ;
WriteSaveHeader();
SaveCStr("Bag data" );
SortHandlers( 1 ); // Sort by address to speed up CookieOfHandler
CallbackForAllBags( SaveBagData );
CloseAfterSave();
}
// Finally, reset all the link words
CallbackForAllBags( RemoveSaveIndex );
// Restore situation by calling all post-save methods
ModulesPostSave();
return result;
}
/***************************************************************************
* *
* F LoadWorkspace ( < fname > ) . . . . . load the workspace to the named file
* *
* * ' LoadWorkspace ' is the entry point to the workspace saving . It is not
* * installed as a GAP function , but instead called from InitializeGap when
* * the - L command - line flag is given
* *
* * The file saveload . tex in the dev directory describes the saved format
* * in more detail . Most of the work will be done from inside GASMAN , because
* * we need to fiddle with Bag internals somewhat
* *
*/
void LoadWorkspace( Char * fname )
{
UInt nGlobs, nBags, i, maxSize;
Char buf[256 ];
Obj * glob;
// Open saved workspace
OpenForLoad( fname );
// Check file header
LoadCStr(buf,256 );
if (strncmp (buf, "GAP " , 4 ) != 0 ) {
Panic("File %s does not appear to be a GAP workspace" , fname);
}
if (streq(buf, "GAP workspace" )) {
LoadCStr(buf,256 );
if (!streq(buf, GetKernelDescription())) {
Panic("This workspace is not compatible with GAP kernel (%s, present: "
"%s)" , buf, GetKernelDescription());
}
LoadCStr(buf,256 );
#ifdef SYS_IS_64_BIT
if (!streq(buf,"64 bit" ))
#else
if (!streq(buf,"32 bit" ))
#endif
{
Panic("This workspace was created by a %s version of GAP" , buf);
}
} else {
Panic("File %s probably isn't a GAP workspace" , fname);
}
CheckEndiannessMarker();
LoadCStr(buf,256 );
if (!streq(buf,"Counts and Sizes" ))
{
Panic("Bad divider" );
}
nGlobs = LoadUInt();
nBags = LoadUInt();
maxSize = LoadUInt();
// Make sure there is enough room, and signal GASMAN that
// we are starting a restore
StartRestoringBags(nBags, maxSize);
// The restoring kernel must have at least as many compiled modules
// as the saving one.
LoadCStr(buf,256 );
if (!streq(buf,"Loaded Modules" ))
{
Panic("Bad divider" );
}
LoadModules();
// Now the kernel variables that point into the workspace
LoadCStr(buf,256 );
if (!streq(buf,"Kernel to WS refs" ))
{
Panic("Bad divider" );
}
SortGlobals(); // globals by cookie for quick lookup
// TODO: the goal here is to stop exporting `GlobalBags` completely...
if (nGlobs != GlobalBags.nr) {
Panic("Wrong number of global bags in saved workspace %d %d" ,
(int )nGlobs, (int )GlobalBags.nr);
}
for (i = 0 ; i < nGlobs; i++) {
LoadCStr(buf, 256 );
glob = GlobalByCookie(buf);
if (glob == (Obj *)0 ) {
Panic(
"Global object cookie from workspace not found in kernel %s" ,
buf);
}
*glob = LoadSubObj();
if (SyDebugLoading)
Pr("Restored global %s\n" , (Int )buf, 0 );
}
LoadCStr(buf,256 );
if (!streq(buf,"Bag data" ))
{
Panic("Bad divider" );
}
SortHandlers(2 );
for (i = 0 ; i < nBags; i++)
LoadBagData();
FinishedRestoringBags();
CloseAfterLoad();
ModulesPostRestore();
}
static void PrSavedObj( UInt x)
{
if ((x & 3 ) == 1 )
Pr("Immediate integer %d\n" , INT_INTOBJ((Obj)x), 0 );
else if ((x & 3 ) == 2 )
Pr("Immediate FFE %d %d\n" , VAL_FFE((Obj)x), SIZE_FF(FLD_FFE((Obj)x)));
else
Pr("Reference to bag number %d\n" ,x>>2 , 0 );
}
static Obj FuncDumpWorkspace(Obj self, Obj fname)
{
UInt nMods, nGlobs, nBags, i, relative;
Char buf[256 ];
OpenForLoad( CONST_CSTR_STRING(fname) );
LoadCStr(buf,256 );
Pr("Header string: %s\n" , (Int )buf, 0 );
LoadCStr(buf,256 );
Pr("GAP Version: %s\n" , (Int )buf, 0 );
LoadCStr(buf,256 );
Pr("Word length: %s\n" , (Int )buf, 0 );
CheckEndiannessMarker();
LoadCStr(buf,256 );
Pr("Divider string: %s\n" , (Int )buf, 0 );
if (!streq(buf, "Counts and Sizes" ))
ErrorQuit("Bad divider" , 0 , 0 );
Pr("Loaded modules: %d\n" , nMods = LoadUInt(), 0 );
Pr("Global Bags : %d\n" , nGlobs = LoadUInt(), 0 );
Pr("Total Bags : %d\n" , nBags = LoadUInt(), 0 );
Pr("Maximum Size : %d\n" , sizeof (Bag) * LoadUInt(), 0 );
LoadCStr(buf,256 );
Pr("Divider string: %s\n" , (Int )buf, 0 );
if (!streq(buf, "Loaded Modules" ))
ErrorQuit("Bad divider" , 0 , 0 );
for (i = 0 ; i < nMods; i++)
{
UInt type;
type = LoadUInt();
Pr("Type: %d " ,type,0 );
relative = LoadUInt();
if (relative)
Pr("GAP root relative " , 0 , 0 );
else
Pr("absolute " , 0 , 0 );
LoadCStr(buf,256 );
Pr(" %s\n" ,(Int )buf, 0 );
}
LoadCStr(buf,256 );
Pr("Divider string: %s\n" , (Int )buf, 0 );
if (!streq(buf, "Kernel to WS refs" ))
ErrorQuit("Bad divider" , 0 , 0 );
for (i = 0 ; i < nGlobs; i++)
{
LoadCStr(buf,256 );
Pr(" %s " , (Int )buf, 0 );
PrSavedObj(LoadUInt());
}
LoadCStr(buf,256 );
Pr("Divider string: %s\n" , (Int )buf, 0 );
if (!streq(buf, "Bag data" ))
ErrorQuit("Bad divider" , 0 , 0 );
CloseAfterLoad();
return (Obj) 0 ;
}
#endif // GAP_ENABLE_SAVELOAD
static Obj FuncSaveWorkspace(Obj self, Obj filename)
{
#ifdef GAP_ENABLE_SAVELOAD
return SaveWorkspace(filename);
#else
ErrorMayQuit("SaveWorkspace is only supported when GASMAN is in use" , 0 , 0 );
return Fail;
#endif
}
/****************************************************************************
* *
* F * * * * * * * * * * * * * initialize module * * * * * * * * * * * * * * *
*/
/****************************************************************************
* *
* V GVarFuncs . . . . . . . . . . . . . . . . . . list of functions to export
*/
static StructGVarFunc GVarFuncs [] = {
GVAR_FUNC_1ARGS(SaveWorkspace, fname),
#ifdef GAP_ENABLE_SAVELOAD
GVAR_FUNC_1ARGS(DumpWorkspace, fname),
#ifdef USE_GASMAN
GVAR_FUNC_3ARGS(FindBag, minsize, maxsize, tnum),
GVAR_FUNC_1ARGS(BagStats, filename),
#endif
#endif
{ 0 , 0 , 0 , 0 , 0 }
};
/****************************************************************************
* *
* F InitKernel ( < module > ) . . . . . . . . initialise kernel data structures
*/
static Int InitKernel (
StructInitInfo * module )
{
#ifdef GAP_ENABLE_SAVELOAD
SaveFile = -1 ;
LBPointer = LoadBuffer;
LBEnd = LoadBuffer;
// allow ~/... expansion in SaveWorkspace
ImportFuncFromLibrary("UserHomeExpand" , &userHomeExpand);
#endif
// init filters and functions
InitHdlrFuncsFromTable( GVarFuncs );
return 0 ;
}
/****************************************************************************
* *
* F InitLibrary ( < module > ) . . . . . . . initialise library data structures
*/
static Int InitLibrary (
StructInitInfo * module )
{
// init filters and functions
InitGVarFuncsFromTable( GVarFuncs );
return 0 ;
}
/****************************************************************************
* *
* F InitInfoSaveLoad ( ) . . . . . . . . . . . . . . . table of init functions
*/
static StructInitInfo module = {
// init struct using C99 designated initializers; for a full list of
// fields, please refer to the definition of StructInitInfo
.type = MODULE_BUILTIN,
.name = "saveload" ,
.initKernel = InitKernel,
.initLibrary = InitLibrary,
};
StructInitInfo * InitInfoSaveLoad ( void )
{
return &module;
}
Messung V0.5 in Prozent C=96 H=98 G=96
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland