home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 1997 May
/
Pcwk0597.iso
/
delphi
/
dnarrays.lzh
/
ARRAYS.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1995-07-15
|
98KB
|
2,847 lines
{+------------------------------------------------------------
| Unit Arrays
|
| Version: 1.0 Last modified: 04/18/95, 11:47:00
| Author : P. Below
| Project: Delphi common objects
| Description:
| This Unit implements a base class for resizeable array types
| and a few specific derivatives for the common numeric types.
|
| The array classes in this unit are all limited to a maximum
| of 64Kbytes of data. The size of the stored items determines
| the maximal number of items. Errors will raise exceptions,
| index overflow is only reported if range checking is on. The
| index range of each class is 0..MaxIndex, MaxIndex is a property
| of all class types.
|
| The classes have iterator methods similar to BP collections.
| These iterators can optionally call Application.ProcessMessages
| between rounds. This requires usage of the Forms Unit. Since
| this would involve a tremendous overhead for non-VCL projects
| the correspondig USes clause and the iterator code calling
| Application.ProcessMessages is enclosed in $IFDEF DOEVENTS
| blocks. If DOEVENTS is defined, the Forms unit will be used.
| DOEVENTS IS UNDEFINED BY DEFAULT! You need to define this
| symbol in your project to make use of the ability to process
| messages inside iterator loops and recompile this unit!
| The unit does not make any other use of VCL window objects.
+------------------------------------------------------------}
Unit Arrays;
Interface
Uses SysUtils, Classes;
Const
(* the following value is returned by the Find method if the passed
value could not be found in the array *)
NOT_FOUND = High( Cardinal );
Type
(* Our virtual array need a function of this type to sort themselves
and search items. As usual the return type should be < 0 if
item1 < item2, > 0 if item1 > item2 and 0 if both are equal.
Note that the result is not limited to -1, 0, +1! This allows
faster comparison. *)
TCompareProc = Function ( Var item1, item2 ): Integer;
(* these procedural types represent functions that can be called
from one of the iterator method, like ForEach. Version for stand-
alone procedure and for object methods are provided. *)
TIterator = Procedure( Var Element; index: Cardinal );
TLocator = Function( Var Element; index: Cardinal ): Boolean;
TIteratorMethod = Procedure( Var Element; index: Cardinal ) of Object;
TLocatorMethod = Function( Var Element;
index: Cardinal ): Boolean of Object;
(* This error is raised when Sort or Find are called and a compare proc
has not been assigned *)
ECompUndefined = Class( Exception );
(* This error is raised when two class instances are not comaptible
for an operation *)
ETypeMismatch = Class( Exception );
(* This error is raised if a textfile is too large to be loaded into
a TPCharArray or TPStringArray *)
EFileTooLarge = Class( Exception );
TSortOrder = ( TS_NONE, TS_ASCENDING, TS_DESCENDING );
(* these flags covern some of the behaviour of array methods *)
TArrayFlags = ( AF_OwnsData, AF_AutoSize, AF_CanCompare,
AF_User1, AF_User2, AF_User3, AF_User4, AF_User5,
AF_User6, AF_User7, AF_User8, AF_User9, AF_User10,
AF_User11, AF_User12 );
TArrayFlagSet = Set of TArrayFlags;
(* this notification is used by the store/load from textfile methods
of the string/pchar array classes *)
TProgressReporter = Function( pos, max: LongInt;
Var retain: Boolean ): Boolean of Object;
(* T64KArray is our base array class. It is limited to a single 64K
segment for all items. *)
T64KArray = Class( TPersistent )
private
FMemory: Pointer; (* pointer to item buffer *)
FMemSize, (* allocated size of buffer in bytes *)
FItemSize, (* size of individual item in bytes *)
FMaxIndex: Cardinal; (* max valid index, zero-based *)
FSortOrder : TSortOrder; (* true if array is considered sorted *)
FCompareProc : TCompareProc; (* pointer to compare proc *)
FFlags : TArrayFlagSet; (* ability flags *)
Procedure DefineProperties(Filer: TFiler);
override;
Procedure AssignTo( Dest: TPersistent );
override;
Function GetMaxCapacity: Cardinal;
Function GetCapacity: Cardinal;
public
Procedure SaveToFile( Const Filename: String );
virtual;
Procedure LoadFromFile( Const Filename: String );
virtual;
Procedure SaveToStream( Stream: TStream );
virtual;
Procedure LoadFromStream( Stream: TStream );
virtual;
Function GetItemPtr( index: Cardinal ): Pointer;
Procedure PutItem( index: Cardinal; Var data );
Procedure GetItem( index: Cardinal; Var data );
Procedure InvalidateItems( atIndex, numItems: Cardinal );
virtual;
Function ValidIndex( index: Cardinal ): Boolean;
Function ValidateBounds( atIndex: Cardinal;
Var numItems: Cardinal): Boolean;
Constructor Create( itemcount, itemsize: Cardinal ); virtual;
Destructor Destroy; override;
Procedure Zap; virtual;
Function Clone: T64KArray;
virtual;
Procedure ReDim( newcount: Cardinal );
virtual;
Procedure Insert( Var Source; atIndex, numItems: Cardinal );
virtual;
Procedure Delete( atIndex, numItems: Cardinal );
virtual;
Procedure Append( Var Source; numItems: Cardinal );
virtual;
Procedure CopyFrom( Var Source; toIndex, numItems: Cardinal );
virtual;
Procedure CopyTo( Var Dest; fromIndex, numItems: Cardinal );
virtual;
Procedure BlockCopy( Source: T64KArray;
fromIndex, toIndex, numitems: Cardinal );
virtual;
Procedure Sort( ascending: Boolean );
virtual;
Function Find( Var value ): Cardinal;
virtual;
Procedure ForEach( iterator: TIteratorMethod; processMsg: Boolean;
intervall: Cardinal );
Function FirstThat( locator: TLocatorMethod;
processMsg: Boolean;
intervall: Cardinal ): Pointer;
Function LastThat(locator: TLocatorMethod;
processMsg: Boolean;
intervall: Cardinal ): Pointer;
Procedure ForEachProc( iterator: TIterator; processMsg: Boolean;
intervall: Cardinal );
Function FirstThatProc( locator: TLocator;
processMsg: Boolean;
intervall: Cardinal ): Pointer;
Function LastThatProc(locator: TLocator;
processMsg: Boolean;
intervall: Cardinal ): Pointer;
Function GetCount: Cardinal; virtual;
Procedure SetCompareProc( proc: TCompareProc );
Function HasFlag( aFlag: TArrayFlags ): Boolean;
Procedure SetFlag( aFlag: TArrayFlags );
Procedure ClearFlag( aFlag: TArrayFlags );
property Memory: Pointer read FMemory;
property MemSize: Cardinal read FMemSize;
property ItemSize: Cardinal read FItemSize;
property MaxIndex: Cardinal read FMaxIndex;
property Count: Cardinal read GetCount;
property ItemPtr[ Index:Cardinal ]: Pointer read GetItemPtr;
property SortOrder: TSortOrder read FSortOrder write FSortOrder;
property CompareProc: TCompareProc read FCompareProc write SetCompareProc;
property Capacity: Cardinal read GetCapacity;
property MaxCapacity: Cardinal read GetMaxCapacity;
property Flags: TArrayFlagSet read FFlags write FFlags;
End;
C64KArray= Class of T64KArray;
(* Following are a couple of derived classes for the common numeric types.
Access to items can be done via normal array syntax on the instance
via the default array property. *)
TIntegerArray = Class( T64KArray )
public
Constructor Create( itemcount, dummy: Cardinal ); override;
Procedure PutData( index: Cardinal; value: Integer );
Function GetData(index: Cardinal): Integer;
property Data[ Index:Cardinal ]: Integer
read GetData write PutData; default;
End;
TCardinalArray = Class( T64KArray )
public
Constructor Create( itemcount, dummy: Cardinal ); override;
Procedure PutData( index: Cardinal; value: Cardinal );
Function GetData(index: Cardinal): Cardinal;
property Data[ Index:Cardinal ]: Cardinal
read GetData write PutData; default;
End;
TLongIntArray = Class( T64KArray )
public
Constructor Create( itemcount, dummy: Cardinal ); override;
Procedure PutData( index: Cardinal; value: LongInt );
Function GetData(index: Cardinal): LongInt;
property Data[ Index:Cardinal ]: LongInt
read GetData write PutData; default;
End;
TRealArray = Class( T64KArray )
public
Constructor Create( itemcount, dummy: Cardinal ); override;
Procedure PutData( index: Cardinal; value: Real );
Function GetData(index: Cardinal): Real;
property Data[ Index:Cardinal ]: Real
read GetData write PutData; default;
End;
TSingleArray = Class( T64KArray )
public
Constructor Create( itemcount, dummy: Cardinal ); override;
Procedure PutData( index: Cardinal; value: Single );
Function GetData(index: Cardinal): Single;
property Data[ Index: Cardinal ]: Single
read GetData write PutData; default;
End;
TDoubleArray = Class( T64KArray )
public
Constructor Create( itemcount, dummy: Cardinal ); override;
Procedure PutData( index: Cardinal; value: Double );
Function GetData(index: Cardinal): Double;
property Data[ Index: Cardinal ]: Double
read GetData write PutData; default;
End;
TExtendedArray = Class( T64KArray )
public
Constructor Create( itemcount, dummy: Cardinal ); override;
Procedure PutData( index: Cardinal; value: Extended );
Function GetData(index: Cardinal): Extended;
property Data[ Index:Cardinal ]: Extended
read GetData write PutData; default;
End;
TPointerArray = Class( T64KArray )
public
Constructor Create( itemcount, dummy: Cardinal ); override;
Procedure PutData( index: Cardinal; value: Pointer );
Function GetData(index: Cardinal): Pointer;
Procedure CopyFrom( Var Source; toIndex, numItems: Cardinal );
override;
Procedure CopyTo( Var Dest; fromIndex, numItems: Cardinal );
override;
Procedure InvalidateItems(atIndex, numItems: Cardinal);
override;
Function CloneItem( item: Pointer ): Pointer; virtual;
Procedure FreeItem( item: Pointer ); virtual;
Procedure SaveToFile( Const Filename: String );
override;
Procedure LoadFromFile( Const Filename: String );
override;
Procedure SaveToStream( Stream: TStream );
override;
Procedure LoadFromStream( Stream: TStream );
override;
Procedure SaveItemToStream( S: TStream; Item: Pointer );
virtual;
Procedure LoadItemFromStream( S: TStream; Var Item: Pointer );
virtual;
property AsPtr[ Index: Cardinal ]: Pointer
read GetData write PutData;
property Data[ Index: Cardinal ]: Pointer
read GetData write PutData; (* NOT default here! *)
End;
TPCharArray = Class( TPointerArray )
public
Constructor Create( itemcount, dummy: Cardinal ); override;
Procedure PutData( index: Cardinal; value: PChar );
Function GetData(index: Cardinal): PChar;
Function CloneItem( item: Pointer ): Pointer;
override;
Procedure FreeItem( item: Pointer );
override;
Procedure SaveItemToStream( S: TStream; Item: Pointer );
override;
Procedure LoadItemFromStream( S: TStream; Var Item: Pointer );
override;
Procedure PutAsString( index: Cardinal; Const value: String );
Function GetAsString(index: Cardinal): String;
Procedure PutAsInteger( index: Cardinal; value: LongInt );
Function GetAsInteger(index: Cardinal): LongInt;
Procedure PutAsReal( index: Cardinal; value: Extended );
Function GetAsReal(index: Cardinal): Extended;
Procedure LoadFromTextfile( Const Filename: String;
appendData: Boolean;
reporter: TProgressReporter );
Procedure SaveToTextfile( Const Filename: String;
appendData: Boolean;
reporter: TProgressReporter );
property Data[ Index: Cardinal ]: PChar
read GetData write PutData; Default;
property AsString[ Index: Cardinal ]: String
read GetAsString write PutAsString;
property AsInteger[ Index: Cardinal ]: LongInt
read GetAsInteger write PutAsInteger;
property AsReal[ Index: Cardinal ]: Extended
read GetAsReal write PutAsReal;
End;
TPStringArray = Class( TPointerArray )
public
Constructor Create( itemcount, dummy: Cardinal ); override;
Procedure PutData( index: Cardinal; Const value: String );
Function GetData(index: Cardinal): String;
Function CloneItem( item: Pointer ): Pointer;
override;
Procedure FreeItem( item: Pointer );
override;
Procedure SaveItemToStream( S: TStream; Item: Pointer );
override;
Procedure LoadItemFromStream( S: TStream; Var Item: Pointer );
override;
Function GetAsPtr(index: Cardinal): PString;
Procedure PutAsPChar( index: Cardinal; value: PChar );
Function GetAsPChar(index: Cardinal): PChar;
Procedure PutAsInteger( index: Cardinal; value: LongInt );
Function GetAsInteger(index: Cardinal): LongInt;
Procedure PutAsReal( index: Cardinal; value: Extended );
Function GetAsReal(index: Cardinal): Extended;
Procedure LoadFromTextfile( Const Filename: String;
appendData: Boolean;
reporter: TProgressReporter );
Procedure SaveToTextfile( Const Filename: String;
appendData: Boolean;
reporter: TProgressReporter );
property Data[ Index: Cardinal ]: String
read GetData write PutData; Default;
property AsPChar[ Index: Cardinal ]: Pchar
read GetAsPChar write PutAsPChar;
property AsInteger[ Index: Cardinal ]: LongInt
read GetAsInteger write PutAsInteger;
property AsReal[ Index: Cardinal ]: Extended
read GetAsReal write PutAsReal;
property AsPString[ Index: Cardinal ]: PString
read GetAsPtr;
End;
(* a couple of compare procedures as used by the numeric array classes *)
Function CmpIntegers( Var item1, item2 ): Integer;
Function CmpCardinals( Var item1, item2 ): Integer;
Function CmpLongs( Var item1, item2 ): Integer;
Function CmpReals( Var item1, item2 ): Integer;
Function CmpSingles( Var item1, item2 ): Integer;
Function CmpDoubles( Var item1, item2 ): Integer;
Function CmpExtendeds( Var item1, item2 ): Integer;
Function CmpPChars( Var item1, item2 ): Integer;
Function CmpPStrings( Var item1, item2 ): Integer;
Implementation
{$IFDEF DOEVENTS}
Uses Forms, FastMem, WinProcs;
{$ELSE}
Uses FastMem, WinProcs;
{$ENDIF}
Const
(* This section defines the error messages for exceptions specific to
the objects in this Unit. Translate as necessary. *)
ErrAssign =
'T64KArray.AssignTo: Destination object does not match this array.';
ErrLoad =
'T64KArray.LoadFromStream: The stored items have different size than'+
' this arrays items.';
ErrCompare =
'T64KArray.Sort/Find: No comparision function has been assigned '+
'for this array object.';
ErrIndex =
'T64KArray: Index %u out of bounds, maximum allowed is %u';
ErrSegmentOverflow =
'T64KArray.ReDim: requested size > 64Kbyte!';
ErrFileTooLarge =
'LoadFromTextfile: File %s has too many lines to load completely!';
{+----------------------
| Methods of T64KArray
+----------------------}
{************************************************************
* T64KArray.DefineProperties
*
* Parameters:
* Filer: a storage handler object
* Description:
* This methods prepares the object for streaming by telling the
* Filer which methods to call for loading and storing the array
* data.
* Error Conditions:
* none
*
*Created: 04/18/95 14:33:53 by P. Below
************************************************************}
Procedure T64KArray.DefineProperties(Filer: TFiler);
Begin
inherited DefineProperties( Filer );
Filer.DefineBinaryProperty( 'ArrayData', LoadFromStream,
SaveToStream, FMemory <> Nil );
End; { T64KArray.DefineProperties }
{************************************************************
* T64KArray.AssignTo
*
* Parameters:
* Dest: the target object
* Description:
* This method copies the contents of this array to the destination
* array, provided the destination is a descendant of T64KArray and
* has the same component size. The destination array is redim'ed to
* the same size as this array. The actual copy is performed by
* the BlockCopy method, which a descendant class can override to
* realize a deep copy, for instance, if the items stored in the
* array are pointers.
* Error Conditions:
* This method will raise a EConvertError exception, if the type of
* the destination does not match that of Self. It may also cause
* a protection fault, if Dest ist Nil ( really stupid! ) or an out
* of memory exception in ReDim.
*
*Created: 04/18/95 15:01:29 by P. Below
************************************************************}
Procedure T64KArray.AssignTo( Dest: TPersistent );
Var
D: T64KArray absolute Dest;
Begin
If ( Dest Is ClassType ) and ( ItemSize = D.ItemSize ) Then Begin
If D.MaxIndex < MaxIndex Then
D.Redim( Succ( MaxIndex ) );
D.BlockCopy( Self, 0, 0, Succ( maxIndex ));
D.SortOrder := SortOrder;
D.Flags := Flags;
D.CompareProc := CompareProc;
End { If }
Else
raise ETypeMismatch.Create( errAssign );
End; { T64KArray.AssignTo }
Function T64KArray.GetMaxCapacity: Cardinal;
Begin
Result := High( Cardinal ) div ItemSize;
End; { T64KArray.GetMaxCapacity }
Function T64KArray.GetCapacity: Cardinal;
Begin
Result := Succ( MaxIndex );
End; { T64KArray.GetCapacity }
{************************************************************
* T64KArray.SaveToFile
*
* Parameters:
* Filename: name of file to write
* Description:
* Saves the data in this array to a file. Only the array data
* itself is written, neither the component size not the number
* of items are stored! This makes it possible to access the file
* as a File Of Component ( where Component is the type stored in
* this array, not a Delphi Component!).
* Error Conditions:
* May raise a EInOutError exception if a file-related error occurs.
*
*Created: 05/01/95 16:09:08 by P. Below
************************************************************}
Procedure T64KArray.SaveToFile( Const Filename: String );
Var
F: File;
Begin
AssignFile( F, Filename );
Rewrite( F, ItemSize );
try
BlockWrite( F, FMemory^, Succ( MaxIndex ));
finally
CloseFile( F );
end;
End; { T64KArray.SaveToFile }
{************************************************************
* T64KArray.LoadFromFile
*
* Parameters:
* Filename: name of file to load
* Description:
* Loads the contents of the requested file into the array, which
* is redimensioned to fit the data.
* For this to work smoothly the file should have been created
* by the SaveToFile method of an array object of the same type
* as this one and it must be < 64KBytes in size! If it is larger
* only part of it will be read. If the items in the file do have
* a different item size that this array assumes (a fact we cannot
* check), the loaded data will propably come out as garbage!
* Error Conditions:
* May raise a EInOutError exception if a file-related error occurs.
*
*Created: 05/01/95 16:28:50 by P. Below
************************************************************}
Procedure T64KArray.LoadFromFile( Const Filename: String );
Var
F: File;
N: LongInt;
Begin
AssignFile( F, Filename );
FileMode := fmOpenRead or fmShareDenyWrite;
try
Reset( F, ItemSize );
N := FileSize( F );
If (N*ItemSize) > LongInt( High( Cardinal )) Then
N := High( Cardinal ) div ItemSize;
Redim( N );
BlockRead( F, FMemory^, Succ( MaxIndex ));
finally
FileMode := 2;
CloseFile( F );
end;
End; { T64KArray.LoadFromFile }
{************************************************************
* T64KArray.SaveToStream
*
* Parameters:
* Stream: an opened stream that takes the array data
* Description:
* This method stores the arrays item size and max index
* (NOT the number of items!) followed by the array data into
* the passed stream. NOTE that this is different from SaveToFile,
* which only writes the array data!
* You can use this method to append the array data to an open
* stream that can already contain other data in front and receive
* additional data after we are done here.
* We do not stream the array object itself, only its data!
* Error Conditions:
* The stream may raise an exception if it runs into problems.
*
*Created: 05/01/95 16:53:49 by P. Below
************************************************************}
Procedure T64KArray.SaveToStream( Stream: TStream );
Var
{$IFDEF WIN32}
TempSize, TempIndex: Cardinal;
{$ELSE}
TempSize, TempIndex: LongInt;
{$ENDIF}
Begin
TempSize := FItemSize;
TempIndex := FMaxIndex;
With Stream Do Begin
Write( TempSize, Sizeof( TempSize ));
Write( TempIndex, Sizeof( TempIndex ));
Write( FMemory^, FMemSize );
End; { With }
End; { T64KArray.SaveToStream }
{************************************************************
* T64KArray.LoadFromStream
*
* Parameters:
* Stream: an opened stream that holds the array data to read
* Description:
* This method reads the stored arrays item size and max index
* and checks the item size vs. our own item size. If these two
* do match, the array is redimensioned according to the needed
* size and the array data are read from the passed stream.
* NOTE that this is different from LoadFromFile, which only
* reads the array data and assumes they have the right item size!
* You can use this method to get the array data from an open
* stream that can already contain other data in front and
* additional data after. However, it is your responsibility
* to position the stream pointer correctly.
* Error Conditions:
* The stream may raise an exception if it runs into problems.
* We will raise an ETypeMismatch exception if the item size read
* from the stream does not match our own item size.
*
*Created: 05/01/95 16:53:49 by P. Below
************************************************************}
Procedure T64KArray.LoadFromStream( Stream: TStream );
Var
{$IFDEF WIN32}
TempSize, TempIndex: Cardinal;
{$ELSE}
TempSize, TempIndex: LongInt;
{$ENDIF}
Begin
Zap;
With Stream Do Begin
Read( TempSize, Sizeof( TempSize ));
Read( TempIndex, Sizeof( TempIndex ));
If TempSize = ItemSize Then Begin
Redim( Succ( TempIndex ));
Read( FMemory^, FMemSize );
End { If }
Else
raise ETypeMismatch.Create( errLoad );
End; { With }
End; { T64KArray.LoadFromStream }
{************************************************************
* T64KArray.GetItemPtr
*
* Parameters:
* index: index ( zero-based ) of the item to access
* Returns:
* a pointer to the requested item in this array
* Description:
* Does brute-force pointer arithmetic to calculate the
* items address from index and size.
* WARNING! Does no checks for FMemory=Nil!
* Error Conditions:
* If the passed index is out of range, the method will raise
* an ERangeError exception, if range checking is enabled,
* otherwise it returns a pointer to the first item in the
* array.
*
*Created: 04/18/95 15:56:08 by P. Below
************************************************************}
Function T64KArray.GetItemPtr( index: Cardinal ): Pointer;
Begin
Result := FMemory;
If ValidIndex( index ) Then
Inc( PtrRec( Result ).ofs, index*FItemSize )
End; { T64KArray.GetItemPtr }
{************************************************************
* T64KArray.GetCount
*
* Parameters:
* none
* Returns:
* the number of used items in the array
* Description:
* This method is used to implement the Count property. For
* this class it acts like Capacity, because all items of the
* array are considered in use. But for a descendant class that
* works more like a BP collection, only part of the items
* may be actually used. These classes can override GetCount to
* return the actually used number. The Count property is used
* by Sort, Find and the iterator methods to get the upper bound
* of the range to operate on; these methods will thus work
* without changes in collection-like descendants.
* Error Conditions:
* none
*
*Created: 05/20/95 18:07:46 by P. Below
************************************************************}
Function T64KArray.GetCount: Cardinal;
Begin
Result := Succ( FMaxIndex )
End;
{************************************************************
* T64KArray.PutItem
*
* Parameters:
* data: a data item to put into the array, must have same
* size as the arrays components.
* index: index of array slot to put the data into ( zero-based )
* Description:
* Uses a direct mem copy to put the data into the array. No
* error checks on type of the passed data are possible here!
* NOTE:
* The method obviously overwrites the old contents of the index
* slot but it does _not_ invalidate the old entry! Thus this
* method can be used by an InvalidateItems handler to set a
* pointer to Nil.
* Error Conditions:
* If the index is out ouf bounds, does nothing.
*
*Created: 04/18/95 16:10:14 by P. Below
************************************************************}
Procedure T64KArray.PutItem(index: Cardinal; Var data );
Begin
If ValidIndex( index ) Then Begin
MemMove( @data, GetItemPtr( index ), FItemSize );
SortOrder := TS_NONE;
End;
End; { T64KArray.PutItem }
{ Same as above, only on reverse gear }
Procedure T64KArray.GetItem( index: Cardinal; Var data );
Begin
If ValidIndex( index ) Then
MemMove( GetItemPtr( index ), @data, FItemSize );
End; { T64KArray.GetItem }
{************************************************************
* T64KArray.Create
*
* Parameters:
* itemcount: number of items the array should hold, cannot be
* 0! 0 is mapped to 1.
* itemsize : size in bytes of an individual item
* Description:
* Allocates the memory for the array and sets the fields
* according to the passed data. In the Win16 version the
* product of itemcount and itemsize has to be < 64Kbyte.
* We reduce the itemcount to an allowed value, if necessary,
* without raising any error if it is to large.
* Error Conditions:
* If GetMem fails we rely on the default exception handling to
* fail the constructor.
*
*Created: 04/18/95 16:30:08 by P. Below
************************************************************}
Constructor T64KArray.Create( itemcount, itemsize: Cardinal );
{$IFNDEF WIN32}
Var
s: LongInt;
{$ENDIF}
Begin
inherited Create;
If itemcount = 0 Then Inc( itemcount );
{$IFNDEF WIN32}
s := LongInt( itemcount ) * itemsize;
If s >= $10000 Then Begin
(* user has math problems, be gracious and reduce itemcount
to allowed value *)
itemcount := $FFFF div itemsize;
End; { If }
{$ENDIF}
FMemSize := itemcount * itemsize;
GetMem( FMemory, FMemSize );
MemFill( FMemory, FMemSize, 0 );
FItemSize := itemsize;
FMaxIndex := Pred( itemcount );
FFlags := [ AF_OwnsData, AF_AutoSize ];
End; { T64KArray.Create }
{************************************************************
* T64KArray.Destroy
*
* Parameters:
* none
* Description:
* Standard destructor, frees the memory allocated for the array
* and then calls the inherited destructor. We invalidate all used
* items first.
* Error Conditions:
* none
*
*Created: 04/18/95 16:34:35 by P. Below
************************************************************}
Destructor T64KArray.Destroy;
Begin
If FMemory <> Nil Then Begin
InvalidateItems( 0, Count );
FreeMem( FMemory, FMemSize );
FMemSize := 0;
FMaxIndex := 0;
FItemSize := 0;
End; { If }
inherited Destroy;
End; { T64KArray.Destroy }
(* Invalidate all used items and fill the memory with 0 *)
Procedure T64KArray.Zap;
Begin
InvalidateItems( 0, Count );
MemFill( Memory, Capacity*ItemSize, 0 );
End; { T64KArray.Zap }
{************************************************************
* T64KArray.Clone
*
* Parameters:
* none
* Returns:
* Pointer to a freshly minted exact copy of this object
* Description:
* Creates a new object of the same type as this one is and
* copies the arrays contents to the new object via AssignTo.
* If the actual class type stores pointers to other stuff it
* is the responsibility of that class to override the BlockCopy
* method used by AssignTo to implement a deep copy.
* Error Conditions:
* Construction of the new object may fail due to out of memory.
* The assign process may conceivably also fail, if it involves
* a deep copy. If that happens, the raised exception is trapped,
* the new object destroyed and the exception is reraised for
* handling at an upper level.
*
*Created: 04/18/95 16:46:35 by P. Below
************************************************************}
Function T64KArray.Clone: T64KArray;
Var
cI : C64KArray;
Begin
cI := C64KArray(Self.ClassType);
Result := cI.Create( Succ( FMaxIndex ), FItemSize );
try
AssignTo( Result );
except
on EOutOfMemory Do Begin
Result.Free;
Result := Nil;
raise
end;
end;
End; { T64KArray.Clone }
{************************************************************
* T64KArray.ReDim
*
* Parameters:
* newcount: number of items the new array should hold, cannot
* be 0! 0 is mapped to 1.
* Description:
* Reallocates the array to a new size. The old items are
* copied over, as far as possible. New slots are nulled out.
* If the new array is smaller than the old one the extra
* items are invalidated so a derived class can do cleanup
* on them.
* Error Conditions:
* ReAllocMem, the RTL function used, may raise an out of memory
* exception.
* If compiled with debugging on ($D+) we will raise an ERangeError
* exception, if the requested size is > 64K and we are compiling
* for Win16.
*
*Created: 04/18/95 17:12:12 by P. Below
************************************************************}
Procedure T64KArray.ReDim( newcount: Cardinal );
{$IFNDEF WIN32}
Var
s: LongInt;
{$ENDIF}
Begin
If newcount = 0 Then Inc( newcount );
If newcount <> Succ( FMaxIndex ) Then Begin
If newcount < Succ( FMaxIndex ) Then
InvalidateItems( newcount, FMaxIndex-newcount+1 )
{$IFNDEF WIN32}
Else Begin
s := LongInt( newcount ) * itemsize;
If s >= $10000 Then Begin
(* user has math problems, be gracious and reduce newcount
to allowed value *)
newcount := GetMaxCapacity;
{$IFOPT D+}
(* raise an exception, if compiled for debugging *)
raise
ERangeError.Create( ErrSegmentOverflow );
{$ENDIF}
End; { If }
End
{$ENDIF}
;
FMemory := ReAllocMem( FMemory,
Succ( FMaxIndex )*FItemSize,
newcount * FItemSize );
FMemSize := newcount* FItemSize;
FMaxIndex := Pred( newcount );
SortOrder := TS_NONE;
End; { If }
End; { T64KArray.ReDim }
{************************************************************
* T64KArray.InvalidateItems
*
* Parameters:
* atIndex: index of first item about to be nuked
* numItems: number of items effected
* Description:
* This method is called is items are about to be deleted from
* the array. It does nothing for this class but descendants
* storing pointers or objects may use it to perform cleanup
* tasks for the items about to be deleted.
* Error Conditions:
* none
*
*Created: 04/19/95 16:48:42 by P. Below
************************************************************}
Procedure T64KArray.InvalidateItems( atIndex, numItems: Cardinal );
Begin
{ This is a NOP for this class }
End; (* T64KArray.InvalidateItems *)
{************************************************************
* T64KArray.ValidIndex
*
* Parameters:
* atIndex: an index value
* Returns:
* true if the index is in range, false otherwise
* Description:
* This method is used by a couple of others to validate an
* index.
* Error Conditions:
* If Index is > MaxIndex the method will raise a ERangeError
* exception, if range checking is on, or return false if range
* checking is off.
*
*Created: 04/19/95 16:58:57 by P. Below
************************************************************}
Function T64KArray.ValidIndex( Index: Cardinal ): Boolean;
Begin
Result := True;
If Index > FMaxIndex Then Begin
{$IFOPT R+}
raise ERangeError.CreateFmt( ErrIndex, [ index, FMaxIndex ] );
{$ENDIF}
Result := False;
End { If }
End;
{************************************************************
* T64KArray.ValidateBounds
*
* Parameters:
* atIndex: an index value
* numItems: a item count value
* Returns:
* true if the index is in range, false otherwise
* Description:
* This method is used by a couple of others to validate an
* index and make sure that numItems is not higher than the
* number of items from position atIndex on to the end of array.
* Error Conditions:
* If atIndex is > MaxIndex the method will raise a ERangeError
* exception, if range checking is on, or return false if range
* checking is off.
*
* If the numItem parameter is larger than the number of items
* present after position atIndex (inclusive) it is adjusted to
* the maximal number of items possible.
*
*
*Created: 04/19/95 16:58:57 by P. Below
************************************************************}
Function T64KArray.ValidateBounds( atIndex: Cardinal;
Var numItems: Cardinal): Boolean;
Begin
Result := ValidIndex( atIndex );
If Result Then
If ( numItems > Succ( FMaxIndex )) or
(( maxIndex-numItems+1 ) < atIndex ) Then
numItems := FMaxIndex - atIndex + 1;
End; (* T64KArray.ValidateBounds *)
{************************************************************
* T64KArray.Insert
*
* Parameters:
* Source : the source of the new items to insert
* atIndex: index to insert the new items at
* numItems: number of items to insert
* Description:
* This method inserts the passed items, moving all items from
* position atIndex and up numItems positions upwards. The array
* grows as needed, if the ability flag AF_AutoSize is set.
* If it cannot grow ( enough ), items may fall off
* the end! If atIndex is beyond the end of array, Append is used.
* Error Conditions:
* If the method is asked to insert more items than can fit, the
* numItems parameter is adjusted to the maximal number of items
* possible without an exception beeing raised. Redim is used to
* grow the array, to EOutOfMemory is a distinct posibility.
*
*Created: 04/19/95 16:03:29 by P. Below
************************************************************}
Procedure T64KArray.Insert( Var Source; atIndex, numItems: Cardinal );
Var
oldCapacity, itemsToMove, moveTargetIndex, lostItems: Cardinal;
Begin
If numItems = 0 Then
Exit;
(* check 1: if AtIndex is beyond the end of array, we effectively
do an append! *)
If atIndex > MaxIndex Then Begin
Append( Source, numItems );
Exit;
End; { If }
oldCapacity := Capacity;
(* resize the array, this may not succeed completely if the array
would need to grow > 64K in Win16! In that case it will grow
to the maximal possible size. *)
If HasFlag( AF_AutoSize ) Then
Redim( oldCapacity+numItems );
(* check2: correct numItems downwards, if the array could not been
grown to the required size. Note one pathological case here: if
the original atIndex was > MaxIndex AND the array was already
at maximal size, we will run into an invalid index error on the
next statement and end up doing nothing! *)
If ValidateBounds( atIndex, numItems ) Then Begin
(* move the items after the insertion point up to make room for
the new items. *)
itemsToMove := oldCapacity - atIndex;
If itemsToMove > 0 Then Begin
moveTargetIndex := atIndex + numItems;
(* Note: ValidateBounds makes sure that moveTargetIndex is at
max MaxIndex+1 ( =Capacity )! *)
(* check 3: if any items fall off at the end, invalidate them
and reduce the number to move accordingly. *)
If ( Capacity - moveTargetIndex ) < itemsToMove Then Begin
lostItems := itemsToMove + moveTargetIndex - Capacity;
InvalidateItems( atIndex + itemsToMove-lostItems, lostItems );
itemsToMove := itemsToMove - lostItems;
End; { If }
(* move the items beyond the end of insertion range up *)
MemMove( GetItemPtr( atIndex ),
GetItemPtr( moveTargetIndex ),
itemsToMove * ItemSize );
(* now null out the places where we will put the new items.
this is necessary to prevent the InvalidateItems call for
these items CopyFrom will do from barfing, if the items
are pointers, for example. *)
MemFill( GetItemPtr( atIndex ), numItems*ItemSize, 0 );
End; { If }
(* move the items to insert into the array *)
CopyFrom( Source, atIndex, numItems );
End; { If }
End; { T64KArray.Insert }
{************************************************************
* T64KArray.Delete
*
* Parameters:
* atIndex: index to start deleting items
* numItems: number of items to delete
* Description:
* This method deletes items by moving all items above the
* requested range down numItems slots and redims the array
* to the smaller size, if the ability flag AF_AutoSize is set.
* The deleted items are invalidated first,
* so descendant class storing pointers or objects can free the storage
* for the deleted items or do other cleanup tasks, as appropriate.
* Error Conditions:
* If atIndex is > MaxIndex the method will raise a ERangeError
* exception, if range checking is on, or do nothing if range
* checking is off.
*
* If the method is asked to delete more items than there are, the
* numItems parameter is adjusted to the maximal number of items
* possible without an exception beeing raised.
*
*Created: 04/19/95 16:37:34 by P. Below
************************************************************}
Procedure T64KArray.Delete( atIndex, numItems: Cardinal );
Begin
If numItems = 0 Then
Exit;
If ValidateBounds( atIndex, numItems ) Then Begin
(* invalidate the items about to be deleted so a derived class
can do cleanup on them. *)
InvalidateItems( atIndex, numItems );
(* move the items above those we delete down, if there are any *)
If ( atIndex+numItems ) <= MaxIndex Then
MemMove( GetItemPtr( atIndex+numItems ),
GetItemPtr( atIndex ),
( maxIndex-atIndex-numItems+1 )*ItemSize );
(* null out the now free slots *)
MemFill( GetItemPtr( MaxIndex-numItems+1 ),
numItems*ItemSize, 0 );
If HasFlag( AF_AutoSize ) Then
Redim( Capacity - numItems );
End; { If }
End; { T64KArray.Delete }
{************************************************************
* T64KArray.Append
*
* Parameters:
* Source : the source of the new items to append
* numItems: number of items to append
* Description:
* This method appends the passed items. The array
* grows as needed. If it cannot grow enough, not all items may be
* copied! Note that this method is independant of the settings
* of the AF_AutoSize ability flag!
* Error Conditions:
* If the method is asked to append more items than can fit, the
* numItems parameter is adjusted to the maximal number of items
* possible without an exception beeing raised. Redim may raise
* an EOutOfMemory exception.
*
*Created: 04/19/95 16:03:29 by P. Below
************************************************************}
Procedure T64KArray.Append( Var Source; numItems: Cardinal );
Var
n: Cardinal;
Begin
n := Capacity;
Redim( n+numItems );
CopyFrom( Source, n, numItems );
End; { T64KArray.Append }
{************************************************************
* T64KArray.CopyFrom
*
* Parameters:
* Source: source of the items to be copied
* toIndex: index for the first copied item
* numItems: number of items to copy
* Description:
* This methods overwrites the next numItems items in this array
* starting at position toIndex with items from the Source. The
* overwritten items are invalidated first.
* Error Conditions:
* If toIndex is > MaxIndex the method will raise a ERangeError
* exception, if range checking is on, or do nothing if range
* checking is off. If the Source memory contains less than the
* specified number of items to copy a protection fault may result.
*
* If the method is asked to copy more items than will fit, the
* numItems parameter is adjusted to the maximal number of items
* possible without an exception beeing raised.
*
*
*Created: 04/19/95 17:14:49 by P. Below
************************************************************}
Procedure T64KArray.CopyFrom( Var Source; toIndex, numItems: Cardinal );
Begin
If numItems = 0 Then
Exit;
If ValidateBounds( toIndex, numItems ) Then Begin
(* invalidate the items about to be overwritten so a derived class
can do cleanup on them. *)
InvalidateItems( toIndex, numItems );
(* do the copy *)
MemMove( @Source,
GetItemPtr(toIndex ),
numItems*ItemSize );
SortOrder := TS_NONE;
End; { If }
End; { T64KArray.CopyFrom }
{************************************************************
* T64KArray.CopyTo
*
* Parameters:
* Dest: memory to copy items to
* fromIndex: index of first item to copy
* numItems: number of items to copy
* Description:
* This method copies items from this array to a memory target.
* WARNING!
* This may be a problem if the copied items are pointers or
* objects, since this is a shallow copy and the result will
* be several references to the same memory locations! A derived
* class may have to override this method to deal with this problem.
* Error Conditions:
* If fromIndex is > MaxIndex the method will raise a ERangeError
* exception, if range checking is on, or do nothing if range
* checking is off. If the Dest memory can hold less than the
* specified number of items to copy a protection fault may result.
*
* If the method is asked to copy more items than there are, the
* numItems parameter is adjusted to the maximal number of items
* possible without an exception beeing raised.
*
*Created: 04/19/95 17:19:07 by P. Below
************************************************************}
Procedure T64KArray.CopyTo( Var Dest; fromIndex, numItems: Cardinal );
Begin
If numItems = 0 Then
Exit;
If ValidateBounds( fromIndex, numItems ) Then Begin
MemMove( GetItemPtr( fromIndex ), @Dest,
numItems*ItemSize );
End; { If }
End; { T64KArray.CopyTo }
{************************************************************
* T64KArray.BlockCopy
*
* Parameters:
* Source: an array object instance to copy items from
* fromIndex: index in source of first item to copy
* toIndex: index in self to copy the first item to
* numitems: number of items to copy
* Description:
* Uses CopyFrom to do the actual copy process after doing a
* few sanity checks on the source. CopyFrom does the checks
* on the target. The numitems count may be reduced if either
* the source does not have that many items or Self cannot take
* them.
* Error Conditions:
* Will raise a ETypeMismatch exception if the Source object is
* not of the same or a derived type as Self and also if it has
* a different item size. ERangeError exceptions may be raised
* by called methods.
*
*Created: 04/19/95 17:57:41 by P. Below
************************************************************}
Procedure T64KArray.BlockCopy( Source: T64KArray;
fromIndex, toIndex, numitems: Cardinal );
Begin
If numitems = 0 Then
Exit;
If ( Source Is ClassType ) and ( ItemSize = Source.ItemSize ) Then
Begin
If Source.ValidateBounds( fromIndex, numItems ) Then
CopyFrom( Source.GetItemPtr( fromIndex )^, toIndex, numItems )
End
Else
raise ETypeMismatch.Create( ErrAssign );
End; { T64KArray.BlockCopy }
{************************************************************
* T64KArray.Sort
*
* Parameters:
* ascending: defines whether to sort in ascending or descending
* order
* Description:
* This method implements a recursive QuickSort. It can only
* do its work if a comparison function has been assigned to
* the FCompareProc field. Since this is a generic procedure
* to sort any kind of data, it is possible to get a much
* better performance for specific data types by reimplementing
* the Sort for this type.
* Error Conditions:
* Will raise a ECompUndefined exception if no comparison function
* has been defined. Them method may also run out of memory
* in GetMem while allocating the pivot data buffer.
*
*Created: 04/22/95 16:02:24 by P. Below
************************************************************}
Procedure T64KArray.Sort( ascending: Boolean );
Procedure QSort( L, R: Cardinal );
Var
i, j: Cardinal;
pPivot: Pointer;
Begin
i:= L;
j:= R;
GetMem( pPivot, ItemSize );
try
GetItem( ( L+R ) div 2, pPivot^ );
Repeat
If ascending Then Begin
While FCompareProc( GetItemPtr( i )^, pPivot^ ) < 0 Do
Inc( i );
While FCompareProc( pPivot^, GetItemPtr( j )^ ) < 0 Do
Dec( j );
End { If }
Else Begin
While FCompareProc( GetItemPtr( i )^, pPivot^ ) > 0 Do
Inc( i );
While FCompareProc( pPivot^, GetItemPtr( j )^ ) > 0 Do
Dec( j );
End; { Else }
If i <= j Then Begin
MemSwap( GetItemPtr( i ), GetItemPtr( j ), ItemSize );
Inc( i );
If j > 0 Then Dec( j );
End; { If }
Until i > j ;
If L < j Then QSort( L, j );
If i < R Then QSort( i, R );
finally
FreeMem( pPivot, ItemSize );
end;
End; { QSort }
Begin { Sort }
(* do we have anything to do? *)
If ( Count = 0 ) or not HasFlag( AF_CanCompare ) Then
Exit;
If ascending Then
If ( SortOrder = TS_ASCENDING ) Then Exit
Else
Else
If ( SortOrder = TS_DESCENDING ) Then Exit;
(* ok, _can_ we do it? *)
If @FCompareProc <> Nil Then Begin
QSort( 0, Pred( Count ));
If ascending Then
SortOrder := TS_ASCENDING
Else
SortOrder := TS_DESCENDING;
End
Else
raise ECompUndefined.Create( ErrCompare );
End; { T64KArray.Sort }
{************************************************************
* T64KArray.Find
*
* Parameters:
* value: item to search for in the array
* Returns:
* index of found item or NOT_FOUND when the value is not in array.
* Description:
* Depending on the sort state of the array this Function will do
* a binary or sequential search thru the array, using the
* comparison function supplied in FCompareProc to compare value
* to the current item.
* WARNING!
* If the list is sorted and contains multiple instances of the same
* value, the search will not necessarily find the _first_ instance
* of this value! This is a general shortcome of binary search; set
* SortOrder to TS_NONE before the search to force sequential search
* if the array contains multiple copies of the same value.
*
* Like for the Sort method descendants may gain a considerable
* improvement in performance if they reimplement this method with
* optimized data access and comparison.
* Error Conditions:
* Will raise a ECompUndefined exception if no comparison function
* has been defined.
*
*
*Created: 04/22/95 16:31:13 by P. Below
************************************************************}
Function T64KArray.Find( Var value ): Cardinal;
Function LinearSearch: Cardinal;
Var
i: Cardinal;
p: Pointer;
Begin
Result := NOT_FOUND;
p := FMemory;
For i:= 0 To Pred( Count ) Do Begin
If FCompareProc( value, p^ ) = 0 Then Begin
Result := i;
Break;
End; { If }
Inc( PtrRec( p ).ofs, ItemSize );
End; { For }
End; { LinearSearch }
Function BinarySearch: Cardinal;
Var
u,l,i: Cardinal;
n : Integer;
asc : Boolean;
Begin
Result := NOT_FOUND;
l := 0;
u := Pred( Count );
asc := SortOrder = TS_ASCENDING;
While l <= u Do Begin
i := ( l+u ) div 2;
n := FCompareProc( value, GetItemPtr( i )^);
If n = 0 Then Begin
Result := i;
Break;
End;
If l = u Then
Break;
If asc xor ( n < 0 )Then
l := i
Else
u := i;
End; { While }
End; { BinarySearch }
Begin { Find }
Result := NOT_FOUND;
If ( Count = 0 ) or not HasFlag( AF_CanCompare ) Then
Exit;
If @FCompareProc <> Nil Then Begin
If SortOrder = TS_NONE Then
Result := LinearSearch
Else
Result := BinarySearch;
End
Else
raise ECompUndefined.Create( ErrCompare );
End; { T64KArray.Find }
{************************************************************
* T64KArray.ForEach
*
* Parameters:
* iterator: an object method adhering to the TIteratorMethod
* prototype defined in the Interface.
* processMsg: this flag deterimines whether the method will
* call Application.ProcessMessages inside the iterator
* loop
* intervall: determines how often ProcessMessages is called, a
* higher number means messages will be processed less
* often since the method uses (index mod intervall)=0
* as trigger to call ProcessMessages.
* Description:
* The method loops over all entries of the array and passes the
* address of each with its index to the iterator method.
* If processMsg = True, the method will call ProcessMessages on each
* intervall'th round of the loop. Note that this only happens when
* this Unit has been compiled with the symbol DOEVENTS defined!
* Error Conditions:
* The method has no error conditions per se but horrible things will
* happen if you call it with a Nil iterator since we do not check
* for this condition!
*
*Created: 04/22/95 17:07:27 by P. Below
************************************************************}
Procedure T64KArray.ForEach( iterator: TIteratorMethod; processMsg: Boolean;
intervall: Cardinal );
Var
i: Cardinal;
p: Pointer;
Begin
p := FMemory;
For i:= 0 To Pred( Count ) Do Begin
Iterator( p^, i );
Inc( PtrRec( p ).ofs, ItemSize );
{$IFDEF DOEVENTS}
If processMsg and (( i mod intervall ) = 0) Then
Application.ProcessMessages;
{$ENDIF}
End; { For }
End; { T64KArray.ForEach }
{************************************************************
* T64KArray.FirstThat
*
* Parameters:
* locator: an object method adhering to the TLocatorMethod
* prototype defined in the Interface.
* processMsg: this flag determines whether the method will
* call Application.ProcessMessages inside the iterator
* loop
* intervall: determines how often ProcessMessages is called, a
* higher number means messages will be processed less
* often since the method uses (index mod intervall)=0
* as trigger to call ProcessMessages.
* Returns:
* The address of the item for which the locator returned True, or
* Nil if it returned False for all items.
* Description:
* The method loops over all entries of the array and passes the
* address of each with its index to the locator method. The loop
* terminates immediately when the locator method returns True.
* If processMsg = True, the method will call ProcessMessages on each
* intervall'th round of the loop. Note that this only happens when
* this Unit has been compiled with the symbol DOEVENTS defined!
* Error Conditions:
* The method has no error conditions per se but horrible things will
* happen if you call it with a Nil locator since we do not check
* for this condition!
*
*Created: 04/22/95 17:07:27 by P. Below
************************************************************}
Function T64KArray.FirstThat( locator: TLocatorMethod;
processMsg: Boolean;
intervall: Cardinal ): Pointer;
Var
i: Cardinal;
p: Pointer;
Begin
Result := Nil;
p := FMemory;
For i:= 0 To Pred( Count ) Do Begin
If Locator( p^, i ) Then Begin
Result := p;
Break
End;
Inc( PtrRec( p ).ofs, ItemSize );
{$IFDEF DOEVENTS}
If processMsg and (( i mod intervall ) = 0) Then
Application.ProcessMessages;
{$ENDIF}
End; { For }
End; { T64KArray.FirstThat }
{************************************************************
* T64KArray.LastThat
*
* Parameters:
* locator: an object method adhering to the TLocatorMethod
* prototype defined in the Interface.
* processMsg: this flag determines whether the method will
* call Application.ProcessMessages inside the iterator
* loop
* intervall: determines how often ProcessMessages is called, a
* higher number means messages will be processed less
* often since the method uses (index mod intervall)=0
* as trigger to call ProcessMessages.
* Returns:
* The address of the item for which the locator returned True, or
* Nil if it returned False for all items.
* Description:
* The method loops over all entries of the array, starting with the
* last item working backwards, and passes the
* address of each with its index to the locator method. The loop
* terminates immediately when the locator method returns True.
* If processMsg = True, the method will call ProcessMessages on each
* intervall'th round of the loop. Note that this only happens when
* this Unit has been compiled with the symbol DOEVENTS defined!
* Error Conditions:
* The method has no error conditions per se but horrible things will
* happen if you call it with a Nil locator since we do not check
* for this condition!
*
*Created: 04/22/95 17:07:27 by P. Below
************************************************************}
Function T64KArray.LastThat(locator: TLocatorMethod;
processMsg: Boolean;
intervall: Cardinal ): Pointer;
Var
i: Cardinal;
p: Pointer;
Begin
Result := Nil;
p := GetItemPtr( Pred( Count ) );
For i:= Pred( Count ) DownTo 0 Do Begin
If Locator( p^, i ) Then Begin
Result := p;
Break
End;
Dec( PtrRec( p ).ofs, ItemSize );
{$IFDEF DOEVENTS}
If processMsg and (( i mod intervall ) = 0) Then
Application.ProcessMessages;
{$ENDIF}
End; { For }
End; { T64KArray.LastThat }
{************************************************************
* T64KArray.ForEachProc
*
* Parameters:
* iterator: a Procedure adhering to the TIterator
* prototype defined in the Interface.
* processMsg: this flag deterimines whether the method will
* call Application.ProcessMessages inside the iterator
* loop
* intervall: determines how often ProcessMessages is called, a
* higher number means messages will be processed less
* often since the method uses (index mod intervall)=0
* as trigger to call ProcessMessages.
* Description:
* The method loops over all entries of the array and passes the
* address of each with its index to the iterator method.
* If processMsg = True, the method will call ProcessMessages on each
* intervall'th round of the loop. Note that this only happens when
* this Unit has been compiled with the symbol DOEVENTS defined!
* Error Conditions:
* The method has no error conditions per se but horrible things will
* happen if you call it with a Nil iterator since we do not check
* for this condition!
*
*Created: 04/22/95 17:07:27 by P. Below
************************************************************}
Procedure T64KArray.ForEachProc( iterator: TIterator; processMsg: Boolean;
intervall: Cardinal );
Var
i: Cardinal;
p: Pointer;
Begin
p := FMemory;
For i:= 0 To Pred( Count ) Do Begin
Iterator( p^, i );
Inc( PtrRec( p ).ofs, ItemSize );
{$IFDEF DOEVENTS}
If processMsg and (( i mod intervall ) = 0) Then
Application.ProcessMessages;
{$ENDIF}
End; { For }
End; { T64KArray.ForEachProc }
{************************************************************
* T64KArray.FirstThatProc
*
* Parameters:
* locator: a Function adhering to the TLocator
* prototype defined in the Interface.
* processMsg: this flag determines whether the method will
* call Application.ProcessMessages inside the iterator
* loop
* intervall: determines how often ProcessMessages is called, a
* higher number means messages will be processed less
* often since the method uses (index mod intervall)=0
* as trigger to call ProcessMessages.
* Returns:
* The address of the item for which the locator returned True, or
* Nil if it returned False for all items.
* Description:
* The method loops over all entries of the array and passes the
* address of each with its index to the locator method. The loop
* terminates immediately when the locator method returns True.
* If processMsg = True, the method will call ProcessMessages on each
* intervall'th round of the loop. Note that this only happens when
* this Unit has been compiled with the symbol DOEVENTS defined!
* Error Conditions:
* The method has no error conditions per se but horrible things will
* happen if you call it with a Nil locator since we do not check
* for this condition!
*
*Created: 04/22/95 17:07:27 by P. Below
************************************************************}
Function T64KArray.FirstThatProc( locator: TLocator;
processMsg: Boolean;
intervall: Cardinal ): Pointer;
Var
i: Cardinal;
p: Pointer;
Begin
Result := Nil;
p := FMemory;
For i:= 0 To Pred( Count ) Do Begin
If Locator( p^, i ) Then Begin
Result := p;
Break
End;
Inc( PtrRec( p ).ofs, ItemSize );
{$IFDEF DOEVENTS}
If processMsg and (( i mod intervall ) = 0) Then
Application.ProcessMessages;
{$ENDIF}
End; { For }
End; { T64KArray.FirstThatProc }
{************************************************************
* T64KArray.LastThatProc
*
* Parameters:
* locator: a Function adhering to the TLocator
* prototype defined in the Interface.
* processMsg: this flag determines whether the method will
* call Application.ProcessMessages inside the iterator
* loop
* intervall: determines how often ProcessMessages is called, a
* higher number means messages will be processed less
* often since the method uses (index mod intervall)=0
* as trigger to call ProcessMessages.
* Returns:
* The address of the item for which the locator returned True, or
* Nil if it returned False for all items.
* Description:
* The method loops over all entries of the array, starting with the
* last item working backwards, and passes the
* address of each with its index to the locator method. The loop
* terminates immediately when the locator method returns True.
* If processMsg = True, the method will call ProcessMessages on each
* intervall'th round of the loop. Note that this only happens when
* this Unit has been compiled with the symbol DOEVENTS defined!
* Error Conditions:
* The method has no error conditions per se but horrible things will
* happen if you call it with a Nil locator since we do not check
* for this condition!
*
*Created: 04/22/95 17:07:27 by P. Below
************************************************************}
Function T64KArray.LastThatProc(locator: TLocator;
processMsg: Boolean;
intervall: Cardinal ): Pointer;
Var
i: Cardinal;
p: Pointer;
Begin
Result := Nil;
p := GetItemPtr( Pred( Count ) );
For i:= Pred( Count ) DownTo 0 Do Begin
If Locator( p^, i ) Then Begin
Result := p;
Break
End;
Dec( PtrRec( p ).ofs, ItemSize );
{$IFDEF DOEVENTS}
If processMsg and (( i mod intervall ) = 0) Then
Application.ProcessMessages;
{$ENDIF}
End; { For }
End; { T64KArray.LastThatProc }
Procedure T64KArray.SetCompareProc( proc: TCompareProc );
Begin
FCompareProc := proc;
If @proc = Nil Then
ClearFlag( AF_CanCompare )
Else
SetFlag( AF_CanCompare );
End; { T64KArray.SetCompareProc }
(* The following methods manipulate the FFlags set of array
'abilites'. *)
Function T64KArray.HasFlag( aFlag: TArrayFlags ): Boolean;
Begin
Result := aFlag In Flags;
End; { T64KArray.HasFlag }
Procedure T64KArray.SetFlag( aFlag: TArrayFlags );
Begin
Include( FFLags, aFlag );
End; { T64KArray.SetFlag }
Procedure T64KArray.ClearFlag( aFlag: TArrayFlags );
Begin
Exclude( FFLags, aFlag );
End; { T64KArray.ClearFlag }
{+--------------------------
| Methods of TIntegerArray
+-------------------------}
Type
TIArray =Array[ 0..High( Cardinal ) div Sizeof( Integer )-1 ] of Integer;
PIArray = ^TIArray;
Constructor TIntegerArray.Create( itemcount, dummy: Cardinal );
Begin
inherited Create( itemcount, Sizeof( integer ));
CompareProc := CmpIntegers;
End; { TIntegerArray.Create }
Procedure TIntegerArray.PutData( index: Cardinal ; value: Integer );
Begin
{$IFOPT R+}
If ValidIndex( index ) Then Begin
{$ENDIF}
PIArray( FMemory )^[ index ] := value;
SortOrder := TS_NONE;
{$IFOPT R+}
End;
{$ENDIF}
End; { TIntegerArray.PutData }
Function TIntegerArray.GetData(index: Cardinal): Integer;
Begin
{$IFOPT R+}
If ValidIndex( index ) Then
{$ENDIF}
Result := PIArray( FMemory )^[ index ];
End; { TIntegerArray.GetData }
{+---------------------------
| Methods of TCardinalArray
+--------------------------}
Type
TCArray =Array[ 0..High( Cardinal ) div Sizeof( Cardinal )-1 ] of Cardinal;
PCArray = ^TCArray;
Constructor TCardinalArray.Create( itemcount, dummy: Cardinal );
Begin
inherited Create( itemcount, Sizeof( Cardinal ));
CompareProc := CmpCardinals;
End; { TCardinalArray.Create }
Procedure TCardinalArray.PutData( index: Cardinal ; value: Cardinal );
Begin
{$IFOPT R+}
If ValidIndex( index ) Then Begin
{$ENDIF}
PCArray( FMemory )^[ index ] := value;
SortOrder := TS_NONE;
{$IFOPT R+}
End;
{$ENDIF}
End; { TCardinalArray.PutData }
Function TCardinalArray.GetData(index: Cardinal): Cardinal;
Begin
{$IFOPT R+}
If ValidIndex( index ) Then
{$ENDIF}
Result := PCArray( FMemory )^[ index ];
End; { TCardinalArray.GetData }
{+---------------------------
| Methods of TLongIntArray
+--------------------------}
Type
TLArray = Array[ 0..High( Cardinal ) div Sizeof( LongInt )-1 ] of LongInt;
PLArray = ^TLArray;
Constructor TLongIntArray.Create( itemcount, dummy: Cardinal );
Begin
inherited Create( itemcount, Sizeof( LongInt ));
CompareProc := CmpLongs;
End; { TLongIntArray.Create }
Procedure TLongIntArray.PutData( index: Cardinal ; value: LongInt );
Begin
{$IFOPT R+}
If ValidIndex( index ) Then Begin
{$ENDIF}
PLArray( FMemory )^[ index ] := value;
SortOrder := TS_NONE;
{$IFOPT R+}
End;
{$ENDIF}
End; { TLongIntArray.PutData }
Function TLongIntArray.GetData(index: Cardinal): LongInt;
Begin
{$IFOPT R+}
If ValidIndex( index ) Then
{$ENDIF}
Result := PLArray( FMemory )^[ index ];
End; { TLongIntArray.GetData }
{+-----------------------
| Methods of TRealArray
+----------------------}
Type
TRArray = Array[ 0..High( Cardinal ) div Sizeof( Real )-1 ] of Real;
PRArray = ^TRArray;
Constructor TRealArray.Create( itemcount, dummy: Cardinal );
Begin
inherited Create( itemcount, Sizeof( Real ));
CompareProc := CmpReals;
End; { TRealArray.Create }
Procedure TRealArray.PutData( index: Cardinal ; value: Real );
Begin
{$IFOPT R+}
If ValidIndex( index ) Then Begin
{$ENDIF}
PRArray( FMemory )^[ index ] := value;
SortOrder := TS_NONE;
{$IFOPT R+}
End;
{$ENDIF}
End; { TRealArray.PutData }
Function TRealArray.GetData(index: Cardinal): Real;
Begin
{$IFOPT R+}
If ValidIndex( index ) Then
{$ENDIF}
Result := PRArray( FMemory )^[ index ];
End; { TRealArray.GetData }
{+-------------------------
| Methods of TSingleArray
+------------------------}
Type
TSArray = Array[ 0..High( Cardinal ) div Sizeof( Single )-1 ] of Single;
PSArray = ^TSArray;
Constructor TSingleArray.Create( itemcount, dummy: Cardinal );
Begin
inherited Create( itemcount, Sizeof( Single ));
CompareProc := CmpSingles;
End; { TSingleArray.Create }
Procedure TSingleArray.PutData( index: Cardinal ; value: Single );
Begin
{$IFOPT R+}
If ValidIndex( index ) Then Begin
{$ENDIF}
PSArray( FMemory )^[ index ] := value;
SortOrder := TS_NONE;
{$IFOPT R+}
End;
{$ENDIF}
End; { TSingleArray.PutData }
Function TSingleArray.GetData(index: Cardinal): Single;
Begin
{$IFOPT R+}
If ValidIndex( index ) Then
{$ENDIF}
Result := PSArray( FMemory )^[ index ];
End; { TSingleArray.GetData }
{+-------------------------
| Methods of TDoubleArray
+------------------------}
Type
TDArray = Array[ 0..High( Cardinal ) div Sizeof( Double )-1 ] of Double;
PDArray = ^TDArray;
Constructor TDoubleArray.Create( itemcount, dummy: Cardinal );
Begin
inherited Create( itemcount, Sizeof( Double ));
CompareProc := CmpDoubles;
End; { TDoubleArray.Create }
Procedure TDoubleArray.PutData( index: Cardinal ; value: Double );
Begin
{$IFOPT R+}
If ValidIndex( index ) Then Begin
{$ENDIF}
PDArray( FMemory )^[ index ] := value;
SortOrder := TS_NONE;
{$IFOPT R+}
End;
{$ENDIF}
End; { TDoubleArray.PutData }
Function TDoubleArray.GetData(index: Cardinal): Double;
Begin
{$IFOPT R+}
If ValidIndex( index ) Then
{$ENDIF}
Result := PDArray( FMemory )^[ index ];
End; { TDoubleArray.GetData }
{+---------------------------
| Methods of TExtendedArray
+--------------------------}
Type
TEArray = Array[ 0..High( Cardinal ) div Sizeof( Extended )-1 ] of Extended;
PEArray = ^TEArray;
Constructor TExtendedArray.Create( itemcount, dummy: Cardinal );
Begin
inherited Create( itemcount, Sizeof( Extended ));
CompareProc := CmpExtendeds;
End; { TExtendedArray.Create }
Procedure TExtendedArray.PutData( index: Cardinal ; value: Extended );
Begin
{$IFOPT R+}
If ValidIndex( index ) Then Begin
{$ENDIF}
PEArray( FMemory )^[ index ] := value;
SortOrder := TS_NONE;
{$IFOPT R+}
End;
{$ENDIF}
End; { TExtendedArray.PutData }
Function TExtendedArray.GetData(index: Cardinal): Extended;
Begin
{$IFOPT R+}
If ValidIndex( index ) Then
{$ENDIF}
Result := PEArray( FMemory )^[ index ];
End; { TExtendedArray.GetData }
{+--------------------------
| Methods of TPointerArray
+-------------------------}
Type
TPArray = Array [ 0..High( Cardinal ) div Sizeof( Pointer )-1 ] Of Pointer;
PPArray = ^TPArray;
Constructor TPointerArray.Create( itemcount, dummy: Cardinal );
Begin
inherited Create( itemcount, Sizeof( Pointer ));
Flags := [ AF_AutoSize ];
(* no comparison function can be assigned here since we do not
even know how large the memory areas are our pointers point to.
by default the array will also not own the data it collects. *)
End; { TPointerArray.Create }
{************************************************************
* TPointerArray.CopyFrom
*
* Parameters:
* Source: source of the items to be copied
* toIndex: index for the first copied item
* numItems: number of items to copy
* Description:
* This methods overwrites the next numItems items in this array
* starting at position toIndex with _copies_ (hopefully) of items
* from the Source. The overwritten items are invalidated first.
* The actual copy is done item by item with calls to the CloneItem
* method. The version provided with this calls will only do a shallow
* copy ( it has no idea about what the pointer point to ), so you
* should derive your own class with an overriden CloneItem method
* to get a deep copy.
* Error Conditions:
* If toIndex is > MaxIndex the method will raise a ERangeError
* exception, if range checking is on, or do nothing if range
* checking is off. If the Source memory contains less than the
* specified number of items to copy a protection fault may result.
*
* If the method is asked to copy more items than will fit, the
* numItems parameter is adjusted to the maximal number of items
* possible without an exception beeing raised.
*
*
*Created: 05/28/95 21:14:49 by P. Below
************************************************************}
Procedure TPointerArray.CopyFrom( Var Source; toIndex, numItems: Cardinal );
Var
i: Cardinal;
p: PPArray;
arr: TPArray absolute Source;
Begin
If numItems = 0 Then
Exit;
If ValidateBounds( toIndex, numItems ) Then Begin
(* invalidate the items about to be overwritten so a derived class
can do cleanup on them. *)
InvalidateItems( toIndex, numItems );
p := PPArray( Memory );
For i:= 0 To Pred( numItems ) Do
p^[ toIndex+i ] := CloneItem( arr[ i ] );
SortOrder := TS_NONE;
End; { If }
End; { TPointerArray.CopyFrom }
{************************************************************
* TPointerArray.CopyTo
*
* Parameters:
* Dest: memory to copy items to
* fromIndex: index of first item to copy
* numItems: number of items to copy
* Description:
* This method copies items from this array to a memory target.
* The items are copied one after the other using the CloneItem
* method. The version in this class does only a shallow copy
* (copies the pointer), since it has no idea what the pointers
* point to. You should override CloneItem in derived classes to
* get a deep copy.
* Error Conditions:
* If fromIndex is > MaxIndex the method will raise a ERangeError
* exception, if range checking is on, or do nothing if range
* checking is off. If the Dest memory can hold less than the
* specified number of items to copy a protection fault may result.
*
* If the method is asked to copy more items than there are, the
* numItems parameter is adjusted to the maximal number of items
* possible without an exception beeing raised.
*
*Created: 05/28/95 21:19:07 by P. Below
************************************************************}
Procedure TPointerArray.CopyTo( Var Dest; fromIndex, numItems: Cardinal );
Var
i: Cardinal;
p: PPArray;
arr: TPArray absolute Dest;
Begin
If numItems = 0 Then
Exit;
If ValidateBounds( fromIndex, numItems ) Then Begin
p := PPArray( Memory );
For i:= 0 To Pred( numItems ) Do
arr[ i ] := CloneItem( p^[ fromIndex+i ] );
End; { If }
End; { TPointerArray.CopyTo }
(* PutData implements the write access via the default Data property.
It first frees the pointer at index and then stores a pointer to
a _copy_ of the passed data into that slot. *)
Procedure TPointerArray.PutData( index: Cardinal ; value: Pointer );
Begin
If ValidIndex( index ) Then Begin
If ( PPArray( Memory )^[ index ] <> Nil ) and HasFlag( AF_OwnsData )
Then
FreeItem( PPArray( Memory )^[ index ] );
PPArray( Memory )^[ index ] := CloneItem( value );
SortOrder := TS_NONE;
End;
End; { TPointerArray.PutData }
(* returns pointer in slot index, or Nil, if the index is invalid. *)
Function TPointerArray.GetData(index: Cardinal): Pointer;
Begin
If ValidIndex( index ) Then
Result := PPArray( Memory )^[ index ]
Else
Result := Nil;
End; { TPointerArray.GetData }
Procedure TPointerArray.FreeItem( item: Pointer );
Begin
(* this is a nop for this class since we do not know what item
points to *)
End; { TPointerArray.FreeItem }
(* calls FreeItem of each of the items in range and sets the item
to nil *)
Procedure TPointerArray.InvalidateItems(atIndex, numItems: Cardinal);
Var
n: Cardinal;
p: Pointer;
Begin
If (numItems > 0) and HasFlag( AF_OwnsData ) Then
If ValidateBounds( atIndex, numItems ) Then
For n := atIndex To Pred( numItems+atIndex ) Do Begin
p:= AsPtr[ n ];
If p <> Nil Then Begin
FreeItem( p );
p := Nil;
PutItem(n, p);
End;
End; { For }
End; { TPointerArray.InvalidateItems }
(* this version of CloneItem does nothing since we have no info on
the memory item points to. A descendent class would override this
method to provide a deep copy of item *)
Function TPointerArray.CloneItem( item: Pointer ): Pointer;
Begin
Result := item;
End; { TPointerArray.CloneItem }
(* since we need to save at least the number of items in the array in
addition to the data we take the easy way out and realize file
save via stream. *)
Procedure TPointerArray.SaveToFile( Const Filename: String );
Var
S: TFileStream;
Begin
S:= TFileStream.Create( Filename, fmCreate );
try
SaveToStream( S );
finally
S.Free
end;
End; { TPointerArray.SaveToFile }
Procedure TPointerArray.LoadFromFile( Const Filename: String );
Var
S: TFileStream;
Begin
S:= TFileStream.Create( Filename, fmOpenRead or fmShareDenyWrite );
try
LoadFromStream( S );
finally
S.Free
end;
End; { TPointerArray.LoadFromFile }
(* write first the size of the array then call SaveItemToStream for
each item *)
Procedure TPointerArray.SaveToStream( Stream: TStream );
Var
{$IFDEF WIN32}
temp: Cardinal;
{$ELSE}
temp: LongInt;
{$ENDIF}
n: Cardinal;
Begin
temp := Count;
With Stream Do Begin
Write( temp, Sizeof( temp ));
For n := 0 To MaxIndex Do Begin
SaveItemToStream( Stream, AsPtr[ n ] );
End; { For }
End; { With }
End; { TPointerArray.SaveToStream }
Procedure TPointerArray.LoadFromStream( Stream: TStream );
Var
{$IFDEF WIN32}
temp: Cardinal;
{$ELSE}
temp: LongInt;
{$ENDIF}
n: Cardinal;
P: Pointer;
Begin
With Stream Do Begin
Read( temp, Sizeof( temp ));
InvalidateItems( 0, Count );
Redim( temp );
For n := 0 To MaxIndex Do Begin
LoadItemfromStream( Stream, P );
(* we use PutItem here because otherwise we would end up
with a _copy_ of the data in P^ beeing stored, if
CopyItem implements deep copy! *)
PutItem( n, P );
End; { For }
End; { With }
End; { TPointerArray.LoadFromStream }
Procedure TPointerArray.SaveItemToStream( S: TStream; Item: Pointer );
Begin
raise
Exception.Create(
'Call to abstract method: TPointerArray.SaveItemToStream');
(* depends on data stored *)
End; { TPointerArray.SaveItemToStream }
Procedure TPointerArray.LoadItemFromStream( S: TStream; Var Item: Pointer );
Begin
raise
Exception.Create(
'Call to abstract method: TPointerArray.LoadItemFromStream');
End; { TPointerArray.LoadItemFromStream }
{+--------------------------
| Methods of TPcharArray
+-------------------------}
Constructor TPcharArray.Create( itemcount, dummy: Cardinal );
Begin
inherited Create( itemcount, Sizeof( Pointer ));
Flags := [ AF_OwnsData, AF_AutoSize, AF_CanCompare ];
CompareProc := CmpPChars;
End; { TPcharArray.Create }
Procedure TPcharArray.PutData( index: Cardinal; value: PChar );
Begin
inherited PutData( index, Pointer( value ));
End; { TPcharArray.PutData }
Function TPcharArray.GetData(index: Cardinal): PChar;
Begin
Result := inherited GetData( index );
End; { TPcharArray.GetData }
Function TPcharArray.CloneItem( item: Pointer ): Pointer;
Begin
If HasFlag( AF_OwnsData ) Then
If item <> Nil Then
Result := StrNew( item )
Else
Result := Nil
Else
Result := item;
End; { TPcharArray.CloneItem }
Procedure TPcharArray.FreeItem( item: Pointer );
Begin
If HasFlag( AF_OwnsData ) Then
StrDispose( item );
End; { TPcharArray.FreeItem }
Procedure TPcharArray.SaveItemToStream( S: TStream; Item: Pointer );
Var
pStr: PChar absolute Item;
len: Cardinal;
Begin
(* we write the pchar with length in front and including the
terminating zero! *)
If item = Nil Then
len := 0
else
len := StrLen( pStr );
S.Write( len, Sizeof( Len ));
If len > 0 Then
S.Write( pStr^, len+1 );
End; { TPcharArray.SaveItemToStream }
Procedure TPcharArray.LoadItemFromStream( S: TStream; Var Item: Pointer );
Var
len: Cardinal;
Begin
S.Read( len, Sizeof( len ));
If len > 0 Then Begin
Item := StrAlloc( len+1 );
S.Read( Item^, len+1 );
End { If }
Else
Item := Nil;
End; { TPcharArray.LoadItemFromStream }
Procedure TPcharArray.PutAsString( index: Cardinal; Const value: String );
Var
pStr: PChar;
Begin
pStr := StrAlloc( Length( value )+1 );
try
StrPCopy( pStr, value );
FreeItem( GetData( index ));
PutItem( index, pStr );
except
StrDispose( pStr );
raise
end;
End; { TPcharArray.PutAsString }
Function TPcharArray.GetAsString(index: Cardinal): String;
Var
pStr: PChar;
Begin
pStr := GetData( index );
If pStr <> Nil Then
Result := StrPas( pStr )
Else
Result := EmptyStr;
End; { TPcharArray.GetAsString }
Procedure TPcharArray.PutAsInteger( index: Cardinal; value: LongInt );
Begin
PutAsString( index, IntToStr( value ));
End; { TPcharArray.PutAsInteger }
Function TPcharArray.GetAsInteger(index: Cardinal): LongInt;
Begin
try
Result := StrToInt( GetAsString( index ));
except
Result := 0
end;
End; { TPcharArray.GetAsInteger }
Procedure TPcharArray.PutAsReal( index: Cardinal; value: Extended );
Begin
PutAsString( index, FloatToStr( value ));
End; { TPcharArray.PutAsReal }
Function TPcharArray.GetAsReal(index: Cardinal): Extended;
Begin
try
Result := StrToFloat( GetAsString( index ));
except
Result := 0.0
end;
End; { TPcharArray.GetAsReal }
Function GetFileSize( Const Filename: String): LongInt;
Var
SRec: TSearchRec;
Begin
If FindFirst( Filename, faAnyfile, SRec ) = 0 Then
Result := SRec.Size
Else
Result := 0;
FindClose(SRec);
End;
Procedure TPcharArray.LoadFromTextfile( Const Filename: String;
appendData: Boolean;
reporter: TProgressReporter );
Type
TLine = Array [ 0..$8000 ] Of Char;
TBuf = Array [ 0..$3fff ] of Char;
Var
pBuf: ^TBuf;
F: TextFile;
lines, currpos: Cardinal;
line: ^TLine;
fsize, sum: LongInt;
retain: Boolean;
Begin
(* open file for read *)
fsize := GetFilesize(Filename);
If fsize = 0 Then Exit;
AssignFile( F, Filename );
pBuf := Nil;
New(pBuf);
try
System.SetTextBuf( F, pBuf^, Sizeof( pBuf^ ));
Reset( F );
line := Nil;
try
New( line );
(* prepare array by blasting all items in it if we are not
asked to append the new data, set currpos to the first
index we put new data in *)
If not appendData Then Begin
Zap;
currpos := 0;
End { If }
Else
currpos := Count;
(* get a very rough estimate of the number of lines in the file *)
If (LongInt(MaxCapacity)*20) < FSize Then
lines := MaxCapacity
Else
lines := FSize div 20;
(* resize the array so the new lines will ( hopefully ) fit without
to many redims in between *)
If appendData Then
Redim( Count+lines )
Else
Redim( lines );
(* now start reading lines *)
sum := 0;
While not Eof( F ) Do Begin
ReadLn( F, line^ );
If currpos = Capacity Then
If currpos = MaxCapacity Then
raise EFileTooLarge.CreateFmt( ErrFileTooLarge, [filename] )
Else
Redim( Capacity+100 );
PutData( currpos, PChar(line) );
If @reporter <> Nil Then Begin
sum := sum+StrLen( PChar(line) )+2;
If not reporter( sum, fsize, retain ) Then Begin
If not retain Then
Delete( 0, currpos+1 );
Break;
End;
End;
Inc( currpos );
End; { While }
If currpos < Capacity Then
Redim( currpos );
finally
CloseFile( F );
If line <> Nil Then
Dispose( line );
end;
finally
Dispose( pBuf );
end;
End; { TPcharArray.LoadFromTextfile }
Procedure TPcharArray.SaveToTextfile( Const Filename: String;
appendData: Boolean;
reporter: TProgressReporter );
Type
TBuf = Array [ 0..$3fff ] of Char;
Var
pBuf: ^TBuf;
F: TextFile;
n: Cardinal;
total, sum: LongInt;
retain: Boolean;
p: PChar;
Begin
(* calculate total size of text to save, including CR-LF lineends *)
total := 0;
For n := 0 To Count-1 Do Begin
p := Data[ n ];
If p <> Nil Then
total := total + StrLen( p ) + 2
Else
Inc( total, 2 ); (* nil strings produce an empty line in the file *)
End; { For }
(* assign the file and give it a text buffer to speed up file I/O *)
AssignFile( F, Filename );
pBuf := Nil;
New(pBuf);
try
System.SetTextBuf( F, pBuf^, Sizeof( pBuf^ ));
(* open the file *)
If appendData Then
System.Append( F )
Else
System.Rewrite( F );
try
(* write the text *)
sum := 0;
retain := True;
For n := 0 To Count-1 Do Begin
p := Data[ n ];
If p <> Nil Then
WriteLn( F, p )
Else
WriteLn( F );
(* report progress if someone is listening *)
If @reporter <> Nil Then Begin
If p <> Nil Then
sum := sum+StrLen( p )+2
Else
Inc( sum, 2 );
If not reporter( sum, total, retain ) Then
break;
End; { If }
End; { For }
finally
CloseFile( F );
If not retain Then
Erase( F );
end;
finally
Dispose( pBuf );
end;
End; { TPcharArray.SaveToTextfile }
{+--------------------------
| Methods of TPStringArray
+-------------------------}
Constructor TPStringArray.Create( itemcount, dummy: Cardinal );
Begin
inherited Create( itemcount, Sizeof( Pointer ));
Flags := [ AF_OwnsData, AF_AutoSize, AF_CanCompare ];
CompareProc := CmpPStrings;
End; { TPStringArray.Create }
Procedure TPStringArray.PutData( index: Cardinal; Const value: String );
Begin
inherited PutData( index, @value )
End; { TPStringArray.PutData }
Function TPStringArray.GetData(index: Cardinal): String;
Var
p: Pointer;
Begin
p := inherited GetData( index );
If p = Nil Then
Result := EmptyStr
Else
Result := PString( p )^;
End; { TPStringArray.GetData }
Function TPStringArray.GetAsPtr(index: Cardinal): PString;
Begin
Result := PString( inherited GetData( index ));
End; { TPStringArray.GetAsPtr }
Function TPStringArray.CloneItem( item: Pointer ): Pointer;
Begin
If HasFlag( AF_OwnsData ) Then
If item <> Nil Then
Result := NewStr( PString( item )^ )
Else
Result := Nil
Else
Result := item;
End; { TPStringArray.CloneItem }
Procedure TPStringArray.FreeItem( item: Pointer );
Begin
If HasFlag( AF_OwnsData ) Then
DisposeStr( PString( item ));
End; { TPStringArray.FreeItem }
Procedure TPStringArray.SaveItemToStream( S: TStream; Item: Pointer );
Var
len: Cardinal;
Begin
If item = Nil Then
len := 0
else
len := Length( PString( item )^ );
If len > 0 Then
S.Write( Item^, len+1 )
Else
S.Write( len, 1 );
End; { TPStringArray.SaveItemToStream }
Procedure TPStringArray.LoadItemFromStream( S: TStream; Var Item: Pointer );
Var
Str : String;
Begin
S.Read( Str, 1 );
If Length( Str ) > 0 Then
S.Read( Str[ 1 ], Length( Str ));
Item := NewStr( Str );
End; { TPStringArray.LoadItemFromStream }
Procedure TPStringArray.PutAsPChar( index: Cardinal; value: PChar );
Begin
If value = Nil Then
PutData( index, EmptyStr )
Else
PutData( index, StrPas( value ));
End; { TPStringArray.PutAsPChar }
Function TPStringArray.GetAsPChar(index: Cardinal): PChar;
Var
pStr: PString;
Begin
pStr := GetAsPtr( index );
If pStr = Nil Then
Result := Nil
Else Begin
Result := StrAlloc( Length( pStr^ )+1 );
StrPCopy( Result, pStr^ );
End;
End; { TPStringArray.GetAsPChar }
Procedure TPStringArray.PutAsInteger( index: Cardinal; value: LongInt );
Begin
PutData( index, IntToStr( value ));
End; { TPStringArray.PutAsInteger }
Function TPStringArray.GetAsInteger(index: Cardinal): LongInt;
Begin
try
Result := StrToInt( GetData( index ));
except
Result := 0;
end;
End; { TPStringArray.GetAsInteger }
Procedure TPStringArray.PutAsReal( index: Cardinal; value: Extended );
Begin
PutData( index, FloatToStr( value ));
End; { TPStringArray.PutAsReal }
Function TPStringArray.GetAsReal(index: Cardinal): Extended;
Begin
try
Result := StrToFloat( GetData( index ));
except
Result := 0.0;
end;
End; { TPStringArray.GetAsReal }
Procedure TPStringArray.LoadFromTextfile( Const Filename: String;
appendData: Boolean;
reporter: TProgressReporter );
Type
TBuf = Array [ 0..$3fff ] of Char;
Var
pBuf: ^TBuf;
F: TextFile;
lines, currpos: Cardinal;
line: String;
fsize: LongInt;
sum: LongInt;
retain: Boolean;
Begin
(* open file for read *)
fsize := GetFilesize(Filename);
If fsize = 0 Then Exit;
AssignFile( F, Filename );
pBuf := Nil;
New(pBuf);
try
System.SetTextBuf( F, pBuf^, Sizeof( pBuf^ ));
Reset( F );
try
(* prepare array by blasting all items in it if we are not
asked to append the new data, set currpos to the first
index we put new data in *)
If not appendData Then Begin
Zap;
currpos := 0;
End { If }
Else
currpos := Count;
(* get a very rough estimate of the number of lines in the file. *)
If (LongInt(MaxCapacity)*20) < FSize Then
lines := MaxCapacity
Else
lines := FSize div 20;
(* resize the array so the new lines will ( hopefully ) fit without
to many redims in between *)
If appendData Then
Redim( Count+lines )
Else
Redim( lines );
(* now start reading lines *)
sum := 0;
While not Eof( F ) Do Begin
ReadLn( F, line );
If currpos = Capacity Then
If currpos = MaxCapacity Then
raise EFileTooLarge.CreateFmt( ErrFileTooLarge, [filename] )
Else
Redim( Capacity+100 );
PutData( currpos, line );
If @reporter <> Nil Then Begin
sum := sum+Length( line )+2;
If not reporter( sum, fsize, retain ) Then Begin
If not retain Then
Delete( 0, currpos+1 );
Break;
End;
End;
Inc( currpos );
End; { While }
If currpos < Capacity Then
Redim( currpos );
finally
CloseFile( F );
end;
finally
Dispose(pBuf);
end;
End; { TPStringArray.LoadFromTextfile }
Procedure TPStringArray.SaveToTextfile( Const Filename: String;
appendData: Boolean;
reporter: TProgressReporter );
Type
TBuf = Array [ 0..$3fff ] of Char;
Var
pBuf: ^TBuf;
F: TextFile;
n: Cardinal;
total, sum: LongInt;
retain: Boolean;
p: PString;
Begin
(* calculate total size of text to save, including CR-LF lineends *)
total := 0;
For n := 0 To Count-1 Do Begin
p := AsPString[n];
If p <> Nil Then
total := total + Length( p^ ) + 2
Else
Inc( total, 2 ); (* nil strings produce an empty line in the file *)
End; { For }
AssignFile( F, Filename );
pBuf := Nil;
New(pBuf);
try
System.SetTextBuf( F, pBuf^, Sizeof( pBuf^ ));
If appendData Then
System.Append( F )
Else
System.Rewrite( F );
try
(* write the text *)
sum := 0;
retain := True;
For n := 0 To Count-1 Do Begin
p := AsPString[n];
If p <> Nil Then
WriteLn( F, p^ )
Else
WriteLn( F );
(* report progress if someone is listening *)
If @reporter <> Nil Then Begin
If p <> Nil Then
sum := sum+Length( p^ )+2
Else
Inc( sum, 2 );
If not reporter( sum, total, retain ) Then
break;
End; { If }
End; { For }
finally
CloseFile( F );
If not retain Then
Erase( F );
end;
finally
Dispose( pBuf );
end;
End; { TPStringArray.SaveToTextfile }
{+----------------------
| Auxillary procedures
+----------------------}
Function CmpIntegers( Var item1, item2 ): Integer;
Var
i1: Integer absolute item1;
i2: Integer absolute item2;
Begin
Result := i1-i2;
End;
Function CmpCardinals( Var item1, item2 ): Integer;
Var
i1: Cardinal absolute item1;
i2: Cardinal absolute item2;
Begin
If i1 < i2 Then
Result := -1
Else
If i1 > i2 Then
Result := 1
Else
Result := 0;
End;
Function CmpLongs( Var item1, item2 ): Integer;
Var
i1: LongInt absolute item1;
i2: LongInt absolute item2;
Begin
If i1 < i2 Then
Result := -1
Else
If i1 > i2 Then
Result := 1
Else
Result := 0;
End;
Function CmpReals( Var item1, item2 ): Integer;
Var
i1: Real absolute item1;
i2: Real absolute item2;
r: Real;
Begin
r := i1-i2;
If Abs( r ) < 1.0E-30 Then
result := 0
Else
If r < 0 Then
result := -1
Else
result := 1;
End;
Function CmpSingles(Var item1, item2 ): Integer;
Var
i1: Single absolute item1;
i2: Single absolute item2;
r: Single;
Begin
r := i1-i2;
If Abs( r ) < 1.0E-30 Then
result := 0
Else
If r < 0 Then
result := -1
Else
result := 1;
End;
Function CmpDoubles(Var item1, item2 ): Integer;
Var
i1: Double absolute item1;
i2: Double absolute item2;
r: Double;
Begin
r := i1-i2;
If Abs( r ) < 1.0E-100 Then
result := 0
Else
If r < 0 Then
result := -1
Else
result := 1;
End;
Function CmpExtendeds( Var item1, item2 ): Integer;
Var
i1: Extended absolute item1;
i2: Extended absolute item2;
r: Extended;
Begin
r := i1-i2;
If Abs( r ) < 1.0E-3000 Then
result := 0
Else
If r < 0 Then
result := -1
Else
result := 1;
End;
Function CmpPChars( Var item1, item2 ): Integer;
Var
p1: PChar absolute item1;
p2: PChar absolute item2;
Begin
Result := lstrcmp( p1, p2 );
End;
Function CmpPStrings( Var item1, item2 ): Integer;
Var
p1: PString absolute item1;
p2: PString absolute item2;
Begin
Result := AnsiCompareStr( p1^, p2^ );
End;
End.