mirror of
				https://gitlab.com/freepascal.org/fpc/source.git
				synced 2025-11-04 10:19:31 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			510 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			510 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
{
 | 
						|
    $Id$
 | 
						|
    This file is part of the Free Pascal run time library.
 | 
						|
    Copyright (c) 1993-99 by the Free Pascal development team.
 | 
						|
 | 
						|
    functions for heap management in the data segment
 | 
						|
 | 
						|
    See the file COPYING.FPC, included in this distribution,
 | 
						|
    for details about the copyright.
 | 
						|
 | 
						|
    This program is distributed in the hope that it will be useful,
 | 
						|
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
						|
 | 
						|
 **********************************************************************}
 | 
						|
 | 
						|
{****************************************************************************}
 | 
						|
 | 
						|
{ Reuse bigger blocks instead of allocating a new block at freelist/heapptr.
 | 
						|
  the tried bigger blocks are always multiple sizes of the current block }
 | 
						|
{$define REUSEBIGGER}
 | 
						|
 | 
						|
{ Allocate small blocks at heapptr instead of walking the freelist }
 | 
						|
{$define SMALLATHEAPPTR}
 | 
						|
 | 
						|
{ Dump info when the heap needs to grow }
 | 
						|
{ define DUMPGROW}
 | 
						|
 | 
						|
{ Default heap settings }
 | 
						|
const
 | 
						|
  blocksize    = 16;  { at least size of freerecord }
 | 
						|
  blockshr     = 4;   { shr value for blocksize=2^blockshr}
 | 
						|
  maxblocksize = 1024+blocksize; { 1024+8 needed for heaprecord }
 | 
						|
  maxblock     = maxblocksize div blocksize;
 | 
						|
  maxreusebigger = 8; { max reuse bigger tries }
 | 
						|
 | 
						|
{****************************************************************************}
 | 
						|
 | 
						|
{$ifdef DUMPGROW}
 | 
						|
  {$define DUMPBLOCKS}
 | 
						|
{$endif}
 | 
						|
 | 
						|
{ Memory manager }
 | 
						|
const
 | 
						|
  MemoryManager: TMemoryManager = (
 | 
						|
    GetMem: SysGetMem;
 | 
						|
    FreeMem: SysFreeMem;
 | 
						|
    FreeMemSize: SysFreeMemSize;
 | 
						|
    MemSize: SysMemSize
 | 
						|
  );
 | 
						|
 | 
						|
type
 | 
						|
  ppfreerecord = ^pfreerecord;
 | 
						|
  pfreerecord  = ^tfreerecord;
 | 
						|
  tfreerecord  = record
 | 
						|
    size  : longint;
 | 
						|
    root  : ppfreerecord;
 | 
						|
    next,
 | 
						|
    prev  : pfreerecord;
 | 
						|
  end; { 16 bytes }
 | 
						|
 | 
						|
  pheaprecord = ^theaprecord;
 | 
						|
  theaprecord = record
 | 
						|
  { this should overlap with tfreerecord }
 | 
						|
    size  : longint;
 | 
						|
    root  : ppfreerecord;
 | 
						|
  end; { 8 bytes }
 | 
						|
 | 
						|
  tfreelists   = array[0..maxblock] of pfreerecord;
 | 
						|
  pfreelists   = ^tfreelists;
 | 
						|
 | 
						|
var
 | 
						|
  internal_memavail  : longint;
 | 
						|
  internal_heapsize  : longint;
 | 
						|
  freelists          : tfreelists;
 | 
						|
  checkfreememsize   : boolean;
 | 
						|
 | 
						|
{*****************************************************************************
 | 
						|
                             Memory Manager
 | 
						|
*****************************************************************************}
 | 
						|
 | 
						|
procedure GetMemoryManager(var MemMgr:TMemoryManager);
 | 
						|
begin
 | 
						|
  MemMgr:=MemoryManager;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
procedure SetMemoryManager(const MemMgr:TMemoryManager);
 | 
						|
begin
 | 
						|
  MemoryManager:=MemMgr;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
function IsMemoryManagerSet:Boolean;
 | 
						|
begin
 | 
						|
  IsMemoryManagerSet:=(MemoryManager.GetMem<>@SysGetMem) or
 | 
						|
                      (MemoryManager.FreeMem<>@SysFreeMem);
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
procedure GetMem(Var p:pointer;Size:Longint);[public,alias:'FPC_GETMEM'];
 | 
						|
