Below is a help file for a utility I am developing as an upgrade on lib
datafile. Comments please. Robin.
------------------------------------------------------------------------------
HELP restore_data, save_data Robin Popplestone MAR 1994
This text may be freely copied provided the above attribution is preserved.
CONTENTS - (Use <ENTER> g to access required sections)
-- How to use these facilities
-- class_save class_restore class_update - user defined "methods"
-- Writing the -save- procedure
-- The format of the saved data
-- An Example - saving a circular list.
-- Limitations - no saving of widgets, external data and some procedures
-- restore_data provides more capabilities than datafile
How to use these facilities
---------------------------
save_data allows many Pop data structures, including "circular" structures, to
be recorded on backing store, and to be read back. It thus extends the scope
of LIB * DATAFILE. To write a structure to backing store type:
save_data(<struct>, <filename>);
Similarly, to read a structure back from backing store, type:
restore_data(<filename>) -> <struct>;
the (default) permitted datatypes are:
words, numbers, lists, vector types, record types,
vector arrays, ordinary properties, booleans and closures
A named procedure can be "saved"; what this means is that its name is
written to the file on saving. On restoration the procedure with the
same (global) name is restored, using valof. save_data checks that
the name of the procedure in its -pdprops- is associated with the
procedure itself as value.
A similar approach is adopted for -key- objects. The dataword is written
and the corresponding key is regenerated.
class_save class_restore class_update - user defined "methods"
---------------------------------------------------------------
Where a structure may contain sub-structures which may not be saved
by the default -save_data- procedure, for example because it contains
external data, or widgets, or anonymous procedures, or if it contains
information that it is not necessary to save, such as a big image taken
from a library, then then user can define her own "methods" using an analog
of the simple built in class_... procedures, which work in a manner analogous
to class_print (see ref print).
For any data class, with data-key Key, which is to be saved by a user-defined
procedure, write a procedure P_save (say) to save, members of that class, a
procedure P_restore to restore class-members and a procedure P_update to
update members as described below. Then, making the assignments
section $-SAVE_DATA;
...... code for P_save etc.
P_save -> class_save(Key);
P_restore -> class_restore(Key);
P_update -> class_update(Key);
endsection;
will ensure that members of that class can be saved and restored.
Writing the -save- procedure
------------------------------
This procedure should write out enough components of the data-structure
to allow it to be restored by the user-written restore procedure. Layout
is not prescribed, but compound components will normally be written
using the -save_field- procedure. This will typically write just an index
number for a compound component, as described in the -format- section
below. -save_field- must be used if there is any possibility of
circularity. The -dataword- will be written out -before- the -save- procedure
is called, and a semicolon will be written after. For example:
define save_Image(Im);
lvars W = Widget_Image(Im);
undef -> Widget_Image(Im);
appdata(Im,save_field);
W -> Widget_Image(Im);
enddefine;
Here we simply null out the Widget_Image field, knowing we will have
enough information to recreate it on restoration. (as it happens).
Writing the -restore- and -update- procedures
-----------------------------------------------
These both read data written by the corresponding -save- procedure. The
save file is scanned twice. In the first scan -restore- procedures are
called to build the data-structures, in the second forward references
are updated.
Thus the -restore- procedure will normally read and stack components
of the structure and then call the class-constructor. The procedure
-restore_field- is available to read each component.
The format of the saved data
-----------------------------
The whole saved file is described by the syntax:
<saved_file> -> SavedData <version> <field> <objects>
<version> -> V1
Here the <version> specifies which version of save_data was used to save
the data. If the file does not begin with SavedData, the older datafile
capability is assumed to have been used.
A <field> is a specification of a POP item (record, vector etc.) which
is either self-contained or is the index referring to a line in the
file where the item actually "is", i.e. a kind of in-file pointer. Simple
items and certain compound items are self-contained.
<field> -> (<index>) Pointer to the item in line labelled <index>
-> "<string>" The word made from the string.
-> <string> A POP-11 string.
-> <word> A POP-11 variable - take the valof.
The <word> option is used for named procedures and certain system constants
(e.g. false).
In the <saved_file> syntax, the <field> specifies the actual item that
has been saved. This distinguishes it from its sub-items, since it will
not necessarily have any particular index. A simple item will have no
<objects> following it. Thus the number 654 is saved as:
SavedData V1
654
However, normally we have <objects>
<objects> -> <object> <objects>
-> <null>
where <null> is the grammar which generates just the null sequence.
<object> -> <index> <dataword> <fields> ;
-> <index> %a <array>
-> <index> %c <closure>
-> <index> %p <property>
An array is written out as specified by the following syntax. Here
the two <int> values are the arrayvector_bounds of the array. No
updater is written as it is illegal to change the updater of an array.
<array> -> <boundslist> <vector> <subscr> <int> <int> <byrow> <props>
<boundslist> -> [ <int> <int> .... ]
<vector> -> <field> ;;; The vector of values
<subscr> -> <field> ;;; Subscripting procedure
<byrow> -> true | false ;;; storage by row?
<props> -> <field> ;;; The pdprops
A closure is written out as:
<closure> -> <pdpart> <pdprops> <updater>
Here all three components are written out as fields.
A property is written out as:
<property> -> <alist> <size> <default> <props> <updater>
<alist> -> <field> ;;; The association list of the property
<size> -> <int> ;;; The size of the hash table
<default> -> <field> ;;; the value to be returned by default
<props> -> <field> ;;; The -pdprops- field of the property.
<updater> -> <field> ;;; The -updater- of the property.
;;; This is -false- if the standard updater
;;; is in use.
An Example - saving a circular list.
-----------------------------------
The circular list created by:
vars circular = [44 fred 'ab' [55 66] last];
circular -> circular.tl.tl.tl.tl.tl;
save_data(circular,'circ.tmp');
creates the following file:
SavedData V1
(1)
1 pair 44 (2);
2 pair "'fred'"(3);
3 pair 'ab'(4);
4 pair (5)(6);
5 pair 55 (7);
6 pair "'last'"(1);
7 pair 66 nil ;
Limitations - no saving of widgets, external data and some procedures
---------------------------------------------------------------------
Apart from the inability to save anonymous procedures, widgets and external
data, there are other limitations.
(1) This facility only handles properties that can be created with
-newproperty- and assumes that any properties are -permanent-.
(2) Properties which are circular in the sense that they associate themselves
with some other data, for example P below:
P -> P(key)
must NOT be saved - an infinite loop will result. It is anticipated that
this will occur rather seldom.
(3) Apart from arrays and properties, no procedure is -truly- saved, since
its name only is written. This is probably the most useful capability,
since, provided there is no change in the specification of procedures,
data should be reusable with a new version of a program.
(4) The updater of properties is not saved.
restore_data provides more capabilities than datafile
-----------------------------------------------------
The scope is extended primarily to include circular structures. In addition
double precision floating point numbers ("ddecimals") are written with
the appropriate number of significant digits to be accurately restored.
Two separate procedures are provided for saving and restoring since it
is anticipated that there will be applications where only one of these is
required.
A simple way of defining save and restore "methods" is provided.
Use with lib objectclass
------------------------
These procedures will save objects generated with lib objectclass, and
will restore them, provided that the specification of object classes
remains constant between saving and restoring. The "methods" draw on the
older paradigm of class_... procedures rather than being methods within
lib objectclass, since this library is not intended to require lib objectclass
to be loaded.
|