Board index » delphi » dynamically allocating space for arrays using getmem or new procedure in Delphi 3

dynamically allocating space for arrays using getmem or new procedure in Delphi 3

I am trying to create a series of arrays dynamically using getmem.  I
have one array that is statically defined that will point to one of 10
other "arrays" that are dynamically allocated.  I need to do this so
that if I need 8 bit data I only allocate arrays for byte data or if I
only need 16 bit data I will allocate arrays for word data. The need
will vary depending on several other factors.  I don't want to over
allocate data just to be prepared to accept either 8 or 16 bit data.  I
tried to allocate an array which is approximately 22000 elements using a
getmem and saving a pointer from the static pointer array to the newly
acquired storage.  This seemed to work OK, however when I tried to use
the storage I got an access violation writing data to one of the
arrays.  From a dump of the pointers in my static pointer array I can
tell that I have indeed allocated blocks of storage for data at the
failing address of 93C000 in hex. My suspicion is that when I used INC
procedure to increment the pointer it got lost when I crossed a page
boundry, giving the exception.  What is the correct way to dynamically
allocate an array? How do I put data into the new array?  Thanks for any
help you can give.
 

Re:dynamically allocating space for arrays using getmem or new procedure in Delphi 3


Quote
James R. Darrah wrote:

> I am trying to create a series of arrays dynamically using getmem.  I
> have one array that is statically defined that will point to one of 10
> other "arrays" that are dynamically allocated.  I need to do this so
> that if I need 8 bit data I only allocate arrays for byte data or if I
> only need 16 bit data I will allocate arrays for word data. The need
> will vary depending on several other factors.  I don't want to over
> allocate data just to be prepared to accept either 8 or 16 bit data.  I
> tried to allocate an array which is approximately 22000 elements using a
> getmem and saving a pointer from the static pointer array to the newly
> acquired storage.  This seemed to work OK, however when I tried to use
> the storage I got an access violation writing data to one of the
> arrays.  From a dump of the pointers in my static pointer array I can
> tell that I have indeed allocated blocks of storage for data at the
> failing address of 93C000 in hex. My suspicion is that when I used INC
> procedure to increment the pointer it got lost when I crossed a page
> boundry, giving the exception.  What is the correct way to dynamically
> allocate an array? How do I put data into the new array?  Thanks for any
> help you can give.

I surmise that you are working in a 16-bit environment.  Many of the
potential problems have been solved for you by other container-classes,
such as TMemoryStream.

But, having said that, let me loosen-up a bit and say, take a closer
look at container classes of all kinds.  You can download 'em, and you
can buy 'em.  Who says that the only way to store 22,000 words in a
fashion that "looks" like an array, is actually to allocate contiguous
storage and wrestle with it?

In one of my projects I had to deal with a situation where I could have
up to 65,536 words but usually far less.  So I constructed a
container-class which stores the elements as a one-level tree with 32
leaves, each containing 2,048 words.  Leaves are allocated only when a
non-zero value needs to be stored.  The leaves don't have to be
physically contiguous in memory.  The "read" and "write" methods of this
container-class are only a few instructions long, but it completely
eliminates this very-similar problem.

This class is reproduced below:

{? Copyright Sundial Services.  Permission to use granted.}
interface

type
  TWordArray = class(TObject)
    FMinSize,
    FMaxSize: Word;
    FPages:   array[0..31] of Pointer;
    procedure CheckElement(element: Word);
    function  GetElement(element: Word): Word;
    procedure PutElement(element, value: Word);
  public
    constructor Create(minSize, maxSize: Word);
    destructor  Destroy;
    property Elements[element: Word]: Word
      read GetElement write PutElement; default;
    property MinSize: Word
      read FMinSize write FMinSize;
    property MaxSize: Word
      read FMaxSize write FMaxSize;

    procedure Clear;
    procedure Exclude (element, value: Word);
    procedure Include (element, value: Word);
    function  Includes(element, value: Word): boolean;
  end; {TWordArray}

implementation

{
  TWORDARRAY:  AN ARRAY OF 16-BIT VALUES.

Quote
}

