Help, need dynamic two dimensional array data structure!

G'day,

Well as the subject header suggests I need access to a decent dynamic
two dimensional array data structure that is fast to access and is
disk based.

I did my own data structure, shown below (yickes), and there are a
number of  problems with it. One is that is that it stores many
redundancies for non square matrices and it dies for a 1800 * 1800
matrix with an element size of 5 bytes.

If any one could suggest a neater alternative it would be much
appreciated. I purchases Systools for delphi, but to my disappointment
it does not have such a structure.

Thanxs,
Daniel

unit Sqrmatx;

interface

uses Classes, FmxUtils, SysUtils;

const
  MaxRecordSize = 1000;

type

(*
=============================================================================
                              DataRecord
==============================================================================
*)

DataRecord = class(TObject)
  public
    constructor Create; virtual;
    destructor Destroy; virtual;

    function GetSize : longint; virtual; abstract;
    procedure LoadFromStream(var stream : TMemoryStream); virtual;
abstract;
    procedure SaveToStream (var stream : TMemoryStream); virtual;
abstract;
end;

(*
================================================================================
                     SquareMatrix Declarations
================================================================================
*)

ArrayHeader = Record
                colNum     : integer;
                rowNum     : integer;
                LargestDim : integer;
                Size       : longint;
              end;

SquareMatrix = class(TObject)
public
  constructor Create(const workFilename : string); virtual;
  destructor Destroy; virtual;

  procedure AddCol(colId : integer);
  procedure AddRow(rowId : integer);
  procedure Clear; virtual;
  procedure DelCol(colId : integer);
  procedure DelRow(rowId : integer);
  function GetData(const colId, rowId : integer) : boolean;
  procedure LoadFrom(const filename : string); virtual;
  procedure SaveTo(const filename : string); virtual;
  function SetData(colId, rowId : integer) : boolean;

protected
  Data : DataRecord; { to be created by subclasses }
  ArrayInfo : ArrayHeader;

  procedure ClearDataItem; virtual; abstract;
  procedure InitialiseColumn(colId : integer); virtual;
  procedure InitialiseRow(rowId : integer); virtual;

private
  DataStream : TMemoryStream;
  DataFilename : string; { utilised by descendants }

  function GetStreamOffset(const colId, rowId : longint) : longint;
  procedure ReSize(dim : integer); virtual;
end;

implementation
(*
================================================================================
                          DataRecord functions
================================================================================
*)
constructor DataRecord.Create;
begin
  inherited Create;
end;

destructor DataRecord.Destroy;
begin
  inherited Destroy;
end;

(*
================================================================================
                           SquareMatrix Public functions
================================================================================
*)

constructor SquareMatrix.Create(const workFilename : string);
begin
  inherited Create;
  DataFilename := workFilename;
  DataStream := TMemoryStream.Create;
  with ArrayInfo do
  begin
    rowNum := 0;
    colNum := 0;
    LargestDim := 0;
    Size := 0;
  end;
end;

destructor SquareMatrix.Destroy;
begin
  DataStream.Free;
  Data.Free;
  inherited Destroy;
end;

procedure SquareMatrix.AddCol(colId : integer);
{
 Argument    : colId - col number to add to the matrix
 Description : If needed it adds a new columm and row to the matrix to
               accommodate the given column number, and initialises
the column.

Quote
}

begin
  with ArrayInfo do
  begin
    if colId >= colNum then
    begin
      Inc(colNum);
      if colNum > LargestDim then
      begin
        LargestDim := colNum + 20;
        {
        Inc(LargestDim);
        }
        Resize(LargestDim);
      end;
    end;
  end;
  self.InitialiseColumn(colId);
end;

procedure SquareMatrix.AddRow(rowId : integer);
{
 Argument    : rowId - row number to add to the matrix
 Description : If needed it adds a new column and row to the matrix to
               accommodate the given row number, and initialises the
row

Quote
}

begin
  with ArrayInfo do
  begin
    if rowId >= rowNum then
    begin
      Inc(rowNum);
      if rowNum > LargestDim then
      begin
        LargestDim := rowNum + 20;
        {
        Inc(LargestDim);
        }
        Resize(LargestDim);
      end;
    end;
  end;
  self.InitialiseRow(rowId);
end;

procedure SquareMatrix.Clear;
{
 Description : Clears the matrix of any data and initialises its size
to nil

Quote
}

begin
  DataStream.Clear;
  with ArrayInfo do
  begin
    colNum := 0;
    rowNum := 0;
    LargestDim := 0;
    Size := 0;
  end;
end;

procedure SquareMatrix.DelCol(colId : integer);
{
 Argument    : colId - index of column to delete
 Description : If the column index corresponds to the boundary it
reduces the
               size of the square matrix by 1 in both dimensions, else
it
               initialises the column specified by the colId.

Quote
}

begin
  with ArrayInfo do
  begin
    if colId = colNum-1 then
    begin
      if colNum > rowNum then
      begin
        Dec(LargestDim);
        Resize(LargestDim);
      end;
      Dec(colNum);
    end
    else if colId < colNum-1 then
      InitialiseColumn(colId);
  end;
end;

procedure SquareMatrix.DelRow(rowId : integer);
{
 Argument    : rowId - index of row to delete
 Description : If the row index corresponds to the boundary it reduces
the
               size of the square matrix by 1 in both dimensions, else
it
               initialises the column specified by the rowId.

Quote
}