begin
 | 
						|
  MemoryManager.GetMem(p,Size);
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
procedure FreeMem(Var p:pointer);
 | 
						|
begin
 | 
						|
  MemoryManager.FreeMem(p);
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
procedure FreeMem(Var p:pointer;Size:Longint);[public,alias:'FPC_FREEMEM'];
 | 
						|
begin
 | 
						|
  MemoryManager.FreeMemSize(p,Size);
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
function MemSize(p:pointer):Longint;
 | 
						|
begin
 | 
						|
  MemSize:=MemoryManager.MemSize(p);
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
{ Needed for calls from Assembler }
 | 
						|
procedure AsmFreeMem(Var p:pointer);
 | 
						|
begin
 | 
						|
  MemoryManager.FreeMem(p);
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
{*****************************************************************************
 | 
						|
                       Heapsize,Memavail,MaxAvail
 | 
						|
*****************************************************************************}
 | 
						|
 | 
						|
function heapsize : longint;
 | 
						|
begin
 | 
						|
  heapsize:=internal_heapsize;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
function memavail : longint;
 | 
						|
begin
 | 
						|
  memavail:=internal_memavail;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
function maxavail : longint;
 | 
						|
var
 | 
						|
  hp : pfreerecord;
 | 
						|
begin
 | 
						|
  maxavail:=heapend-heapptr;
 | 
						|
  hp:=freelists[0];
 | 
						|
  while assigned(hp) do
 | 
						|
   begin
 | 
						|
     if hp^.size>maxavail then
 | 
						|
       maxavail:=hp^.size;
 | 
						|
     hp:=hp^.next;
 | 
						|
   end;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
{$ifdef DUMPBLOCKS}
 | 
						|
procedure DumpBlocks;
 | 
						|
var
 | 
						|
  s,i,j : longint;
 | 
						|
  hp  : pfreerecord;
 | 
						|
begin
 | 
						|
  for i:=1 to maxblock do
 | 
						|
   begin
 | 
						|
     hp:=freelists[i];
 | 
						|
     j:=0;
 | 
						|
     while assigned(hp) do
 | 
						|
      begin
 | 
						|
        hp:=hp^.next;
 | 
						|
        inc(j);
 | 
						|
      end;
 | 
						|
     writeln('Block ',i*blocksize,': ',j);
 | 
						|
   end;
 | 
						|
{ freelist 0 }
 | 
						|
  hp:=freelists[0];
 | 
						|
  j:=0;
 | 
						|
  s:=0;
 | 
						|
  while assigned(hp) do
 | 
						|
   begin
 | 
						|
     hp:=hp^.next;
 | 
						|
     inc(j);
 | 
						|
     if hp^.size>s then
 | 
						|
      s:=hp^.size;
 | 
						|
   end;
 | 
						|
  writeln('Main: ',j,' maxsize: ',s);
 | 
						|
end;
 | 
						|
{$endif}
 | 
						|
 | 
						|
 | 
						|
{*****************************************************************************
 | 
						|
                                 SysGetMem
 | 
						|
*****************************************************************************}
 | 
						|
 | 
						|
procedure SysGetMem(var p : pointer;size : longint);
 | 
						|
type
 | 
						|
  heaperrorproc=function(size:longint):integer;
 | 
						|
var
 | 
						|
  proc  : heaperrorproc;
 | 
						|
  pcurr : pfreerecord;
 | 
						|
  again : boolean;
 | 
						|
  heapfree,
 | 
						|
  s,s1,i,
 | 
						|
  sizeleft : longint;
 | 
						|
