Unit SuperStream |
SuperStream is copyright (c) 1998 Ross Judson. Email support@soletta.com for support.
All rights reserved. Unauthorized duplication or use prohibited. SuperStream documentation is in DelphiDoc format. For information on DelphiDoc, please visit www.soletta.com. Any updates to SuperStream can also be found there.
The SuperStream brings several important new capabilities to Delphi.
First, the TStreamAdapter class permits the construction of streams that alter or buffer the data that passes through them, on the way to another stream. An ownership concept is present, so that the streams are easy to build and free. This enables, for example, easy buffering of TFileStreams. The stock TFileStream is unbuffered and rather slow if many small io operations are made to it. A TBufferedStream adapter can be placed on top of it to improve performance. Stream adapters can be chained, so a TObjStream can be placed over a TBufferedStream, which is over a TFileStream, and so on.
TBufferedStreams speed up io against the underlying stream if it is slow for many small reads and writes.
TObjStream permits the easy storage and recovery of complex object graphs, complete with versioning. It makes use of as much information as it can that is provided by the Delphi compiler. Usage of the object streams is as simple as declaring an "object io procedure" for a class of object and registering it. TObjStream differs from most object streaming code in that it makes heavy use of Delphi's open arrays to make coding as easy as possible.
TObjStream understands class hierarchies, so any io procedures that are declared for superclasses are also called.
TObjStream is suitable for many lightweight object storage tasks. Couple TObjStream objects together with TBufferedStreams to improve performance.
Here are the steps to use an object stream:
TObjIO = procedure(obj : TObject; stream : TObjStream; direction : TObjIODirection; version : Integer; var callSuper : Boolean);
Your IO procedure should be prepared to receive a version number other than the tip revision. If it receives an older version, it should correctly load the older version of the object. The best way to do this is to use case statements, switching on the object version. When you write objects, you should generally write the latest version. By doing this, your application can read in old objects, but will automatically upgrade them to the latest version. See the TestIO routine in the sample file for an example of this.
If you wish to register a class for IO but don't need to provide a procedure for it 'cause the superclass procedure will do, you still need to call RegisterClass. Pass nil in place of an IO procedure pointer. See the StrTestFrm.pas file for example IO procedures. You will primarily be using the TransferItems and TransferItemsEx calls. If you need to handle TDateTime, Single, or Double, make sure you use the TransferItemsEx call, passing the ssvt constants appropriate to your data.
To transfer arrays of items, use the TransferArrays calls.
Serializing sets can be a bit tricky. Use the TransferBlocks call to handle sets -- pass the address of the set and do a SizeOf() call on the set to find out how much data to store.
Note that you can freely mix calls to WriteObject and the various transfer calls during your IO procedures.
Here is an example IO procedure:
procedure TestIO(obj : TObject; stream : TObjStream; direction : TObjIODirection; version : Integer; var callSuper : Boolean);
begin
with obj as TTest do
case version of
1:
begin
// old version didn't have a t value
stream.TransferItems([s], [@s], direction, version);
t := 'yipe';
end;
2:
stream.TransferItems([s,t], [@s, @t], direction, version);
end;
end;
Classes |
TBufferedInputStream - The buffered input stream adapter can accelerate the use of underlying
streams.
TBufferedOutputStream - The buffered output stream adapter can accelerate the use of underlying
streams.
TObjList - TObjList is a subclass of TList that contains a list of objects.
TObjStream - Object stream adapters read and write objects from other streams.
TObjStreamException - Exceptions thrown by the object streaming system will be of this class
or a descendent.
TStreamAdapter - TStreamAdapter defines a stream that wraps another stream.
TStreamRegistration - Stream registration objects keep information about each class that is
streamable.
Functions |
Types |
TInitializer
TObjCreation
TObjIO
TObjIODirection
TObjStreamHeader
TObjStreamOption
TObjStreamOptions
Constants |
ssvtAnsiString
ssvtBoolean
ssvtChar
ssvtClass
ssvtCurrency
ssvtDateTime
ssvtDouble
ssvtExtended
ssvtInteger
ssvtInterface
ssvtNone
ssvtObject
ssvtPChar
ssvtPointer
ssvtPWideChar
ssvtSingle
ssvtString
ssvtVariant
ssvtWideChar
ssvtWideString
Variables |
Functions |
Types |
TInitializer = procedure
TObjCreation = procedure(obj : TObject; stream : TObjStream; version : Integer) of object
TObjIO = procedure(obj : TObject; stream : TObjStream; direction : TObjIODirection; version : Integer; var callSuper : Boolean)IO procedures must have this signature. obj is the object being read or written. If being written, you will probably want to case the object to the correct type. If being read, the object will already have been created, but will NOT have had a constructor called. If your object requires that a constructor be called, invoke it directly, as in obj.Create. This will not create a new object, but will initialize yours. Note that many constructors just initialize variables -- if you're about to read in all those variables, you don't need to set them beforehand.
Stream is the object stream. You may invoke any of its methods in your IO procedure, including WriteObject and the TransferXXX family.
Direction indicates whether the call is for reading (iodirRead) or writing (iodirWrite). Most of the time you won't have to worry about this -- the TransferXXX calls read and write objects automatically depending on the direction flag passed to them.
Version is the version of the object. You will always be requested to write only the latest version of an object, unless you specifically try to write an earlier version yourself. SuperStream won't do it. You may be asked to read an earlier version of an object. You should make sure you correctly read the earlier version, and fill in any extra information that isn't covered. That way you'll have automatic upgrading of your objects.
CallSuper is a boolean that's preset to true, indicating that the superClass' IO procedure will be called. If you don't want the superClass' IO procedure to be called, set this to false before returning.
TObjIODirection = (iodirRead, iodirWrite);This determines the whether the io is read or write.
TObjStreamHeader = recordEach object stream starts with one of these.
magic : Integer;
dummy1 : Integer;
dummy2 : Integer;
dummy3 : Integer;
dummy4 : Integer;
end;
TObjStreamOption = ( osoGraph // support object graphs );Options that can be applied to object streams. Currently only the graph option is supported. Supplying osoGraph as an option permits the reading and writing of arbitrary graphs of objects. If this option is not supplied, objects will be written in full each time they are encountered. It is highly recommended that the osoGraph option be supplied if there is any chance of an object appearing more than once. Not supplying the option will result in a small speedup.
TObjStreamOptions = set of TObjStreamOptionA set of stream options.
Constants |
Variables |