type
  TWordArrayPage = array[0..2047] of Word;
  PWordArrayPage = ^TWordArrayPage;

constructor TWordArray.Create(minSize, maxSize: Word);
begin
  inherited Create;
  FMinSize := minSize;
  if maxSize = 0
    then FMaxSize := 65535                 { to simplify loops that want
to iterate from min to max ... }
    else FMaxSize := maxSize;
  if (FMaxSize < FMinSize)
    then raise EInvalidOperation.CreateFmt( 'impossible bounds [%d,
%d]',
                                            [ FMinSize, FMaxSize ] );
end;

destructor TWordArray.Destroy;
begin
  Clear;                            { we rely upon this method to
physically delete the pages }
  inherited Destroy;
end;

{
  CheckElement:
    It turns out that the most convenient way to handle errors in
referencing these arrays
      or bit-sets, is to provide an exception handler here in these
types.
    If the max-size is zero, it means that no test should occur.

Quote
}

procedure TWordArray.CheckElement(element: Word);
begin
  if (FMaxSize > 0) and (FMaxSize < 65535) and (element > FMaxSize)
    then raise EInvalidOperation.CreateFmt( 'access array-element %d,
but max size is %d',
                                            [ element, FMaxSize ] );
  if (FMinSize > 0) and (element < FMinSize)
    then raise EInvalidOperation.CreateFmt( 'access array-element %d,
but min size is %d',
                                            [ element, FMinSize ] );
end; {CheckElement}

{
  The "GetElement" routine takes advantage of the fact that unused
elements
    are always defined to be zero.

Quote
}

function TWordArray.GetElement(element: Word): Word;
var
  i, m: Word;
begin
  CheckElement(element);
  i := (element shr 11) and $001F;                   { the "and" is
unnecessary, but hey ... }
  m := element          and $07FF;
  if FPages[i] = nil
    then result := 0
    else result := PWordArrayPage(FPages[i])^[m];
end; {GetElement}

{
  The "PutElement" routine allocates pages as required, always setting
the entire
    page to binary zeroes before storing the value.

Quote
}

procedure TWordArray.PutElement(element, value: Word);
var
  i, m: Word;
begin
  CheckElement(element);
  i := (element shr 11) and $001F;                   { the "and" is
unnecessary, but hey ... }
  m := element          and $07FF;
  if FPages[i] = nil then begin                      { no page here
yet? }
    if value = 0 then exit;                          { well, no need to
burn a page just to store a zero! }
    FPages[i] := AllocMem(sizeof(TWordArrayPage));   { otherwise,
allocate page, set it to zero }
  end;
  PWordArrayPage(FPages[i])^[m] := value;
end; {PutElement}

{
  "Include" / "Exclude" / "Includes" -- faster logical-bit routines.
  These operations are not implemented for all the array-types, simply
because
    we don't need them!

Quote
}

procedure TWordArray.Include(element, value: Word);
var
  i, m: Word;
begin
  CheckElement(element);
  if value = 0 then exit;                            { don't waste my
time }

  i := (element shr 11) and $001F;                   { the "and" is
unnecessary, but hey ... }
  m := element          and $07FF;
  if FPages[i] = nil then begin                      { no page here yet?

Quote
}

    FPages[i] := AllocMem(sizeof(TWordArrayPage));   { gotta have one,
so allocate page, set it to zero }
  end;
  PWordArrayPage(FPages[i])^[m] := PWordArrayPage(FPages[i])^[m] or
value;
end; {Include}

procedure TWordArray.Exclude(element, value: Word);
var
  i, m: Word;