begin
 | 
						|
{ Something to allocate ? }
 | 
						|
  if size<=0 then
 | 
						|
   begin
 | 
						|
     { give an error for < 0 }
 | 
						|
     if size<0 then
 | 
						|
      HandleError(204);
 | 
						|
     { we always need to allocate something, using heapend is not possible,
 | 
						|
       because heappend can be changed by growheap (PFV) }
 | 
						|
     size:=1;
 | 
						|
   end;
 | 
						|
{ calc to multiply of 16 after adding the needed 8 bytes heaprecord }
 | 
						|
  size:=(size+sizeof(theaprecord)+(blocksize-1)) and (not (blocksize-1));
 | 
						|
  dec(internal_memavail,size);
 | 
						|
{ try to find a block in one of the freelists per size }
 | 
						|
  pcurr:=nil;
 | 
						|
  s:=size shr blockshr;
 | 
						|
  if s<=maxblock then
 | 
						|
   begin
 | 
						|
     { correct size match ? }
 | 
						|
     if assigned(freelists[s]) then
 | 
						|
      begin
 | 
						|
        { create the block we should return }
 | 
						|
        p:=pointer(freelists[s])+sizeof(theaprecord);
 | 
						|
        { update freelist }
 | 
						|
        freelists[s]:=freelists[s]^.next;
 | 
						|
        if assigned(freelists[s]) then
 | 
						|
         freelists[s]^.prev:=nil;
 | 
						|
        exit;
 | 
						|
      end;
 | 
						|
{$ifdef REUSEBIGGER}
 | 
						|
     { try a bigger block }
 | 
						|
     s1:=s+s;
 | 
						|
     i:=0;
 | 
						|
     while (s1<=maxblock) and (i<maxreusebigger) do
 | 
						|
      begin
 | 
						|
        if assigned(freelists[s1]) then
 | 
						|
         begin
 | 
						|
           pcurr:=freelists[s1];
 | 
						|
           break;
 | 
						|
         end;
 | 
						|
        inc(s1,s);
 | 
						|
        inc(i);
 | 
						|
      end;
 | 
						|
{$endif}
 | 
						|
   end
 | 
						|
  else
 | 
						|
   s:=0;
 | 
						|
  repeat
 | 
						|
    { not found, then check the main freelist for the first match }
 | 
						|
    heapfree:=heapend-heapptr;
 | 
						|
    if not(assigned(pcurr)) and
 | 
						|
       ((size>maxblocksize) or (heapfree<size)) then
 | 
						|
     begin
 | 
						|
       pcurr:=freelists[0];
 | 
						|
       while assigned(pcurr) do
 | 
						|
        begin
 | 
						|
          if pcurr^.size>=size then
 | 
						|
           break;
 | 
						|
          pcurr:=pcurr^.next;
 | 
						|
        end;
 | 
						|
     end;
 | 
						|
    { have we found a block, then get it and free up the other left part,
 | 
						|
      if no blocks are found then allocated at the heapptr or grow the heap }
 | 
						|
    if assigned(pcurr) then
 | 
						|
     begin
 | 
						|
       { get pointer of the block we should return }
 | 
						|
       p:=pointer(pcurr);
 | 
						|
       { remove the current block from the freelist }
 | 
						|
       if assigned(pcurr^.next) then
 | 
						|
        pcurr^.next^.prev:=pcurr^.prev;
 | 
						|
       if assigned(pcurr^.prev) then
 | 
						|
        pcurr^.prev^.next:=pcurr^.next
 | 
						|
       else
 | 
						|
        pcurr^.root^:=pcurr^.next;
 | 
						|
       { create the left over freelist block, if at least 16 bytes are free }
 | 
						|
       sizeleft:=pcurr^.size-size;
 | 
						|
       s1:=sizeleft shr blockshr;
 | 
						|
       if s1>0 then
 | 
						|
        begin
 | 
						|
          if s1>maxblock then
 | 
						|
           s1:=0;
 | 
						|
          pcurr:=pfreerecord(pointer(pcurr)+size);
 | 
						|
          pcurr^.size:=sizeleft;
 | 
						|
          pcurr^.root:=@freelists[s1];
 | 
						|
          { insert the block in the freelist }
 | 
						|
          pcurr^.next:=freelists[s1];
 | 
						|
          pcurr^.prev:=nil;
 | 
						|
          if assigned(freelists[s1]) then
 | 
						|
           freelists[s1]^.prev:=pcurr;
 | 
						|
          freelists[s1]:=pcurr;
 | 
						|
        end;
 | 
						|
       { create the block we need to return }
 | 
						|
       pheaprecord(p)^.size:=size;
 | 
						|
       pheaprecord(p)^.root:=@freelists[s];
 | 
						|
       inc(p,sizeof(theaprecord));
 | 
						|
       exit;
 | 
						|
     end;
 | 
						|
    { Lastly, the top of the heap is checked, to see if there is }
 | 
						|
    { still memory available.                                   }
 | 
						|
    again:=false;
 | 
						|
    if heapfree<size then
 | 
						|
     begin
 | 
						|
       if assigned(heaperror) then
 | 
						|
        begin
 | 
						|
          proc:=heaperrorproc(heaperror);
 | 
						|
          case proc(size) of
 | 
						|
           0 : HandleError(203);
 | 
						|
           1 : p:=nil;
 | 
						|
           2 : again:=true;
 | 
						|
          end;
 | 
						|
        end
 | 
						|
       else
 | 
						|
        HandleError(203);
 | 
						|
     end
 | 
						|
    else
 | 
						|
     begin
 | 
						|
       p:=heapptr;
 | 
						|
       pheaprecord(p)^.size:=size;
 | 
						|
       pheaprecord(p)^.root:=@freelists[s];
 | 
						|
       inc(p,sizeof(theaprecord));
 | 
						|
       inc(heapptr,size);
 | 
						|
     end;
 | 
						|
  until not again;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
{*****************************************************************************
 | 
						|
                               SysFreeMem
 | 
						|
*****************************************************************************}
 | 
						|
 | 
						|
