/* The format is largely equal to that of Pluto, with some changes to reduce file size, and where necessary due to changes in Lua's architecture. Pseudo-C is used to express the file format. Padding is assumed to be nonexistent. The keyword "one_of" is used to express a concept similar to "union", except that its size is the size of the actual datatype chosen. Thus, objects which contain, directly or indirectly, a one_of, may vary in size. The keyword "if" is used to express the fact that the following block may or may not be present, usually indicated by the type of a previous "one_of". */ struct PersistedData { Header header; /* The header used for basic validation. */ Object rootobj; /* The root object that was persisted. */ }; struct Header { char header[4] = "ERIS"; /* Header signature for rudimentary validation */ uint8_t sizeof_number; /* sizeof(lua_Number) to check type compatibility */ lua_Number test; /* -1.234567890 to check representation compatibility */ uint8_t sizeof_int; /* sizeof(int) in persisted data */ uint8_t sizeof_size_t; /* sizeof(size_t) in persisted data */ /* Note that the last two fields determine the size of the int and size_t * fields in the following definitions. We write each value in the native * "size" and check for truncation when reading, if necessary. */ }; struct Object { one_of { int type; /* if the value is one of LUA_TXXX or ERIS_PERMANENT */ Reference r; /* otherwise */ /* Note that the types LUA_TNIL, LUA_TBOOLEAN, LUA_TNUMBER and * LUA_TLIGHTUSERDATA will never be "referenced", but always be written * directly. */ } if (type) { RealObject o; /* if we have a type, not a reference */ /* If the object is not primitive (see list above) we remember it and * increment the reference counter, and point any future occurrences of * it to this one via a reference (see above, Reference r). */ } }; struct Reference { int ref; /* The index the object was registered with */ }; struct RealObject { one_of { uint8_t b; /* If type == LUA_TBOOLEAN */ size_t l; /* If type == LUA_TLIGHTUSERDATA */ Number n; /* If type == LUA_TNUMBER */ String s; /* If type == LUA_TSTRING */ Table t; /* If type == LUA_TTABLE */ Closure f; /* If type == LUA_TFUNCTION */ Userdata u; /* If type == LUA_TUSERDATA */ Thread th; /* If type == LUA_TTHREAD */ Proto p; /* If type == LUA_TPROTO (from lobject.h) */ UpVal uv; /* If type == LUA_TUPVAL (from lobject.h) */ PermKey pk; /* if type == ERIS_PERMANENT */ }; }; struct Number { if(sizeof(lua_Number) == sizeof(uint32_t)) { uint32_t rep; /* binary representation of the number, stored as * integer to re-use endian-agnosticism */ } else if (sizeof(lua_Number) == sizeof(uint64_t)) { uint64_t rep; /* binary representation of the number, stored as * integer to re-use endian-agnosticism */ } else { /* unsupported float type -- error if asserts are enabled */ } }; struct String { size_t length; /* The length of the string */ char str[length]; /* The actual string (not always null terminated) */ }; struct Table { uint8_t isSpecial; /* 1 if SP is used; 0 otherwise */ one_of { Object c; /* if isspecial == 1; closure to refill the table */ LiteralTable t; /* if isspecial == 0; literal table info */ }; }; struct LiteralTable { Pair p[]; /* key/value pairs */ Object nil = nil; /* Nil reference to terminate */ Object metatable; /* The metatable (nil for none, otherwise LUA_TTABLE) */ }; struct Pair { Object key; /* never nil, since that indicates the end */ Object value; /* never nil, since the entry wouldn't exist then */ }; struct Userdata { uint8_t isSpecial; /* 1 for special persistence, 0 for literal */ one_of { Object c; /* if isspecial == 1; closure to recreate the udata */ LiteralUserdata lu; /* if is_special is 0 */ }; }; struct LiteralUserdata { size_t length; /* Size of the data */ char data[length]; /* The actual data */ Object metatable; /* The metatable (nil for none, otherwise LUA_TTABLE) */ }; struct Closure { uint8_t isCClosure; /* 1 if the closure is a C closure; 0 otherwise */ uint8_t nups; /* Number of upvalues the function uses */ one_of { CClosure ccl; /* if isCClosure == 1 */ LClosure lcl; /* if isCClosure == 0; it's a Lua closure */ }; }; struct CClosure { Object f; /* The actual C function. Must be available via the * permanents table on persist and unpersist. */ Object upvals[Closure.nups]; /* All upvalues */ /* Note that here the upvalues are the actual objects, i.e. these are not * of type LUA_TUPVAL, since C closures' upalues are always closed. */ }; struct LClosure { Object proto; /* The proto this function uses */ Object upvals[Closure.nups]; /* All upvalues */ }; struct UpVal { Object obj; /* The object this upval refers to; we proxy it with * the LUA_TUPVAL type to keep shared upvalues intact */ } struct Proto { int linedefined; /* Start of line range */ int lastlinedefined; /* End of line range */ uint8_t numparams; /* Number of parameters taken */ uint8_t is_vararg; /* 1 if function accepts varargs, 0 otherwise */ uint8_t maxstacksize; /* Size of stack reserved for the function */ int sizecode; /* Number of instructions in code */ Instruction code[sizecode]; /* The proto's code */ int sizek; /* Number of constants referenced */ Object k[sizek]; /* Constants referenced */ int sizep; /* Number of inner Protos referenced */ Object p[sizep]; /* Inner Protos referenced */ int sizeupvalues; /* Number of upvalues used */ Upvaldesc upvalues[sizeupvalues]; /* The locations of the upvalues */ uint8_t debug; /* 1 if debug data is present; 0 otherwise */ if (debug) { Object source; /* The source code string */ int sizelineinfo; /* Number of opcode-line mappings */ int lineinfo[sizelineinfo]; /* opcode-line mappings */ int sizelocvars; /* Number of local variable names */ LocVar[sizelocvars]; /* Local variable names */ Object upvalnames[sizeupvalues]; /* Upvalue names */ } }; struct Upvaldesc { uint8_t instack; /* whether it is in stack */ uint8_t idx; /* index of upvalue (in stack or in outer function's list) */ }; struct LocVar { int startpc; /* Point where variable is active */ int endpc; /* Point where variable is dead */ Object name; /* Name of the local variable */ }; struct Thread { int stacksize; /* The overall size of the stack filled with objects, * including all stack frames. */ size_t top; /* top = L->top - L->stack; */ Object stack[stacksize]; /* All stack values, bottom up */ uint8_t status; /* current thread status (ok, yield) */ size_t errfunc; /* current error handling function (stack index) */ CallInfo ci[]; /* The CallInfo stack, starting with base_ci */ if (status == LUA_YIELD) { size_t extra; /* value of thread->ci->extra, which is the original * value of thread->ci->func */ } OpenUpval openupvals[]; /* Upvalues to open */ }; struct CallInfo { size_t func; /* func = ci->func - thread->stack */ size_t top; /* top = ci->top - thread->stack */ int16_t nresults; /* expected number of results from this function */ uint8_t callstatus; if (callstatus & CIST_YPCALL) { size_t extra; /* the stack level of the function being pcalled */ } if (callstatus & CIST_LUA) { size_t base; /* base = ci->u.l.base - thread-stack */ size_t savedpc; /* savedpc = ci->u.l.savedpc - ci_func(ci)->p->code */ } else { uint8_t status; if (callstatus & (CIST_YPCALL | CIST_YIELDED)) { int ctx; /* context info. in case of yields */ Object k; /* C function, callback for resuming */ } } uint8_t hasNext; /* 1 if there's another CI to read; 0 otherwise */ }; struct OpenUpval { size_t idx; /* stack index of the value + 1; 0 if end of list */ Object upval; /* The upvalue */ }; struct PermKey { uint8_t type; /* The actual LUA_TXXX of the original value. */ Object key; /* The value to use as a key when unpersisting. */ /* Note that we store the type of the original value (replaced by the * permanent table value used as a key when unpersisting) to ensure the * value in the permanents table when unpersisting has the correct type. */ };