begin
  with ArrayInfo do
  begin
    if rowId = rowNum-1 then
    begin
      if rowId > rowNum-1 then
      begin
        if rowNum > colNum then
        begin
          Dec(LargestDim);
          Resize(LargestDim);
        end;
        Dec(rowNum);
      end
      else if rowId < rowNum-1 then
        InitialiseRow(rowId);
    end;
  end;
end;

function SquareMatrix.GetData(const colId, rowId : integer) : boolean;
{
 Argument    : colId, rowId - index of column and row from which to
               retrieve data from
 Returns     : boolean - indicating if successful
 Description : Retrieves the data for a specified item in the matrix,
the
               results of the operation are returned in the Data
variable

Quote
}

var
  pos : longint;
  temp : integer;
begin
  if rowId >= 81 then
    temp := colId;

  pos := GetStreamOffset(colId, rowId);
  if (pos >= 0) and (pos < DataStream.Size) then
  begin
    DataStream.Position := pos;
    Data.LoadFromStream(DataStream);
    GetData := true;
  end
  else
    GetData := false;
end;

procedure SquareMatrix.LoadFrom(const filename : string);
{
 Argument    : filename - name of file to load data from
 Description : Loads the ArrayInfo and DataStream with the data in the
given
               file

Quote
}

var
  stream : TStream;
begin
  stream := TFileStream.Create(filename, fmOpenRead);
  stream.Read(ArrayInfo, SizeOf(ArrayInfo));
  DataStream.SetSize(ArrayInfo.Size);
  stream.ReadBuffer(DataStream.Memory^, ArrayInfo.Size);
  Stream.Free;
end;

procedure SquareMatrix.SaveTo(const filename : string);
{
 Argument    : filename - name of file to save to
 Description : Saves the ArrayInfo and DataStream data to the
specified file

Quote
}

var
  stream : TStream;
begin
  stream := TFileStream.Create(filename, fmCreate);
  stream.Write(ArrayInfo, SizeOf(ArrayInfo));
  stream.Write(DataStream.Memory^, ArrayInfo.Size);
  stream.Free;
end;

function SquareMatrix.SetData(colId, rowId : integer) : boolean;
{
 Argument    : colId, rowId - index of column and row to set data for
 Description : Sets the data for the specified item with contained in
the
               Data variable

Quote
}

var
  pos : longint;
begin
  pos := GetStreamOffset(colId, rowId);
  if pos >= 0 then
  begin
    DataStream.Position := pos;
    Data.SaveToStream(DataStream);
    SetData := true;
  end
  else
    SetData := false;
end;

(*
================================================================================
                   SquareMatrix Protected Functions
================================================================================
*)

procedure SquareMatrix.InitialiseColumn(colId : integer);
{
 Argument    : colId - index of column to initialise
 Description : Initialises the data for a given column with the Data
contained
               in the Data variable

Quote
}

var
  row : integer;
begin
  ClearDataItem;
  for row := 0 to ArrayInfo.rowNum-1 do
    SetData(colId, row);
end;

procedure SquareMatrix.InitialiseRow(rowId : integer);
{
 Argument    : rowId - index of row to initialise
 Description : Initialises the data for a given row with the Data
contained in
               the Data variable

Quote
}

var
  col : integer;
begin
  ClearDataItem;
  for col := 0 to ArrayInfo.colNum-1 do
    SetData(col, rowId);
end;

(*
================================================================================
                   SquareMatrix Private Functions
================================================================================
*)

function SquareMatrix.GetStreamOffset(const colId, rowId : longint) :
longint;
{
 Argument    : colId, rowId - ids of column and row
 Returns     : longint - offset from start to data

Quote
}

var
  temp : integer;
begin
  if (colId < ArrayInfo.LargestDim) and (rowId < ArrayInfo.LargestDim)
and
     (colId >= 0) and (rowId >= 0) then
  begin
    if colId = rowId then
      GetStreamOffset := ((colId + 1)*(colId + 1) -1)*Data.GetSize
    else if colId > rowId then
      GetStreamOffset := (colId*colId + 2*rowId)*Data.GetSize
    else
      GetStreamOffset := (rowId*rowId + 2*colId + 1)*Data.GetSize;
  end
  else
    GetStreamOffset := -1;
end;

procedure SquareMatrix.ReSize(dim : integer);
var
  endLimit, index, size : longint;
  temp : TMemoryStream;
begin    
  { copy old contents }
  temp := TMemoryStream.Create;
  temp.SetSize(ArrayInfo.Size);
  DataStream.SaveToStream(temp);

  { resize array }
  ArrayInfo.LargestDim := dim;
  size := longint(dim)*longint(dim)*longint(Data.GetSize);
  DataStream.SetSize(size);
  ArrayInfo.Size := size;
  if ArrayInfo.Size < size then
    size := size;

  { copy old contents back }
  DataStream.LoadFromStream(temp);
  DataStream.Position := temp.Size;
  ClearDataItem;
  endLimit := (size - temp.Size) div longint(Data.GetSize);
  for index := 0 to endLimit-1 do
    Data.SaveToStream(DataStream);
  temp.Destroy;
end;

end.