procedure SysFreeMem(var p : pointer);
 | 
						|
begin
 | 
						|
  if p=nil then
 | 
						|
   HandleError(204);
 | 
						|
{ fix p to point to the heaprecord }
 | 
						|
  dec(p,sizeof(theaprecord));
 | 
						|
  inc(internal_memavail,pheaprecord(p)^.size);
 | 
						|
{ insert the block in it's freelist }
 | 
						|
  pfreerecord(p)^.prev:=nil;
 | 
						|
  pfreerecord(p)^.next:=pfreerecord(p)^.root^;
 | 
						|
  if assigned(pfreerecord(p)^.next) then
 | 
						|
   pfreerecord(p)^.next^.prev:=pfreerecord(p);
 | 
						|
  pfreerecord(p)^.root^:=pfreerecord(p);
 | 
						|
  p:=nil;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
procedure SysFreeMemSize(var p : pointer;size : longint);
 | 
						|
begin
 | 
						|
  if size<=0 then
 | 
						|
   begin
 | 
						|
     if size<0 then
 | 
						|
      HandleError(204);
 | 
						|
     p:=nil;
 | 
						|
     exit;
 | 
						|
   end;
 | 
						|
  if p=nil then
 | 
						|
   HandleError(204);
 | 
						|
{ fix p to point to the heaprecord }
 | 
						|
  dec(p,sizeof(theaprecord));
 | 
						|
  inc(internal_memavail,pheaprecord(p)^.size);
 | 
						|
{ size check }
 | 
						|
  if checkfreememsize and (size<>-1) then
 | 
						|
   begin
 | 
						|
     size:=(size+sizeof(theaprecord)+(blocksize-1)) and (not (blocksize-1));
 | 
						|
     if size<>pheaprecord(p)^.size then
 | 
						|
      HandleError(204);
 | 
						|
   end;
 | 
						|
{ insert the block in it's freelist }
 | 
						|
  pfreerecord(p)^.prev:=nil;
 | 
						|
  pfreerecord(p)^.next:=pfreerecord(p)^.root^;
 | 
						|
  if assigned(pfreerecord(p)^.next) then
 | 
						|
   pfreerecord(p)^.next^.prev:=pfreerecord(p);
 | 
						|
  pfreerecord(p)^.root^:=pfreerecord(p);
 | 
						|
  p:=nil;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
{*****************************************************************************
 | 
						|
                                    MemSize
 | 
						|
*****************************************************************************}
 | 
						|
 | 
						|
function SysMemSize(p:pointer):longint;
 | 
						|
begin
 | 
						|
  SysMemSize:=pheaprecord(pointer(p)-sizeof(theaprecord))^.size-sizeof(theaprecord);
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
{*****************************************************************************
 | 
						|
                                Mark/Release
 | 
						|
*****************************************************************************}
 | 
						|
 | 
						|
procedure release(var p : pointer);
 | 
						|
begin
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
procedure mark(var p : pointer);
 | 
						|
begin
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
{*****************************************************************************
 | 
						|
                                Grow Heap
 | 
						|
*****************************************************************************}
 | 
						|
 | 
						|
function growheap(size :longint) : integer;
 | 
						|
var
 | 
						|
  NewPos,
 | 
						|
  wantedsize : longint;
 | 
						|
  pcurr      : pfreerecord;
 | 
						|
begin
 | 
						|
{$ifdef DUMPGROW}
 | 
						|
  writeln('grow ',size);
 | 
						|
  DumpBlocks;
 | 
						|
{$endif}
 | 
						|
  wantedsize:=size;
 | 
						|
  { Allocate by 64K size }
 | 
						|
  size:=(size+$ffff) and $ffff0000;
 | 
						|
  { first try 256K (default) }
 | 
						|
  if size<=GrowHeapSize1 then
 | 
						|
   begin
 | 
						|
     NewPos:=Sbrk(GrowHeapSize1);
 | 
						|
     if NewPos>0 then
 | 
						|
      size:=GrowHeapSize1;
 | 
						|
   end
 | 
						|
  else
 | 
						|
  { second try 1024K (default) }
 | 
						|
   if size<=GrowHeapSize2 then
 | 
						|
    begin
 | 
						|
      NewPos:=Sbrk(GrowHeapSize2);
 | 
						|
      if NewPos>0 then
 | 
						|
       size:=GrowHeapSize2;
 | 
						|
    end
 | 
						|
  { else alloate the needed bytes }
 | 
						|
  else
 | 
						|
    NewPos:=SBrk(size);
 | 
						|
  { try again }
 | 
						|
  if NewPos=-1 then
 | 
						|
   begin
 | 
						|
     NewPos:=Sbrk(size);
 | 
						|
     if NewPos=-1 then
 | 
						|
      begin
 | 
						|
        GrowHeap:=0;
 | 
						|
        Exit;
 | 
						|
      end;
 | 
						|
   end;
 | 
						|
{ increase heapend or add to freelist }
 | 
						|
  if heapend=pointer(newpos) then
 | 
						|
   begin
 | 
						|
     heapend:=pointer(newpos+size);
 | 
						|
   end
 | 
						|
  else
 | 
						|
   begin
 | 
						|
     { create freelist entry for old heapptr-heapend }
 | 
						|
     pcurr:=pfreerecord(heapptr);
 | 
						|
     pcurr^.size:=heapend-heapptr;
 | 
						|
     pcurr^.root:=@freelists[0];
 | 
						|
     { insert the block in the freelist }
 | 
						|
     pcurr^.next:=freelists[0];
 | 
						|
     pcurr^.prev:=nil;
 | 
						|
     if assigned(freelists[0]) then
 | 
						|
      freelists[0]^.prev:=pcurr;
 | 
						|
     freelists[0]:=pcurr;
 | 
						|
     { now set the new heapptr,heapend to the new block }
 | 
						|
     heapptr:=pointer(newpos);
 | 
						|
     heapend:=pointer(newpos+size);
 | 
						|
   end;
 | 
						|
{ set the total new heap size }
 | 
						|
  inc(internal_memavail,size);
 | 
						|
  inc(internal_heapsize,size);
 | 
						|
{ try again }
 | 
						|
  GrowHeap:=2;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
{*****************************************************************************
 | 
						|
                                 InitHeap
 | 
						|
*****************************************************************************}
 | 
						|
 | 
						|
{ This function will initialize the Heap manager and need to be called from
 | 
						|
  the initialization of the system unit }
 | 
						|
procedure InitHeap;
 | 
						|
begin
 | 
						|
  FillChar(FreeLists,sizeof(TFreeLists),0);
 | 
						|
  internal_heapsize:=GetHeapSize;
 | 
						|
  internal_memavail:=internal_heapsize;
 | 
						|
  HeapOrg:=GetHeapStart;
 | 
						|
  HeapPtr:=HeapOrg;
 | 
						|
  HeapEnd:=HeapOrg+internal_memavail;
 | 
						|
  HeapError:=@GrowHeap;
 | 
						|
end;
 | 
						|
 | 
						|
{
 | 
						|
  $Log$
 | 
						|
  Revision 1.16  1999-09-17 17:14:12  peter
 | 
						|
    + new heap manager supporting delphi freemem(pointer)
 | 
						|
 | 
						|
}
 |