Contributor: E.L. LAGERBURG

{added by E.L. Lagerburg}


Unit U_Array;

{Dynamic array by E.L. Lagerburg from the Netherlands}


interface
  Uses SysUtils;

const MaxArray = MaxInt div 8;

Type

  PByteArray=^TByteArray;
  TByteArray=array[0..MaxArray] of byte ;

  TIndexEvent = Procedure(Sender:Tobject;Situation:Integer;Rec:Pointer;Index:Integer) of object;


  Tarray = Class(TObject)
  Private
     FOnForIndex:TIndexEvent;
     FOnForEach:TIndexEvent;
     FArray:PByteArray;
     FRecSize,
     FRecCapacity:Integer;
     FRecCount:Integer;
  Protected
     procedure SetCapacity(NewCapacity: Integer);
     Function GetSize:Integer;
     function Get(Index: Integer): Pointer;
     procedure Put(Index: Integer; Rec: Pointer);
     Procedure Error(Nr:Integer);
     procedure Grow;
     procedure SetCount(NewCount: Integer);
   Public
     Constructor Create(RecSize,RecCapacity:Integer);
     Destructor Destroy; override;
     function AddRecord(Rec:Pointer):Integer;
     Procedure ForEach(Situation:Integer);
     Procedure ForIndex(FromIndex,ToIndex,Situation:Integer);
     procedure DeleteRecord(Index: Integer);
     procedure MoveRecord(CurIndex, NewIndex: Integer);
     procedure InsertRecord(Index: Integer;Rec:Pointer);
     procedure ExchangeRecord(Index1, Index2: Integer);
     Procedure Clear;
     Property ByteArray:PByteArray read FArray;
     Property Count:Integer read FRecCount write SetCount;
     Property Size:Integer read GetSize;
     Property RecordSize:Integer read FRecSize;
     property Records[Index: Integer]: Pointer read Get write Put; default;
     Property OnForEach:TIndexEvent read FOnForEach write FOnForEach;
     Property OnForIndex:TIndexEvent read FOnForIndex write FOnForIndex;
   end;


   EArrayError = class(Exception);


implementation

Constructor TArray.Create(RecSize,RecCapacity:Integer);
Begin
  Inherited Create;
  FArray:=nil;
  FRecSize:=RecSize;
  FRecCapacity:=0;
  FRecCount:=0;
  SetCapacity(RecCapacity);
end;

Procedure TArray.Error(Nr:Integer);
Begin
  raise EArrayError.Create('Array index out of bounds '+intToStr(Nr));
End;

procedure TArray.SetCapacity(NewCapacity: Integer);
Begin
  if (NewCapacity < FRecCount) or (NewCapacity > MaxArray) then Error(1);
  if NewCapacity <> FRecCapacity then
  begin
    ReallocMem(FArray, NewCapacity * FRecSize);
    FRecCapacity := NewCapacity;
  end;
end;

Function TArray.AddRecord(Rec:Pointer):Integer;
begin
  Result := FRecCount;
  if Result = FRecCapacity then Grow;
  System.Move(Rec^,Farray^[FRecSize*FRecCount],FRecSize);
  inc(FRecCount);
end;

procedure TArray.InsertRecord(Index: Integer;Rec:Pointer);
begin
  if (Index < 0) or (Index > FRecCount) then Error(2);
  if FRecCount = FRecCapacity then Grow;
  if Index < FRecCount then
    System.Move(FArray^[FRecSize*Index],FArray^[FRecSize*Index+1],
      (FRecCount - Index) * FRecSize);
  System.Move(Rec^,Farray^[FRecSize*Index],FRecSize);
  Inc(FRecCount);
end;

procedure TArray.DeleteRecord(Index: Integer);
begin
  if (Index < 0) or (Index >= FRecCount) then Error(3);
  Dec(FRecCount);
  if Index < FRecCount then
    System.Move(FArray^[FRecSize*(Index + 1)],FArray^[FRecSize*Index],
      (FRecCount - Index) * FRecSize);
end;

procedure TArray.MoveRecord(CurIndex, NewIndex: Integer);
var
  Rec:PByteArray;
begin
  if CurIndex <> NewIndex then
  begin
    if (NewIndex < 0) or (NewIndex >= FRecCount) then Error(4);
    Rec:=nil;
    ReallocMem(Rec,FRecSize);
    System.Move(Farray^[FRecSize*CurIndex],Rec^,FRecSize);
    DeleteRecord(CurIndex);
    InsertRecord(NewIndex,Rec);
    ReallocMem(Rec,0);
  end;
end;

procedure TArray.ExchangeRecord(Index1, Index2: Integer);
var
  Rec:PByteArray;
begin
  if (Index1 < 0) or (Index1 >= FRecCount) or
    (Index2 < 0) or (Index2 >= FRecCount) then Error(5);
  Rec:=nil;
  ReallocMem(Rec,FRecSize);
  System.Move(Farray^[FRecSize*Index1],Rec^,FRecSize);
  System.Move(Farray^[FRecSize*Index2],Farray^[FRecSize*Index1],FRecSize);
  System.Move(Rec^,Farray^[FRecSize*Index2],FRecSize);
  ReallocMem(Rec,0);
end;

procedure TArray.SetCount(NewCount: Integer);
begin
  if (NewCount < 0) or (NewCount > MaxArray) then Error(6);
  if NewCount > FRecCapacity then SetCapacity(NewCount);
  if NewCount > FRecCount then
    FillChar(FArray^[FRecCount*FRecSize],(NewCount - FRecCount) * FRecSize, 0);
  FRecCount := NewCount;
end;

procedure TArray.Grow;
var
  Delta: Integer;
begin
  if FRecCapacity > 8 then Delta := 16 else
    if FRecCapacity > 4 then Delta := 8 else
      Delta := 4;
  SetCapacity(FRecCapacity + Delta);
end;

Function TArray.Get(Index: Integer): Pointer;
Begin
  if (Index < 0) or (Index >= FRecCount) then Error(7);
  Result:=@Farray^[FRecSize*Index];
End;

procedure TArray.Clear;
begin
  FRecCount:=0;
  SetCapacity(0);
end;

Procedure TArray.Put(Index: Integer; Rec: Pointer);
Begin
  if (Index < 0) or (Index >= FRecCount) then Error(8);
  System.Move(Rec^,Farray^[FRecSize*Index],FRecSize);
End;

Procedure TArray.ForEach(Situation:Integer);
Var Teller:Integer;
Begin
  If not Assigned(FOnForEach) then exit;
  For Teller:=0 to FRecCount-1 do
  Begin
    FOnForEach(Self,Situation,Get(Teller),Teller);
  End;
End;

Procedure TArray.ForIndex(FromIndex,ToIndex,Situation:Integer);
Var Teller:Integer;
Begin
  If not Assigned(FOnForIndex) then exit;
  if (FromIndex < 0) or (FromIndex >= FRecCount) then Error(9);
  if (ToIndex < 0) or (ToIndex >= FRecCount) then Error(10);
  For Teller:=FromIndex to ToIndex do
  Begin
    FOnForIndex(Self,Situation,Get(Teller),Teller);
  End;
End;

Function TArray.GetSize:Integer;
Begin
  Result:=FRecSize * FRecCount;
end;

Destructor TArray.Destroy;
Begin
  Clear;
  Inherited Destroy;
End;

end.