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.