mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-05-14 15:52:31 +02: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)
|
|
|
|
}
|