[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: ADT Lib: Persistence
> [...]
> It probably also makes sense to have special riders in StdTypes.
I agree. Fortunately, we can integrate this rather nicely with OOC's
library, by creating extensions of BinaryRider.Reader/Writer, which
have the methods ReadObject and WriteObject (see attached module
AcObject, formerly known as StdObjects). Then Load/Store work with
the abstract classes ObjectReader and ObjectWriter.
Any concrete implementation of these abstract classes also store the
context for the externalize and interalize process, removing the need
for global variables, or an additional context.
> Special functions provided by Stores.Readers are:
> - PROCEDURE (VAR rd : Reader) TurnIntoAlien (cause : INTEGER)
> This causes the currently Internalized store to become Alien.
> - PROCEDURE (VAR rd : Reader) ReadVersion (min, max: INTEGER; OUT version:
> INTEGER)
> This reads a version identifier and cancels the current Internalize if it
> is not between <min> and <max>.
These will be part of AcObject.ObjectReader (they are not part of the
attached sample code yet).
> - a boolean variable "cancelled", which indicates that an Internalize has
> been cancelled. A smart Internalize function will periodically check its
> reader's cancelled status to see if it should exit without completing the
> entire process. Normally this is caused by (1) an inappropriate version
> number, (2) the Store being turned into an Alien.
> - a boolean variable "readAlien" which is true if an Alien has been read
> since the Reader was connected to a File.
These would be part of an implementation of AcObject.ObjectReader, but
should they be part of the abstract class, too? It makes sense, that
`Load' can abort after a `cancel', so `canceled' should appear in the
abstract class. I would rather try to keep `readAlien' out of
AcObject, though.
>
> Special functions provided by Stores.Writers are:
> - PROCEDURE WriteVersion (version: INTEGER)
A method of AcObject.ObjectWriter.
A nice thing about my current outline is, that it moves the algorithm
of writing the objects into a separate class. Except for
`Object.mark', all remnants of a particular externalization algorithm
have been moved out of `AcObject'. (I would like to get rid of `mark'
altogther.)
The attached module `Storage' (got any better name?) is a sketch of an
implementation of the "simple" externalization. Too externalize a
bunch of objects, one would
- call Storage.ConnectWriter to get the writer
- call writer.WriteObject(obj) for all objects in the root set
- call writer.Disconnect to finish and clean up things
We can even have multiple externalization formats, e.g. an additional
one dealing with libraries. Well, it is an idea, don't know if it is
feasible.
> How widely available is mmap() under Unix?
It doesn't appear in the index of the glibc1 docs, but `man mmap' on
my Linux box claims
CONFORMING TO
SVr4, POSIX.1b (formerly POSIX.4), 4.4BSD. Svr4 documents
additional error codes ENXIO and ENODEV.
Since it is POSIX, it is likely present in most recent implementations
of Unix.
-- mva
------------------------------------------------------------------------
MODULE AcObject;
IMPORT
BinaryRider;
TYPE
Object* = POINTER TO ObjectDesc;
ObjectDesc* = RECORD
mark*: LONGINT; (* used by persistence mechanism; DO NOT TOUCH! *)
END;
TYPE (* abstract types, used for persistence mechanism (see Load/Store) *)
ObjectReader* = POINTER TO ObjectReaderDesc;
ObjectReaderDesc* = RECORD
(BinaryRider.ReaderDesc)
END;
ObjectWriter* = POINTER TO ObjectWriterDesc;
ObjectWriterDesc* = RECORD
(BinaryRider.WriterDesc)
END;
PROCEDURE Init* (obj: Object);
BEGIN
obj. mark := 0
END Init;
PROCEDURE Destroy* (obj: Object);
BEGIN
obj. mark := 0
END Destroy;
PROCEDURE (obj: Object) Store* (w: ObjectWriter);
(* Stores data of `obj' to `w'. Nested record pointers are stored by calling
WriteObject.
pre: This procedure is either activated by a super call, or from the
procedure `WriteObject'. *)
BEGIN
END Store;
PROCEDURE (obj: Object) Load* (r: ObjectReader);
(* Loads data of `obj' from `r'.
pre: This procedure is either activated by a super call, or from the
procedure `ReadObject'. *)
BEGIN
END Load;
PROCEDURE (w: ObjectWriter) WriteObject* (obj: Object);
BEGIN
END WriteObject;
PROCEDURE (w: ObjectReader) ReadObject* (VAR obj: Object);
BEGIN
END ReadObject;
END AcObject.
------------------------------------------------------------------------
MODULE Storage;
IMPORT
Channel, Kernel, Types, AcObject;
TYPE
ObjectReader* = POINTER TO ObjectReaderDesc;
ObjectReaderDesc* = RECORD
(AcObject.ObjectReaderDesc)
objCounter: LONGINT;
nodeTab: ARRAY 999(*this should be an open array*) OF AcObject.Object;
END;
ObjectWriter* = POINTER TO ObjectWriterDesc;
ObjectWriterDesc* = RECORD
(AcObject.ObjectWriterDesc)
objCounter: LONGINT;
nodeTab: ARRAY 999(*this should be an open array*) OF AcObject.Object;
END;
CONST
unmarked = 0; (* used by WriteObject *)
PROCEDURE ConnectReader* (ch: Channel.Channel): ObjectReader;
VAR
r: ObjectReader;
BEGIN
NEW (r);
(* InitReader currently not exported by BinaryRider...
BinaryRider.InitReader (r, ch, BinaryRider.littleEndian);*)
r. objCounter := 0;
r. nodeTab[0] := NIL;
IF (r. base = NIL) THEN
RETURN NIL
ELSE
RETURN r
END
END ConnectReader;
PROCEDURE ConnectWriter*(ch: Channel.Channel): ObjectWriter;
VAR
w: ObjectWriter;
BEGIN
NEW (w);
(* InitWriter currently not exported by BinaryRider...
BinaryRider.InitWriter (w, ch, BinaryRider.littleEndian);*)
w. objCounter := 0;
w. nodeTab[0] := NIL;
IF (w. base = NIL) THEN
RETURN NIL
ELSE
RETURN w
END
END ConnectWriter;
PROCEDURE (r: ObjectReader) ReadObject* (VAR obj: AcObject.Object);
VAR
mark: LONGINT;
name: ARRAY 256(*should be unlimited*) OF CHAR;
module: Kernel.Module;
type: Types.Type;
BEGIN
r. ReadNum (mark);
IF (mark <= 0) THEN
obj := r. nodeTab[mark]
ELSE
r. ReadString (name);
module := Kernel.modules;
WHILE (module. name^ # name) DO
module := module. next
END;
(* for now: trap if `name' does not exist *)
r. ReadString (name);
type := Types.This (module, name);
(* for now: trap if `name' does not exist *)
Types.NewObj (obj, type);
r. nodeTab[mark] := obj;
obj. Load (r)
END
END ReadObject;
PROCEDURE (w: ObjectWriter) WriteObject* (obj: AcObject.Object);
VAR
type: Types.Type;
BEGIN
IF (obj = NIL) THEN
w. WriteNum (0)
ELSIF (obj. mark # unmarked) THEN
w. WriteNum (-obj. mark)
ELSE (* first occurence of this object *)
INC (w. objCounter);
obj. mark := w. objCounter;
w. WriteNum (w. objCounter);
type := Types.TypeOf (obj);
w. WriteString (type. module. name^);
w. WriteString (type. name^);
obj. Store (w)
END
END WriteObject;
END Storage.