[comp.lang.pascal] Strange... The Turbo Pascal NEW command

winfave@dutrun.UUCP (Alexander Verbraeck) (08/01/89)

There's one thing about Turbo Pascal that I don't understand,
and that is the how the New procedure knows the size of the
block on the heap to be allocated. At this  moment some of my 
students are working on a heap manager on top of the standard
TP heap manager that allows 4 Gbyte of heap space using paging 
and virtual memory swapping pages to (Ram)disk, where it is
*not* neccesary to change any existing code. You can still use
the standard ^ types, New and Dispose. If there is any interest 
in this manager when it is finished, I can post it to the net
(if-and-only-if we succeed of course).

The problem is that we do not understand the way the New 
procedure works. When you call New(P) where P points to a certain
type, the New procedure allocates the correct number of bytes on
the heap. We think that the compiler inserts some extra parameters 
when calling New, for the following program works _NOT_ ok:

program TestNew(input,output);

const
  AS = 500;

type
  TT = array[1..AS] of integer;
  PT = ^TT;

var
  IP,JP : PT;
  i,j   : integer;


procedure New(var P);

begin
  System.New(pointer(P));
end;

begin
  writeln(MemAvail);
  New(IP);
  writeln(MemAvail);
  System.New(JP);
  writeln(MemAvail);
  for i:=1 to AS do IP^[i]:=i;
  for i:=1 to AS do JP^[i]:=0;
  for j:=1 to 10 do write(IP^[j]:5);
  writeln;
  write('Press Enter');
  readln;
end.

Output of this program is for instance:

------------------------------------------------------------
502314
502314    --> No memory allocated at all!
501314    --> 1000 bytes allocated
    0    0    0    0    0    0    0    0    0    0
Press Enter
------------------------------------------------------------

Which means that no memory is allocated during the New call,
the correct amount of memory is allocated during the System.New
call, and IP and JP share Heap-memory.

Does anyone know what parameters are passed extra to the New
procedure? How can I use these parameters? Is it possible to
make a customized version of the New procedure? How do I get
it to pass the right parameters on to the System.New procedure?

---------------------------------------------------------------------
Alexander Verbraeck                            e-mail:
Delft University of Technology                 winfave@hdetud1.bitnet
Department of Information Systems              winfave@dutrun.uucp
PO Box 356, 2600 AJ  The Netherlands
---------------------------------------------------------------------

ags@mentor.cc.purdue.edu (Dave Seaman) (08/01/89)

In article <845@dutrun.UUCP> winfave@dutrun.UUCP (A.Verbraeck) writes:
>There's one thing about Turbo Pascal that I don't understand,
>and that is the how the New procedure knows the size of the
>block on the heap to be allocated. 

If you really want to do this, you should be using Modula-2 instead of
Pascal.  It has been a while since I looked at this, but the way I remember it
is as follows.  Modula-2 specifies that a call NEW(P) is translated into the
system procedure ALLOCATE(P,n), where n is the size of the object to be
allocated, and is determined by the compiler.  ALLOCATE is imported from the
SYSTEM module.

The Pascal standard does not provide any implementation details for NEW.
Therefore there is no guarantee that what you are asking is even possible in
any given implementation, short of writing assembly code.  There certainly is
no portable way of doing it.
-- 
Dave Seaman	  					
ags@seaman.cc.purdue.edu

abcscnuk@csuna.csun.edu (Naoto Kimura) (08/02/89)

In article <3534@mentor.cc.purdue.edu> ags@mentor.cc.purdue.edu (Dave Seaman) writes:
>In article <845@dutrun.UUCP> winfave@dutrun.UUCP (A.Verbraeck) writes:
>>There's one thing about Turbo Pascal that I don't understand,
>>and that is the how the New procedure knows the size of the
>>block on the heap to be allocated. 
> ... (text deleted) ...
>
>Dave Seaman	  					
>ags@seaman.cc.purdue.edu

As far as I can guess, it performs a GetMem with the proper size, which
is the size of the type that the pointer to points to.  That is, if the
pointer variable passed to New is a pointer to a record, it allocates
enough memory to store that record type.  It knows the size because it
knows the record type and the size (as given by the SizeOf function).
I haven't tried to send a Pointer type (just pointer, not a pointer to
anything in particular) into New to see how much it allocates.  It's
really annoying that I can't allocate a 64K block (I can only allocate
64K - 1 byte).

                //-n-\\			 Naoto Kimura
        _____---=======---_____		 (abcscnuk@csuna.csun.edu)
    ====____\   /.. ..\   /____====
  //         ---\__O__/---         \\	Enterprise... Surrender or we'll
  \_\                             /_/	send back your *&^$% tribbles !!

dmurdoch@watstat.waterloo.edu (Duncan Murdoch) (08/02/89)

In article <2110@csuna.csun.edu> abcscnuk@csuna.csun.edu (Naoto Kimura) writes:
>It's
>really annoying that I can't allocate a 64K block (I can only allocate
>64K - 1 byte).

