unit ddynamicarray;

{$MODE FPC}
{$MODESWITCH ADVANCEDRECORDS}
{$MODESWITCH DEFAULTPARAMETERS}
{$MODESWITCH OUT}
{$MODESWITCH RESULT}

interface

type
PDynamicArray = ^TDynamicArray;
TDynamicArray = record
private
  FFirst: Pointer;
  FElementSize: SizeUInt;
  FLength: SizeInt;
  FCapacity: SizeUInt;
  function GetElement(Index: SizeUInt): Pointer; inline;
public
  property Elements[Index: SizeUInt]: Pointer read GetElement; default;
end;

procedure daInit(da: PDynamicArray;
                 ElementSize: SizeUInt;
                 InitialCapacity: SizeUInt = 0;
                 InitialLength: SizeInt = 0);
procedure daDone(da: PDynamicArray);
procedure daSetLength(da: PDynamicArray; NewLength: SizeInt);
procedure daSetCapacity(da: PDynamicArray; NewCapacity: SizeUInt);
function daLength(da: PDynamicArray): SizeInt; inline;
function daCapacity(da: PDynamicArray): SizeUInt; inline;
function daGet(da: PDynamicArray; Index: SizeInt): Pointer; inline;

// daSetLength(da, daLength(da) + Value)
procedure daInc(da: PDynamicArray; Value: SizeUInt = 1); inline;
// daLength(da) - 1
function daHigh(da: PDynamicArray): SizeInt; inline;
// da^[daHigh(da)]
function daLast(da: PDynamicArray): Pointer; inline;
// da^[0] + daLength(da) * da^.FElementSize
function daEnd(da: PDynamicArray): Pointer; inline;

implementation

function TDynamicArray.GetElement(Index: SizeUInt): Pointer; inline;
begin
  Exit(FFirst + Index * FElementSize);
end;

function daSize(da: PDynamicArray): SizeUInt;
begin
  if da^.FLength > da^.FCapacity then begin
    Exit(da^.FLength * da^.FElementSize);
  end else
    Exit(da^.FCapacity * da^.FElementSize);
end;

procedure daRealloc(da: PDynamicArray; OldSize, NewSize: SizeUInt);
begin
  if NewSize > OldSize then begin
    if da^.FFirst = nil then begin
      da^.FFirst := GetMem(NewSize);
    end else
      da^.FFirst := ReAllocMem(da^.FFirst, NewSize);
  end else if NewSize < OldSize then begin
    if NewSize <= 0 then begin
      FreeMem(da^.FFirst);
      da^.FFirst := nil;
    end else
      da^.FFirst := ReAllocMem(da^.FFirst, NewSize);
  end;
end;

procedure daInit(da: PDynamicArray;
                 ElementSize: SizeUInt;
                 InitialCapacity: SizeUInt = 0;
                 InitialLength: SizeInt = 0);
begin
  da^.FFirst := nil;
  da^.FElementSize := ElementSize;
  da^.FLength := InitialLength;
  da^.FCapacity := InitialCapacity;
  daRealloc(da, 0, daSize(da));
end;

procedure daDone(da: PDynamicArray);
begin
  if da^.FFirst <> nil then begin
    FreeMem(da^.FFirst);
    da^.FFirst := nil;
  end;
end;

procedure daSetLength(da: PDynamicArray; NewLength: SizeInt);
var
  OldSize: SizeUInt;
begin
  OldSize := daSize(da);
  da^.FLength := NewLength;
  daRealloc(da, OldSize, daSize(da));
end;

procedure daSetCapacity(da: PDynamicArray; NewCapacity: SizeUInt);
var
  OldSize: SizeUInt;
begin
  OldSize := daSize(da);
  da^.FCapacity := NewCapacity;
  daRealloc(da, OldSize, daSize(da));
end;

function daLength(da: PDynamicArray): SizeInt; inline;
begin
  Exit(da^.FLength);
end;

function daCapacity(da: PDynamicArray): SizeUInt; inline;
begin
  Exit(da^.FCapacity);
end;

function daGet(da: PDynamicArray; Index: SizeInt): Pointer; inline;
begin
  Exit(da^.FFirst + Index * da^.FElementSize);
end;

procedure daInc(da: PDynamicArray; Value: SizeUInt); inline;
begin
  daSetLength(da, da^.FLength + Value);
end;

function daHigh(da: PDynamicArray): SizeInt; inline;
begin
  Exit(da^.FLength - 1);
end;

function daLast(da: PDynamicArray): Pointer;
begin
  Exit(da^[da^.FLength - 1]);
end;

function daEnd(da: PDynamicArray): Pointer;
begin
  Exit(da^.FFirst + da^.FElementSize * da^.FLength);
end;

end.
