Oberon || Library || Module Index || Search Engine || Definition || Module

Ulm's Oberon Library:


PersistentObjects - abstraction for persistent objects


CONST fullTypeName = 1; typeCode = 2; incrTypeCode = 3;
CONST withSize = 4; withoutSize = 0;
CONST withHier = 8; withoutHier = 0;

TYPE Object = POINTER TO ObjectRec; TYPE ObjectRec = RECORD (Services.ObjectRec) END;

TYPE ReadProc = PROCEDURE (s: Streams.Stream; o: Object) : BOOLEAN; TYPE WriteProc = PROCEDURE (s: Streams.Stream; o: Object) : BOOLEAN; TYPE CreateProc = PROCEDURE (VAR o: Object); TYPE CreateAndReadProc = PROCEDURE (s: Streams.Stream; create: BOOLEAN; VAR o: Object) : BOOLEAN; TYPE Interface = POINTER TO InterfaceRec; TYPE InterfaceRec = RECORD (Objects.ObjectRec) create: CreateProc; (* create object *) read: ReadProc; (* read data from stream *) write: WriteProc; (* write data to stream *) createAndRead: CreateAndReadProc; (* replaces create & read *) END;

CONST cannotReadData = 0; CONST cannotWriteData = 1; CONST cannotReadType = 2; CONST cannotWriteType = 3; CONST invalidType = 4; CONST unknownType = 5; CONST otherTypeHier = 6; CONST eofReached = 7; CONST cannotSkip = 8; CONST typeGuardFailure = 9; CONST errorcodes = 10; TYPE ErrorCode = SHORTINT; TYPE Event = POINTER TO EventRec; TYPE EventRec = RECORD (Events.EventRec) stream: Streams.Stream; errorcode: ErrorCode; END; VAR errormsg: ARRAY errorcodes OF Events.Message; VAR error: Events.EventType;

PROCEDURE RegisterType(VAR type: Services.Type; name, baseName: ARRAY OF CHAR; if: Interface); PROCEDURE Init(object: Object; type: Services.Type); PROCEDURE SetMode(s: Streams.Stream; mode: Mode); PROCEDURE GetMode(s: Streams.Stream; VAR mode: Mode); PROCEDURE IsProjected(object: Object) : BOOLEAN;

PROCEDURE Read(s: Streams.Stream; VAR object: Object) : BOOLEAN; PROCEDURE Write(s: Streams.Stream; object: Object) : BOOLEAN; PROCEDURE ReadObjectOrNIL(s: Streams.Stream; VAR object: Object) : BOOLEAN; PROCEDURE WriteObjectOrNIL(s: Streams.Stream; object: Object) : BOOLEAN; PROCEDURE ReadInto(s: Streams.Stream; object: Object) : BOOLEAN; PROCEDURE GuardedRead(s: Streams.Stream; guard: Services.Type; VAR object: Object) : BOOLEAN; PROCEDURE GuardedReadObjectOrNIL(s: Streams.Stream; guard: Services.Type; VAR object: Object) : BOOLEAN;