begin
  CheckElement(element);
  if value = 0 then exit;                            { don't waste my
time }

  i := (element shr 11) and $001F;                   { the "and" is
unnecessary, but hey ... }
  m := element          and $07FF;
  if FPages[i] = nil then exit;                      { if no page here
yet, there's nothing to exclude }
  PWordArrayPage(FPages[i])^[m] := PWordArrayPage(FPages[i])^[m] and
(not value);
end; {Exclude}

function TWordArray.Includes(element, value: Word): boolean;
var
  i, m: Word;
begin
  result := false;                                  { default result }
  CheckElement(element);
  if value = 0 then exit;                           { if "includes 0"
... result always false }

  i := (element shr 11) and $001F;                  { the "and" is
unnecessary, but hey ... }
  m := element          and $07FF;
  if FPages[i] = nil then exit;                     { page isn't here
... result is false }

  if (PWordArrayPage(FPages[i])^[m] and value) = value then result :=
true;   { all bits must be true }
end; {Includes}

{
  Clearing an array consists of disposing of all of the existing
data-storage pages
    and setting the corresponding pointers to NIL.
  (N.B. "Destroy" relies on this, and "GetElement" takes advantage of
it.)

Quote
}

procedure TWordArray.Clear;
var
  w:  0..31;
begin
  for w := 0 to 31 do
    if FPages[w] <> nil then begin
      FreeMem(FPages[w], sizeof(TWordArrayPage));
      FPages[w] := nil;
    end;
end;

Re:dynamically allocating space for arrays using getmem or new procedure in Delphi 3


Quote
James R. Darrah wrote in message <361D2CD1.FEF48...@ibm.net>...
>I am trying to create a series of arrays dynamically using getmem.  I

A couple of options: Get D4 and use the new dynamic array type.

Use Sundial's unit.

turn of range checking and use
type Tmybytearray: array[0..0] of byte; // or whatever type, whatever base
index
and use Allocmem and Freemem to "manage" your resources.

--
Grace + Peace  |  Peter N Roth  |  Engineering Objects Int'l
Inprise Tool & Component Builder | Amazon.com associate
          http://www.inconresearch.com/eoi

Re:dynamically allocating space for arrays using getmem or new procedure in Delphi 3


I missed the original message. But I do this a lot with type
declarations like:

   TRSB = Record
     RSB          : integer;
     Sites        : array [0..3] of string[3];
     SingleSite   : boolean;
     StratAlt     : integer;
     Offset       : integer;
     Size         : integer;
     primarycolor :TColor;
     InFPA        : boolean;
   end;

   TRSBPtr = ^TRSBArray;
   TRSBarray = array [0..MaxInt div Sizeof(TRSB)-1] of TRSB;

With this kind of declaration you don't have to turn range checking off,
and the de{*word*81} won't bark about range errors. (This is used throughout
the Delphi 2.0 VCL.)
After declaring a variable you can initialize the pointer a certain
size, then when you need to change the size realloc to a new size.
If loading from a file for instance..
   function LoadPtr(?,?,?):integer;
   var
      RSBPtr:TRSBPtr;
      LclSize:integer;
      i:integer;
   begin
       LclSize:=500;
       GetMem(RSBPtr,LclSize*Sizeof(TRSB));
       result:=0;
       while SoandSo....
       begin
           // do the file reading stuff or what ever
           // to load the array.....
           with RSBPtr^[result] do
           begin
               RSB:=something;
               for i:=0 to high(sites) do
               begin
                   sites[i]:=somethingelse;
               end;
           end;
           Inc(result);
             //check to see if you need more room
           if result:=lclSize then
           begin
               Inc(lclsize,200);
               ReAllocMem(RSBPtr,lclSize*Sizeof(TRSB));
           end;
       end;
       //now that we are done we can reduce the size to that needed
       ReAllocMem(RSBPtr,result*Sizeof(TRSB));
   end;
  //remember to use Freemem to clean up after yourself, and store
  //the number of records (result) somewhere.

 you could obviously write 'add' & 'del' methods or procedures
 that would ReAllocMem and Inc/Dec(recordcount) to make room for
 more records or to save space after deleting records.

--
Sam Corlett
Curragh Equestrian Center

http://www.curragh.com

---------------------------------------------------------------------
|  By sending me unsolicited commercial email you agree to pay my   |
| standard consulting fee of $250/hr for examining your message (a  |
| minimum charge of one (1) hour).  The bill for my service will be |
| sent to you along with my analysis of your message.               |
---------------------------------------------------------------------

Other Threads