It's really 64K - 15 bytes, and the reason is that the heap manager wants
to be able to allocate it at any address, and still have it addressable
within a single segment.  If it gets allocated at ssss:$000F, then an offset
of 64K - 1 would wrap around to the beginning of the segment.

In any case though, the heap manager is fully documented, so you could write
your own Getmem that wouldn't have this limitation, if you really need to.
You'll have to disable range checking to be able to access all of it, but that's
not a big deal.

Duncan Murdoch

filbo@gorn.santa-cruz.ca.us (Bela Lubkin) (08/02/89)

In article <845@dutrun.UUCP> mcvax!hp4nl!dutrun!winfave@uunet.uu.net
(A.Verbraeck) writes:
>The problem is that we do not understand the way the New 
>procedure works. When you call New(P) where P points to a certain
>type, the New procedure allocates the correct number of bytes on
>the heap. We think that the compiler inserts some extra parameters 
>when calling New, for the following program works _NOT_ ok:

You are right.  Many Turbo Pascal "standard procedures" are not procedures
per se.  The most obvious example is Write/WriteLn: would it be possible
to write those procedures (with variable numbers of arguments, special
syntax for field widths, etc.) in the language as it's documented?  (This
must be true in most Pascal compilers.)

New and Dispose are among the library routines that are called through some
trickery.  When you write  New(Foo),  the compiler generates code similar to
that which would be generated for  GetMem(Foo,SizeOf(Foo^)).  This is not
handled by a preprocessor, nor is GetMem ever actually called explicitly, so
you cannot simply write a procedure named GetMem.  I don't know of any way
to completely transparently replace New & Dispose.  You could buy the source
to the library and replace the System unit, but as a programmer using your
heap code I would not find it "transparent" to have to move your unit into
my library.  ADDing a unit would be fine; replacing one of the vendor's, no.

Hmmm.  Ok, I have a kludge for you.  You'll have to do some digging in the
reference manual for this; my manuals are still waiting to be moved from my
previous residence.  Turbo's standard New/GetMem will call an exception
routine if there's not enough heap to satisfy your request.  You can install
your routines as that exception routine, then grab ALL of Turbo's heap for
yours, so all allocation attempts will generate an exception.  The pointer
you return must point to a memory block that actually starts a few bytes
lower than the pointer points to.  Store the size of the block in this extra
area.  This allows you to write your own Dispose procedure which doesn't
require magic parameters.  (I don't think there's an exception routine for
Dispose -- how could it fail?)

All in all, a better solution would be to write transparent replacements for
GetMem/FreeMem and disallow New/Dispose.  (This is easily accomplished; just
declare New/Dispose procedures with no parameters.  These will hide the
System unit versions and prevent any attempts to call them).

>program TestNew(input,output);
>const
>  AS = 500;
>type
>  TT = array[1..AS] of integer;
>  PT = ^TT;
>var
>  IP,JP : PT;
>  i,j   : integer;
>procedure New(var P);
>begin
>  System.New(pointer(P));
              ^^^^^^^^^^
Type "pointer" acts like a "C" "void *" -- it points to a structure of size
0.  New(pointer(P)) is equivalent to GetMem(P,SizeOf(P^)), or GetMem(P,0),
and allocates no memory.

>end;
>
>begin
>  writeln(MemAvail);
>  New(IP);
>  writeln(MemAvail);
>  System.New(JP);
>  writeln(MemAvail);
>  for i:=1 to AS do IP^[i]:=i;
>  for i:=1 to AS do JP^[i]:=0;
>  for j:=1 to 10 do write(IP^[j]:5);
>  writeln;
>  write('Press Enter');
>  readln;
>end.

Bela Lubkin     * *   filbo@gorn.santa-cruz.ca.us   CIS: 73047,1112
     @        * *     ...ucbvax!ucscc!gorn!filbo    ^^^  REALLY slow [months]
R Pentomino     *     Filbo @ Pyrzqxgl (408) 476-4633 & XBBS (408) 476-4945

tarvaine@tukki.jyu.fi (Tapani Tarvainen) (08/03/89)

In article <3.filbo@gorn.santa-cruz.ca.us> filbo@gorn.santa-cruz.ca.us (Bela Lubkin) writes:

>  (I don't think there's an exception routine for
>Dispose -- how could it fail?)

Yes it can, rare though it is:  There is a fixed size reserved
for the free space list, and if you free a block that isn't adjacent
to another, you need room for another entry.  If memory becomes
sufficiently fragmented, it is possible to fill the list.
This is described on p. 197-198 in TP 5.0 Reference Guide;
it says there is room for 8191 noncontiguous freed blocks.
BTW, it isn't quite as unlikely as the manual suggests:
it happened to me once!  

-- 
Tapani Tarvainen    (tarvaine@jyu.fi, tarvainen@finjyu.bitnet)