PersistentObjects defines an extensible abstraction for persistent objects and implements input and output operations for them (i.e. transforming the object into a sequence of bytes and vice versa). A persistent object is a data structure whose existence transcends time (i.e. the object continues to exist after its creator ceases to exist) and/or space (i.e. the object's location moves from the address space in which it was created).

Defining persistent objects

An interface defines a set of procedures which implement a PersistentObjects-abstraction for a specific extension of PersistentObjects.Object. Not all interface procedures must be given, some of them may be set to NIL:

The interface procedures should meet following specification:

create: PROCEDURE(VAR o: Object);
create (via NEW) a new object of the specific type and perform the type-specific initializations for that object which includes the call of Init for it. Note that Services.Init is called by Init and that initializations of extensions between PersistentObjects.Object and the specific extension should be done in the extension order after calling Init.

read: PROCEDURE(s: Streams.Stream; o: Object) : BOOLEAN;
read the extension-specific data of o from s. Note that one call of PersistentObjects.Read causes read to be called for each non-abstract extension between PersistentObjects.Object and the type of the object.

write: PROCEDURE(s: Streams.Stream; o: Object) : BOOLEAN;
write the extension-specific data of o to s. Note that if createAndRead is specified instead of create and read, write must write the data of all extensions and not only that of the specific extension.

createAndRead: PROCEDURE(s: Streams.Stream; create: BOOLEAN; VAR o: Object) : BOOLEAN;
perform the operations of create (if requested) and read. Note that in difference to read the interface procedure createAndRead is responsible for reading the whole object covering all extensions.

In some cases there exist several variants for the interface procedures write and read to save and load extension-specific data, e.g. a deep copy with all depending objects, or a copy of the object only with further references exported (see RemoteObjects). Additional parameterization may be achieved by attaching disciplines (see Disciplines) to the output stream and by examining them in the write interface procedure. But please note that PersistentObjects is free to pass temporary in-memory streams to the interface procedures. To allow discipline access in this case PersistentObjects links the temporary stream via IndirectDisciplines.Forward to the original stream. Thus, IndirectDisciplines.Seek has to be used instead of Disciplines.Seek to find additional parameters inside of the interface procedures.

The interface procedures should read and write Oberon base types by the use of NetIO. This assures that the exchange of persistent objects is even possible between different hardware architectures. The interface procedures are free to call Read and Write for persistent subobjects. In case of deep copies of possibly circular data structures the use of LinearizedStructures is recommended.

RegisterType is to be called during the initialization time by modules which extend PersistentObjects.Object. The type names to be given should be of the form "ModuleName.TypeName" (where ModuleName is the name of the module the data type is defined in, and TypeName is the name of the data type) to assure uniqueness and to support dynamic loading. If the data type of an object is a direct extension of PersistentObjects.Object, "" is to be given as baseName. Types which have createAndRead as interface procedure must not be given as base type.

Init connects a newly created object with its data type given by type. Note that Init calls Services.Init for object.

Using persistent objects

Persistent objects may be saved and loaded in dependence of a stream-specific mode. By default, each object saved is accompanied by a full type description which includes the whole type hierarchy of that object. This allows each object to be read in independently from others and makes projections possible. Projections are necessary if one of the modules which belongs to the type hierarchy is not present and cannot be loaded dynamically.

For many applications, however, this full type information takes too much space and needs too long to be read in. PersistentObjects supports an incremental type coding mode which leads to very compact coding for object sequences with repeating types. But this requires that objects are read in the same sequence as they have been written earlier.

Modes are to be given as the sum of three integer constants which select three different submodes:

The default mode is fullTypeName + withSize + withHier, a more efficient variant for sequential access is incrTypeCode + withSize + withHier, and the most efficient variant which still supports exchangeability with other programs but prohibits projections is incrTypeCode + withoutSize + withoutHier.

SetMode sets the mode for s and GetMode returns the mode of s which is currently in use. Note that the mode affects write operations only. IsProjected allows to test whether object was projected during its read operation or not.

Write converts object into a sequence of bytes which is written to s which may be later read in by Read. Note that Write requires object to be non-NIL and that Read guarantees object to be non-NIL on success.

GuardedRead works like Read but applies a type guard to the object read in and returns FALSE if the read object is not an extension of guard. Note that in case of type guard failures the read object is not assigned to object to avoid hard type guard failures (i.e. those which would lead to runtime errors which at least abort the current coroutine). To differentiate between type guard failures (which leave the stream at a defined position) and I/O errors (which possibly leave the stream at an undefined position) it may be useful to examine the error events which have been passed to s.

WriteObjectOrNIL and ReadObjectOrNIL work like Write and Read but allow NIL to be passed and to be returned. Note that ReadObjectOrNIL returns TRUE even when NIL has been successfully passed. GuardedReadObjectOrNIL works like GuardedRead but allows like ReadObjectOrNIL NIL to be returned. Because NIL may always be assigned to an object reference, the type test is skipped in case of NIL and TRUE is returned. Note that read and write operations must be properly paired, i.e. the XXXOrNIL operations are not compatible to the other operations.

ReadInto allows to avoid the creation of a new object by reading into an already existing object. This requires the type of the object to be read in to be an extension of object. ReadInto is compatible to Write only.


All read and write operations return FALSE in case of errors and generate an error event in that case which is related to the given stream. Following error codes are implemented:
while it was possible to read and decode the type, a read error occurred during reading of the object data, i.e. one of the read interface procedures returned FALSE. Note that reading is immediately aborted in such a case which leaves the stream at an undefined position (possibly inside of an object).
while it was possible to encode and write the type information, a write error occurred during writing of the object data, i.e. one of the write interface procedures returned FALSE. Note that writing is immediately aborted in such a case which leaves the stream at an undefined position.
is returned in case of failed stream operations only during reading the type information.
a write operation for the underlying stream failed.
bogus input was found which does not conform to a valid type information. This may happen due to undefined stream positions or due to incompatible versions of PersistentObjects. Another common source of this problem are unpaired read and write operations (e.g. writing an object with WriteObjectOrNIL but trying to read it with Read).
a valid type information was found which, however, is not known or supported by the reading program and does not permit projections. This leaves the stream at a defined position only if withSize was given at the time of writing.
is returned in case of incremental type informations which include type hierarchies if inconsistencies have been found. Inconsistencies may result from non-sequential write or read operations, or from different writing sources.
end of file was unexpectedly encountered. This may be returned during reading of the type information or during the reading of the object data.
a projection was attempted and failed projections were prohibited (due to missing size information) or the non-supported data parts could not be skipped. Nevertheless, the so far created and read object is returned in object despite the return value of FALSE.
is returned by GuardedRead or GuardedReadObjectOrNIL if the read object is not an extension of guard, or by ReadInto if the object to be read has not a type which is an extension of that of object.

Several errors which result from programming mistakes are covered by assertions:


simplified support of PersistentObjects for constant objects
collections of persistent objects
attachment of non-persistent data structures
shared disciplines
I/O of possibly circular data structures
distribution of graphs of persistent objects across multiple byte sequences
I/O of Oberon base types
persistent disciplines
support of persistent events
persistent objects representing texts
error handling
support of PersistentObjects for proxy objects which have been returned by RemoteObjects
type system of the library
stream operations


The original implementation and manual page was written 1993 by Detlef Birkholz. The revisions are due to Andreas Borchert.
Edited by: borchert, last change: 2003/07/10, revision: 1.16, converted to HTML: 2003/07/10

Oberon || Library || Module Index || Search Engine || Definition || Module