Contributor: NFI EXPERIMENTAL PROGRAMMING
{-----------------------------------------------------------------------------}
{ A dynamic variable length record and array class for Delphi 2.0 }
{ Copyright 1996,97 NFI Experimental Programming. All Rights Reserved. }
{ This component can be freely used and distributed in commercial and private }
{ environments, provided this notice is not modified in any way and there is }
{ no charge for it other than nomial handling fees. Contact NFI directly for }
{ modifications to this agreement. }
{-----------------------------------------------------------------------------}
{ All correspondance concerning this file should be directed to }
{ NFI Experimental Programming, E-Mail }
{ nfi@post1.com }
{-----------------------------------------------------------------------------}
{ Date last modified: March 4, 1997 }
{-----------------------------------------------------------------------------}
{-----------------------------------------------------------------------------}
{ SORRY FOR THE LACK OF COMMENTING, but as this file was initially designed }
{ and documented "in-house" minimal commenting was deemed necessary. It was }
{ only after this file was requested by several individuals that we decided }
{ on releasing in to the general public. Thus commenting has escaped the }
{ grasp of this source. }
{-----------------------------------------------------------------------------}
{ ----------------------------------------------------------------------------}
{ TNFIRecordList v2.1 }
{ ----------------------------------------------------------------------------}
{ Description: }
{ The TNFIRecordList class operates similarly to the traditional Pascal }
{ TCollection object. It creates a list consisting of TNFIRecordItem }
{ objects capable of storing whatever information you require. }
{-----------------------------------------------------------------------------}
{ ----------------------------------------------------------------------------}
{ TNFIVarRec v2.1 }
{ ----------------------------------------------------------------------------}
{ Description: }
{ NOT TO BE CONFUSED WITH BORLANDS TVARREC type for variants!!! TNFIVarRec }
{ is a variable record class that allows easy data access. It does not }
{ store the internal makeup of information you have stored, so if you store }
{ an integer and two strings then you must remember that! }
{ }
{ Storing and retrieving this information from TNFIVarRec is relatively }
{ easy, take for example: }
{ }
{ I want to store an integer and two strings. How do I do this? }
{ STORING: }
{ NFIVarRec.vInteger := AnInteger; }
{ NFIVarRec.vString := AString1; }
{ NFIVarRec.vString := AString2; }
{ RETRIEVAL:
{ NFIVarRec.ResetPointer; // Point to the start of the record. Do NOT }
{ // call "Reset" as this will remove all data }
{ // contained within TNFIVarRec! }
{ AnInteger := NFIVarRec.vInteger; }
{ AString1 := NFIVarRec.vString; }
{ AString2 := NFIVarRec.vString; }
{ }
{ Note that you must read this information in exactly the same order as you }
{ stored it! }
{-----------------------------------------------------------------------------}
{ ----------------------------------------------------------------------------}
{ TLongArray 2.1 }
{ ----------------------------------------------------------------------------}
{ Description: }
{ TLongArray is a dynamic LongInt array object. Information can be accessed }
{ in the same manner as an array, for example LongArray[x] but supports }
{ item-index removal. If you remove the first item then all other items are }
{ brought down one index automatically. }
{ }
{ The biggest problem associated with this object is the fact that it }
{ ONE based, not Zero as is traditional with C/C++ or Delphi. This is }
{ because all in-house work using TLongArray is one based. If someone would }
{ like to post a fix, feel free to contact us and let us know! }
{ ----------------------------------------------------------------------------}
unit NFILists;
interface
uses SysUtils, Classes, Windows;
 {=======================================================================}
 { RECORD OBJECT TYPES }
 {=======================================================================}
 { TNFIRecordList }
 { This object is the generic record storage object. It maintains a }
 { maximum list of "MaxInt" records of type TNFIRecordItem. }
type
 TNFIVarRec = class;
 TNFIRecordItem = class(TObject)
 private
 Buffer: Pointer;
 RecordSize: Integer;
 public
 constructor Create(ARecord: Pointer; ASize: Integer);
 destructor Destroy; override;
 published
 property Data: Pointer read Buffer;
 property Size: Integer read RecordSize;
 end;
 TNFIRecordList = class(TObject)
 private
 FItems: TList;
 RecordCount: Integer;
 public
 constructor Create;
 destructor Destroy; override;
 procedure AddRecord(ARecord: Pointer; Size: Integer);
 procedure AddTNFIVarRec(ARecord: TNFIVarRec);
 procedure InsertRecord(ARecord: Pointer; AtPos, Size: Integer);
 procedure RemoveRecord(ARecNum: Integer);
 function GetRecord(ARecNum: Integer): Pointer;
 function GetTNFIVarRec(ARecNum: Integer; var ARecord: TNFIVarRec): Boolean;
 function GetRecordSize(ARecNum: Integer): Integer;
 procedure Clear;
 published
 property Items: TList read FItems;
 {property Size: Integer read RecordSize;}
 property Count: Integer read RecordCount;
 end;
 TNFIVarRec = class(TObject)
 private
 Memory: TMemoryStream;
 MemorySize: LongInt;
 Ident: Integer;
 { Ident is used as a "Contents Information" identifier }
 { -- As in "What in the heck did I store in this record ?" }
 procedure SetMemorySize(ASize: LongInt);
 function GetMemory: Pointer;
 function GetSize: LongInt;
 procedure SetByte(AByte: Byte);
 procedure SetShortInt(AShortInt: ShortInt);
 procedure SetInteger(AInteger: Integer);
 procedure SetWord(AWord: Word);
 procedure SetLongInt(ALongInt: LongInt);
 procedure SetString(AString: String);
 procedure SetTimeStamp(ATime: TDateTime);
 function GetByte: Byte;
 function GetShortInt: ShortInt;
 function GetInteger: Integer;
 function GetWord: Word;
 function GetLongInt: LongInt;
 function GetString: String;
 function GetTimeStamp: TDateTime;
 public
 constructor Create;
 destructor Destroy; override;
 procedure ResetPointer;
 procedure Reset;
 procedure Move(Source: Pointer; ASize: LongInt);
 { MOVE: Move converts the buffer to SOURCE, and over-writes existing information }
 procedure MoveItem(AnItem: TNFIRecordItem);
 { As with the move above, but uses a TNFIRecordItem object as the source. }
 function Append(Source: Pointer; ASize: LongInt): Boolean;
 { APPEND: Appends SOURCE to the end of the current information }
 { THESE BLOBS ARE NOT TRADITIONAL DELPHI DATABASE BLOB OBJECTS. BLOB REFERS TO ANY GENERIC }
 { BINARY OBJECT THAT CAN ONLY BE READ AND WRITTEN FROM MEMORY! IF YOU NEED TO STORE A }
 { BITMAP (FOR INSTANCE), YOU CAN READ AND WRITE IT USING APPENDBLOB AND READBLOBEX! NOTE }
 { THAT TNFIVARREC AUTOMATICALLY STORES THE BLOB SIZE SO WHEN READ THE EQUIVALENT AMOUNT }
 { OF MEMORY IS ALLOCATED. MAXIMUM BLOB SIZE IS 2 GB. }
 function AppendBlob(Source: Pointer; ASize: LongInt): Boolean;
 { APPENDBLOB: Appends SOURCE to the end of the current information storing the size of the BLOB}
 function ReadBlob(var Buffer: Pointer): LongInt;
 { READBLOB: Reads a BLOB record from the current position within the file. }
 { Buffer SHOULD NOT be assigned as ReadBlob will change it's location !!! }
 { Buffer is in fact set to point to the internal storage area, so prior }
 { to making any changes make sure that you have copied this information }
 { out! }
 function ReadBlobEx(var Buffer: Pointer): LongInt;
 { READBLOBEX: Creates a new memory buffer of the appropriate size and copies the }
 { contents of the BLOB into this new buffer. This allows the programmer }
 { to directly perform read/write operations on the buffer, unlike READBLOB }
 function AppendPChar(Source: PChar): Boolean;
 procedure ReadPChar(Buffer: PChar);
 function LoadFromFile(AFileName: String): Boolean;
 { Clears out all information prior to loading and points to START! }
 procedure LoadFromStream(AStream: TStream);
 procedure SaveToFile(AFileName: String);
 procedure SaveToStream(var AStream: TStream);
 property vByte: Byte read GetByte write SetByte;
 property vShortInt: ShortInt read GetShortInt write SetShortInt;
 property vInteger: Integer read GetInteger write SetInteger;
 property vWord: Word read GetWord write SetWord;
 property vLongInt: LongInt read GetLongInt write SetLongInt;
 property vLong: LongInt read GetLongInt write SetLongInt;
 property vString: String read GetString write SetString;
 property vTime: TDateTime read GetTimeStamp write SetTimeStamp;
 property Data: Pointer read GetMemory;
 property Size: LongInt read GetSize;
 property Capacity: LongInt read MemorySize write SetMemorySize;
 property ID: Integer read Ident write Ident default 0; // Set to zero anyway,
 end; // but hey...
 // TLongArray is one based!
 TLongArray = class
 private
 Buffer: Pointer;
 BufferSize, DataSize: LongInt;
 procedure Grow; // Conducts a 4k increment
 procedure Truncate; // Truncates the buffer at datasize
 function GetCount: LongInt;
 function GetAtPos(Index: Integer): LongInt;
 procedure ReplacePos(Index: Integer; AValue: LongInt);
 public
 constructor Create;
 destructor Destroy; override;
 procedure Reset;
 procedure Add(ALongInt: LongInt);
 procedure Remove(AtPos: LongInt);
 procedure Insert(AtPos: LongInt; ALongInt: LongInt);
 procedure Replace(AtPos: LongInt; ALongInt: LongInt);
 procedure Inc(AtPos, ANum: LongInt);
 procedure Dec(AtPos, ANum: LongInt);
 property At[Index: Integer]: LongInt read GetAtPos write ReplacePos; default;
 property Size: LongInt read DataSize;
 property Data: Pointer read Buffer;
 property Count: LongInt read GetCount;
 end;
implementation
 {=======================================================================}
 { ** TNFIRECORDITEM CODE }
 {=======================================================================}
constructor TNFIRecordItem.Create(ARecord: Pointer; ASize: Integer);
begin
 RecordSize := ASize;
 GetMem(Buffer, ASize);
 Move(ARecord^, Buffer^, RecordSize);
end;
destructor TNFIRecordItem.Destroy;
begin
 FreeMem(Buffer, RecordSize);
end;
 {=======================================================================}
 { ** TNFIRECORDLIST CODE }
 {=======================================================================}
constructor TNFIRecordList.Create;
begin
 RecordCount := 0;
 FItems := TList.Create;
end;
destructor TNFIRecordList.Destroy;
begin
 if FItems  nil then Clear;
 FItems.Free;
end;
procedure TNFIRecordList.AddRecord(ARecord: Pointer; Size: Integer);
var NewRecord: TNFIRecordItem;
 P: Pointer;
begin
 NewRecord := TNFIRecordItem.Create(ARecord, Size);
 FItems.Add(NewRecord);
 Inc(RecordCount);
end;
procedure TNFIRecordList.AddTNFIVarRec(ARecord: TNFIVarRec);
begin
 If Assigned(ARecord) Then
 AddRecord(ARecord.Data, ARecord.Size);
end;
// Returns a pointer to the data held by a TNFIRecordItem, not a pointer to the
// object itself.
 { The procedure itself uses "ARecord" because without it, the compiler gave }
 { me an error it informs me I shouldn't have. Anyway, it works now so... }
function TNFIRecordList.GetRecord(ARecNum: Integer): Pointer;
var ARecord: TNFIRecordItem;
begin
 If (RecordCount = 0) or (ARecNum < 1) or (ARecNum> RecordCount) Then ARecord := nil
 Else ARecord := FItems[ARecNum - 1];
 If ARecord  nil Then GetRecord := ARecord.Data Else GetRecord := nil;
end;
function TNFIRecordList.GetTNFIVarRec(ARecNum: Integer; var ARecord: TNFIVarRec): Boolean;
var P: Pointer;
begin
 GetTNFIVarRec := False;
 If Assigned(ARecord) Then
 begin
 P := GetRecord(ARecNum);
 ARecord.Move(P, GetRecordSize(ARecNum));
 GetTNFIVarRec := True;
 end;
end;
function TNFIRecordList.GetRecordSize(ARecNum: Integer): Integer;
begin
 If (RecordCount = 0) or (ARecNum < 1) or (ARecNum> RecordCount) Then GetRecordSize := -1
 Else GetRecordSize := TNFIRecordItem(FItems[ARecNum - 1]).Size;
end;
procedure TNFIRecordList.InsertRecord(ARecord: Pointer; AtPos, Size: Integer);
var NewRecord: TNFIRecordItem;
begin
 NewRecord := TNFIRecordItem.Create(ARecord, Size);
 FItems.Insert(AtPos - 1, NewRecord); // As this is a 1 based array, subtract
 Inc(RecordCount); // one from it as TList is zero-based.
end;
procedure TNFIRecordList.RemoveRecord(ARecNum: Integer);
var ARecord: TNFIRecordItem;
begin
 If (RecordCount> 0) and (ARecNum> 0) and (ARecNum <= RecordCount) Then begin ARecord := FItems[ARecNum - 1]; FItems.Delete(ARecNum - 1); ARecord.Free; Dec(RecordCount); end; end; procedure TNFIRecordList.Clear; var i: Integer; begin If RecordCount> 0 Then
 For i := RecordCount downto 1 Do
 RemoveRecord(i);
end;
 {=======================================================================}
 { ** TNFIVarRec CODE }
 {=======================================================================}
constructor TNFIVarRec.Create;
begin
 MemorySize := 8192;
 try
 Memory := TMemoryStream.Create;
 except
 Abort;
 end;
end;
destructor TNFIVarRec.Destroy;
begin
 If Assigned(Memory) Then Memory.Free;
end;
procedure TNFIVarRec.ResetPointer;
begin
 Memory.Position := 0;
end;
procedure TNFIVarRec.Reset;
begin
 ResetPointer;
 Memory.Clear;
end;
procedure TNFIVarRec.SetMemorySize(ASize: LongInt);
begin
 If Assigned(Memory) Then Memory.SetSize(ASize);
end;
procedure TNFIVarRec.Move(Source: Pointer; ASize: LongInt);
begin
 Reset;
 Memory.WriteBuffer(Source^, ASize);
end;
procedure TNFIVarRec.MoveItem(AnItem: TNFIRecordItem);
begin
 If Assigned(AnItem) Then
 Move(AnItem.Data, AnItem.Size);
end;
function TNFIVarRec.Append(Source: Pointer; ASize: LongInt): Boolean;
begin
 try
 Memory.WriteBuffer(Source^, ASize);
 Append := True;
 except
 Append := False;
 end;
end;
{ THESE BLOBS ARE NOT TRADITIONAL DELPHI DATABASE BLOB OBJECTS. BLOB REFERS TO ANY GENERIC }
{ BINARY OBJECT THAT CAN ONLY BE READ AND WRITTEN FROM MEMORY! IF YOU NEED TO STORE A }
{ BITMAP (FOR INSTANCE), YOU CAN READ AND WRITE IT USING APPENDBLOB AND READBLOB! NOTE }
{ THAT TNFIVARREC AUTOMATICALLY STORES THE BLOB SIZE SO WHEN READ THE EQUIVALENT AMOUNT }
{ OF MEMORY IS ALLOCATED. MAXIMUM BLOB SIZE IS 2 GB. }
function TNFIVarRec.AppendBlob(Source: Pointer; ASize: LongInt): Boolean;
begin
 try
 Memory.WriteBuffer(ASize, 4);
 Memory.WriteBuffer(Source^, ASize);
 AppendBlob := True;
 except
 AppendBlob := False;
 end;
end;
{ Please read the associated notes located at the class definition }
function TNFIVarRec.ReadBlob(var Buffer: Pointer): LongInt;
var BlobSize: LongInt;
begin
 try
 Memory.ReadBuffer(BlobSize, 4);
 Buffer := Pointer(LongInt(Memory.Memory) + Memory.Position);
 Memory.Position := Memory.Position + BlobSize;
 ReadBlob := BlobSize;
 except
 Buffer := nil;
 ReadBlob := -1;
 end;
end;
{ Please read the associated notes located at the class definition }
function TNFIVarRec.ReadBlobEx(var Buffer: Pointer): LongInt;
var BlobSize: LongInt;
 P: Pointer;
begin
 try
 Memory.ReadBuffer(BlobSize, 4);
 P := Pointer(LongInt(Memory.Memory) + Memory.Position);
 Buffer := AllocMem(BlobSize);
 System.Move(P^, Buffer^, BlobSize);
 Memory.Position := Memory.Position + BlobSize;
 ReadBlobEx := BlobSize;
 except
 Buffer := nil;
 ReadBlobEx := -1;
 end;
end;
function TNFIVarRec.LoadFromFile(AFileName: String): Boolean;
var FileStream: TFileStream;
begin
 LoadFromFile := False;
 try
 FileStream := TFileStream.Create(AFilename, fmOpenRead);
 Reset;
 Memory.CopyFrom(FileStream, FileStream.Size);
 Memory.Position := 0;
 LoadFromFile := True;
 finally
 If Assigned(FileStream) Then FileStream.Free;
 end;
end;
procedure TNFIVarRec.LoadFromStream(AStream: TStream);
begin
 Reset;
 Memory.CopyFrom(AStream, AStream.Size);
 Memory.Position := 0;
end;
procedure TNFIVarRec.SaveToFile(AFileName: String);
var FileStream: TFileStream;
begin
 try
 FileStream := TFileStream.Create(AFilename, fmCreate);
 ResetPointer; // NOT RESET! We want to keep all information!
 FileStream.CopyFrom(Memory, Memory.Size);
 finally
 If Assigned(FileStream) Then FileStream.Free;
 end;
end;
procedure TNFIVarRec.SaveToStream(var AStream: TStream);
begin
 ResetPointer;
 AStream.CopyFrom(Memory, Memory.Size);
end;
function TNFIVarRec.GetMemory: Pointer;
begin
 GetMemory := Memory.Memory;
end;
function TNFIVarRec.GetSize: LongInt;
begin
 GetSize := Memory.Size;
end;
procedure TNFIVarRec.SetByte(AByte: Byte);
begin
 Memory.WriteBuffer(AByte, 1);
end;
function TNFIVarRec.GetByte: Byte;
var AResult: Byte;
begin
 Memory.ReadBuffer(AResult, 1);
 GetByte := AResult;
end;
procedure TNFIVarRec.SetShortInt(AShortInt: ShortInt);
begin
 SetByte(AShortInt);
end;
function TNFIVarRec.GetShortInt: ShortInt;
begin
 GetShortInt := GetByte;
end;
procedure TNFIVarRec.SetWord(AWord: Word);
begin
 Memory.WriteBuffer(AWord, 2);
end;
procedure TNFIVarRec.SetInteger(AInteger: Integer);
begin
 SetWord(AInteger);
end;
function TNFIVarRec.GetInteger: Integer;
var AResult: Integer;
begin
 Memory.ReadBuffer(AResult, 2);
 GetInteger := AResult;
end;
function TNFIVarRec.GetWord: Word;
begin
 GetWord := GetInteger;
end;
procedure TNFIVarRec.SetLongInt(ALongInt: LongInt);
begin
 Memory.WriteBuffer(ALongInt, 4);
end;
function TNFIVarRec.GetLongInt: LongInt;
var AResult: LongInt;
begin
 Memory.ReadBuffer(AResult, 4);
 GetLongInt := AResult;
end;
procedure TNFIVarRec.SetString(AString: String);
var P: Pointer;
begin
 GetMem(P, Length(AString) + 1);
 StrPCopy(P, AString);
 Memory.WriteBuffer(P^, Length(AString) + 1);
 FreeMem(P);
end;
function TNFIVarRec.GetString: String;
var S: String;
 C: Char;
begin
 S := '';
 Repeat
 Memory.ReadBuffer(C, 1);
 If C  #0 Then S := S + C;
 Until C = #0;
 GetString := S;
end;
procedure TNFIVarRec.SetTimeStamp(ATime: TDateTime);
begin
 Memory.WriteBuffer(ATime, SizeOf(TDateTime));
end;
function TNFIVarRec.GetTimeStamp: TDateTime;
var AResult: TDateTime;
begin
 Memory.ReadBuffer(AResult, SizeOf(TDateTime));
 GetTimeStamp := AResult;
end;
function TNFIVarRec.AppendPChar(Source: PChar): Boolean;
begin
 try
 Memory.WriteBuffer(Source^, StrLen(Source) + 1); { Include the terminating #0 }
 AppendPChar := True;
 except
 AppendPChar := False;
 end;
end;
procedure TNFIVarRec.ReadPChar(Buffer: PChar);
begin
 try
 StrCopy(Buffer, Pointer(LongInt(Memory.Memory) + Memory.Position));
 except
 Buffer := nil;
 end;
end;
 {=======================================================================}
 { ** TLONGARRAY CODE }
 {=======================================================================}
constructor TLongArray.Create;
begin
 Reset;
end;
destructor TLongArray.Destroy;
begin
 If Assigned(Buffer) Then FreeMem(Buffer);
end;
procedure TLongArray.Reset;
begin
 If Assigned(Buffer) Then FreeMem(Buffer);
 BufferSize := 8192;
 Buffer := AllocMem(BufferSize);
 DataSize := 0;
end;
procedure TLongArray.Grow; { Add 4 extra kb to the end of the buffer }
begin
 System.Inc(BufferSize, 4096);
 ReAllocMem(Buffer, BufferSize);
end;
procedure TLongArray.Truncate;
begin
 ReAllocMem(Buffer, DataSize);
 BufferSize := DataSize;
end;
procedure TLongArray.Add(ALongInt: LongInt);
var P: ^LongInt;
begin
 If DataSize = BufferSize Then Grow;
 P := Buffer;
 System.Inc(LongInt(P), DataSize);
 P^ := ALongInt;
 System.Inc(DataSize, 4);
end;
procedure TLongArray.Remove(AtPos: LongInt);
var P, Q: Pointer;
 CopyAmount: LongInt;
begin
 If (DataSize> 0) and (AtPos <= (DataSize div 4)) Then begin P := Buffer; System.Inc(LongInt(P), AtPos * 4); // Point it past the record we are deleting Q := Buffer; System.Inc(Longint(Q), (AtPos - 1) * 4); // Point it to the one we are deleting CopyAmount := DataSize - (AtPos * 4); If CopyAmount> 0 Then
 System.Move(P^, Q^, CopyAmount);
 System.Dec(DataSize, 4);
 If DataSize> 8192 Then Truncate;
 end;
end;
procedure TLongArray.Insert(AtPos: LongInt; ALongInt: LongInt);
var P, TempBuffer: Pointer;
 Q: ^LongInt;
 CopyAmount: LongInt;
begin
 If (AtPos> 0) and ((AtPos - 1 ) * 4 <= DataSize) Then begin If BufferSize = DataSize Then Grow; P := Buffer; System.Inc(LongInt(P), (AtPos - 1) * 4); Q := P; CopyAmount := DataSize - ((AtPos - 1) * 4); If CopyAmount> 0 Then
 begin
 GetMem(TempBuffer, CopyAmount);
 System.Move(P^, TempBuffer^, CopyAmount);
 System.Inc(LongInt(P), 4);
 System.Move(TempBuffer^, P^, CopyAmount);
 FreeMem(TempBuffer);
 end;
 Q^ := ALongInt;
 System.Inc(DataSize, 4);
 end;
end;
function TLongArray.GetAtPos(Index: Integer): LongInt;
var P: ^LongInt;
begin
 GetAtPos := 0;
 If (Index> 0) and ((Index - 1) * 4 <= DataSize) Then begin P := Buffer; System.Inc(LongInt(P), (Index - 1) * 4); GetAtPos := P^; end; end; function TLongArray.GetCount: LongInt; begin If DataSize = 0 Then GetCount := 0 Else GetCount := DataSize div 4; end; procedure TLongArray.Replace(AtPos: LongInt; ALongInt: LongInt); var P: ^LongInt; begin If (AtPos> 0) and ((AtPos - 1) * 4 <= DataSize) Then
 begin
 P := Buffer;
 System.Inc(LongInt(P), (AtPos - 1) * 4);
 P^ := ALongInt;
 end;
end;
// Used for setting the "write" property of "At"
procedure TLongArray.ReplacePos(Index: Integer; AValue: LongInt);
begin
 Replace(Index, AValue);
end;
procedure TLongArray.Inc(AtPos, ANum: LongInt);
var i: LongInt;
begin
 i := At[AtPos] + ANum;
 Replace(AtPos, i);
end;
procedure TLongArray.Dec(AtPos, ANum: LongInt);
var i: LongInt;
begin
 i := At[AtPos] - ANum;
 Replace(AtPos, i);
end;
end.


AltStyle によって変換されたページ (->オリジナル) /