123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 |
- (* CAPO - Computational Analysis Platform for Oberon - by Alan Freed and Felix Friedrich. *)
- (* Version 1, Update 2 *)
- MODULE DataIO; (** AUTHOR "adf, fof"; PURPOSE "File IO for making scientific data types persistent"; *)
- (** A template exists for creating persistent OBJECT's that use the reader and writer defined in this module.
- This template is found in: DataTemplate.Mod. *)
- IMPORT Modules, Streams, Files, Dates, NbrInt, NbrRat, NbrRe, NbrCplx, NbrStrings, DataErrors;
- TYPE
- Item = POINTER TO RECORD
- type: Modules.TypeDesc;
- load: LoadProc;
- store: StoreProc;
- next: Item
- END;
- Registry = POINTER TO RECORD
- root: Item
- END;
- Card = OBJECT
- VAR number: NbrInt.Integer;
- type: Modules.TypeDesc;
- next: Card;
- PROCEDURE Read( R: Reader );
- VAR res: WORD;
- msg: ARRAY 64 OF CHAR;
- string: NbrStrings.String; module: Modules.Module; moduleName, typeName: Modules.Name;
- BEGIN
- NbrInt.Load( R, number ); R.RawString( moduleName ); R.RawString( typeName );
- module := Modules.ThisModule( moduleName, res, msg ); NEW( string, 64 );
- IF module # NIL THEN
- type := Modules.ThisType( module, typeName );
- IF type = NIL THEN
- string := "Type "; string := string + moduleName; string := string + "."; string := string + typeName; string := string + " does not exist on your computer.";
- DataErrors.Error( string^ )
- END
- ELSE
- string := "Module "; string := string + moduleName; string := string + " does not exist on your computer.";
- DataErrors.Error( string^ ); HALT( 1000 ); (* Must halt here, further reading is catastrophic. *)
- END
- END Read;
- PROCEDURE Write( W: Writer );
- BEGIN
- NbrInt.Store( W, number ); W.RawString( type.mod.name ); W.RawString( type.name )
- END Write;
- END Card;
- Library = OBJECT
- VAR entries: NbrInt.Integer;
- root, card: Card;
- PROCEDURE & Initialize*;
- BEGIN
- entries := 0; NEW( root ); root.number := 0; card := root
- END Initialize;
- PROCEDURE Read( R: Reader );
- VAR i: NbrInt.Integer; new: Card;
- BEGIN
- card := root; NbrInt.Load( R, entries );
- FOR i := 1 TO entries DO NEW( new ); new.Read( R ); card.next := new; card := card.next END;
- card := root
- END Read;
- PROCEDURE Write( W: Writer );
- VAR i: NbrInt.Integer;
- BEGIN
- card := root; NbrInt.Store( W, entries );
- FOR i := 1 TO entries DO card := card.next; card.Write( W ) END;
- card := root
- END Write;
- PROCEDURE Push( type: Modules.TypeDesc );
- VAR new: Card;
- BEGIN
- card := root;
- IF type # NIL THEN
- LOOP
- IF card.next = NIL THEN
- NEW( new ); new.number := card.number + 1; new.type := type; card.next := new; card := new;
- NbrInt.Inc( entries ); EXIT
- END;
- IF type = card.type THEN (* This library card already exists. *) EXIT END;
- card := card.next
- END
- END
- END Push;
- END Library;
- (** Reader for reading data from a .Data file. *)
- Reader* = OBJECT (Files.Reader)
- VAR lib: Library;
- file: File;
- PROCEDURE ReaderAvailable( ): BOOLEAN;
- BEGIN
- IF res = Streams.Ok THEN RETURN TRUE
- ELSIF res = Streams.EOF THEN DataErrors.Error( "Attempted to read past the end of file." ); RETURN FALSE
- ELSIF res = Streams.FormatError THEN DataErrors.Error( "A format error encounter by the reader." ); RETURN FALSE
- ELSE DataErrors.IntError( res, "The 'res' error number originating from an Streams reader error." ); RETURN FALSE
- END
- END ReaderAvailable;
- (** Reads a date and time. *)
- PROCEDURE DateTime*( VAR x: Dates.DateTime );
- VAR d, t: LONGINT;
- BEGIN
- IF ReaderAvailable() THEN RawNum( d ); RawNum( t ); x := Dates.OberonToDateTime( d, t ) ELSE HALT( 1000 ) END
- END DateTime;
- (** Reads an integer number. *)
- PROCEDURE Integer*( VAR x: NbrInt.Integer );
- BEGIN
- IF ReaderAvailable() THEN NbrInt.Load( SELF, x ) ELSE HALT( 1000 ) END
- END Integer;
- (** Reads a rational number. *)
- PROCEDURE Rational*( VAR x: NbrRat.Rational );
- BEGIN
- IF ReaderAvailable() THEN NbrRat.Load( SELF, x ) ELSE HALT( 1000 ) END
- END Rational;
- (** Reads a complex number. *)
- PROCEDURE Complex*( VAR x: NbrCplx.Complex );
- BEGIN
- IF ReaderAvailable() THEN NbrCplx.Load( SELF, x ) ELSE HALT( 1000 ) END
- END Complex;
- (** Reads a dynamic 0X-terminated string. *)
- PROCEDURE PtrString*( VAR x: NbrStrings.String );
- BEGIN
- IF ReaderAvailable() THEN NbrStrings.Load( SELF, x ) ELSE HALT( 1000 ) END
- END PtrString;
- (*
- (* Reads a dynamic POINTER TO ARRAY of some type that has been registered via the PlugIn procedure. *)
- PROCEDURE PtrArray*( VAR x: ANY );
- BEGIN
- IF ReaderAvailable() THEN
- ELSE HALT(1000)
- END
- END PtrArray;
- *)
- (** Reads a dynamic object whose type has been registered via the PlugIn procedure. *)
- PROCEDURE Object*( VAR x: OBJECT );
- VAR n: NbrInt.Integer; item: Item;
- BEGIN
- IF ReaderAvailable() THEN
- Integer( n );
- IF n > 0 THEN
- lib.card := lib.root;
- LOOP
- lib.card := lib.card.next;
- IF lib.card = NIL THEN DataErrors.Error( "Corrupt file - sought library card does not exist." ); HALT( 1001 ); EXIT END;
- IF n = lib.card.number THEN
- item := registry.root;
- LOOP
- item := item.next;
- IF item = NIL THEN DataErrors.Warning( "Encountered an alien object to be read from file." ); HALT( 1002 ); EXIT END;
- IF lib.card.type = item.type THEN item.load( SELF, x ); EXIT END
- END;
- EXIT
- END
- END
- ELSE (* The object stored was the NIL pointer. *) x := NIL
- END;
- lib.card := lib.root
- ELSE HALT( 1000 )
- END
- END Object;
- END Reader;
- (** Writer for writing data to a .Data file. *)
- Writer* = OBJECT (Files.Writer);
- VAR lib: Library;
- file: File;
- (** Writes a date and time. *)
- PROCEDURE DateTime*( x: Dates.DateTime );
- VAR d, t: LONGINT;
- BEGIN
- Dates.DateTimeToOberon( x, d, t ); RawNum( d ); RawNum( t ); Update
- END DateTime;
- (** Writes an integer number. *)
- PROCEDURE Integer*( x: NbrInt.Integer );
- BEGIN
- NbrInt.Store( SELF, x ); Update
- END Integer;
- (** Writes a rational number. *)
- PROCEDURE Rational*( x: NbrRat.Rational );
- BEGIN
- NbrRat.Store( SELF, x ); Update
- END Rational;
- (** Writes a real number. *)
- PROCEDURE Real*( x: NbrRe.Real );
- BEGIN
- NbrRe.Store( SELF, x ); Update
- END Real;
- (** Writes a complex number. *)
- PROCEDURE Complex*( x: NbrCplx.Complex );
- BEGIN
- NbrCplx.Store( SELF, x ); Update
- END Complex;
- (** Writes a dynamic 0X-terminated string. *)
- PROCEDURE PtrString*( x: NbrStrings.String );
- BEGIN
- NbrStrings.Store( SELF, x ); Update
- END PtrString;
- (*
- (* Writes a dynamic POINTER TO ARRAY of some type that has been registered via the PlugIn procedure. *)
- PROCEDURE PtrArray*( x: ANY );
- VAR
- BEGIN
- END PtrArray;
- *)
- (** Writes a dynamic object whose type has been registered via the PlugIn procedure. *)
- PROCEDURE Object*( x: OBJECT );
- VAR item: Item;
- BEGIN
- IF x # NIL THEN
- lib.Push( Modules.TypeOf( x ) ); item := registry.root;
- LOOP
- item := item.next;
- IF item = NIL THEN DataErrors.Error( "Attempted to write an object whose type has not been registered." ); HALT( 1003 ); EXIT END;
- IF lib.card.type = item.type THEN Integer( lib.card.number ); item.store( SELF, x ); EXIT END
- END
- ELSE (* Object is NIL. *) Integer( 0 )
- END;
- Update
- END Object;
- END Writer;
- (** File type for files with a .Data extension. *)
- File* = OBJECT (** Not shareable between multiple processes. *)
- VAR F: Files.File;
- lib: Library;
- initialwpos: LONGINT;
- R-: Reader;
- W-: Writer;
- PROCEDURE & Initialize*;
- BEGIN
- NEW( lib ); initialwpos := 0
- END Initialize;
- PROCEDURE Length*( ): LONGINT;
- BEGIN
- RETURN F.Length()
- END Length;
- END File;
- (** Loading procedure type for registering an object for data IO. *)
- LoadProc* = PROCEDURE ( R: Reader; VAR obj: OBJECT );
- (** Storing procedure type for registering an object for data IO. *)
- StoreProc* = PROCEDURE ( W: Writer; obj: OBJECT );
- VAR
- registry: Registry;
- (** Register the load and store procedures that belong to the dynamic object to be made persistent. *)
- PROCEDURE PlugIn*( obj: ANY; load: LoadProc; store: StoreProc );
- VAR item, new: Item; type: Modules.TypeDesc; string: NbrStrings.String;
- BEGIN
- IF obj # NIL THEN
- IF load # NIL THEN
- IF store # NIL THEN
- type := Modules.TypeOf( obj ); item := registry.root;
- LOOP
- IF type = item.type THEN (* already registered *) EXIT END;
- IF item.next = NIL THEN
- NEW( new ); new.type := type; new.load := load; new.store := store; item.next := new; EXIT
- END;
- item := item.next
- END
- ELSE NEW( string, 64 ); string := "A NIL 'store' procedure was sent for registation from module "; string := string + type.mod.name; string := string + "."; DataErrors.Error( string^ )
- END
- ELSE NEW( string, 64 ); string := "A NIL 'load' procedure was sent for registation from module "; string := string + type.mod.name; string := string + "."; DataErrors.Error( string^ )
- END
- ELSE NEW( string, 64 ); string := "A NIL 'obj' was sent for registation from module "; string := string + type.mod.name; string := string + "."; DataErrors.Error( string^ )
- END
- END PlugIn;
- (** Open a .Data file.
- The reader is placed at the beginning of the file.
- The writer is placed at the end of the file, which is at the beginning if the file is new,
- thereby preventing existing data from being overwritten. *)
- PROCEDURE Open*( fileName: Files.FileName ): File;
- VAR existingFile: BOOLEAN; dummy, libPos: LONGINT; file: File; libR: Reader;
- name, fName: ARRAY Files.NameLength OF CHAR;
- suffix: ARRAY Files.PrefixLength OF CHAR;
- BEGIN
- (* Create the file. *)
- NEW( file ); Files.SplitExtension( fileName, name, suffix ); Files.JoinExtension( name, "Data", fName );
- file.F := Files.Old( fName );
- IF file.F # NIL THEN existingFile := TRUE ELSE existingFile := FALSE; file.F := Files.New( fName ) END;
- (* Attach the reader. *)
- NEW( file.R, file.F, 0 ); file.R.file := file;
- IF existingFile THEN
- (* Read in the library. *)
- file.R.RawLInt( libPos ); NEW( libR, file.F, libPos ); file.lib.Read( libR )
- END;
- file.R.lib := file.lib;
- (* Attach the writer. *)
- IF existingFile THEN
- (* Append any new data. *)
- NEW( file.W, file.F, libPos ); file.W.InitFileWriter( file.F, libPos );
- file.initialwpos := libPos; (* Position of a Writer is relative to initial point *)
- ELSE
- NEW( file.W, file.F, 0 );
- (* Handle the place holder locating the library. *)
- file.W.RawLInt( dummy ); file.R.RawLInt( dummy )
- END;
- file.W.lib := file.lib; file.W.file := file; RETURN file
- END Open;
- (** Opening an existing file places the writer at the end of that file, by default, therefore all future
- writings append the file. Calling Rewind moves the reader and writer to the beginning of the file,
- and will therefore overwrite all contents previously held by the file. All prior data will be lost. *)
- PROCEDURE Rewind*( f: File );
- VAR dummy: LONGINT;
- BEGIN
- IF f # NIL THEN
- NEW( f.lib ); NEW( f.R, f.F, 0 ); NEW( f.W, f.F, 0 ); f.initialwpos := 0; f.R.lib := f.lib; f.R.file := f; f.W.lib := f.lib; f.W.file := f;
- (* Handle the place holder locating the library. *)
- f.W.RawLInt( dummy ); f.R.RawLInt( dummy )
- END
- END Rewind;
- (** Close a .Data file.
- Executing this command attaches a hidden library needed to allocate memory to restore stored PTR variables.
- It is therefore imperative that every file opened with Open gets physically closed with this command. *)
- PROCEDURE Close*( f: File );
- VAR libPosW: Writer;
- BEGIN
- f.W.Update; NEW( libPosW, f.F, 0 ); (* writing library position to very first position *)
- libPosW.RawLInt( f.W.Pos() + f.initialwpos ); libPosW.Update; f.lib.Write( f.W );
- f.W.Update; Files.Register( f.F ); f.F := NIL; f.lib := NIL; f.R := NIL; f.W := NIL
- END Close;
- BEGIN
- NEW( registry ); NEW( registry.root )
- END DataIO.
|