mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-06 22:48:07 +02:00
887 lines
26 KiB
ObjectPascal
887 lines
26 KiB
ObjectPascal
{
|
|
$Id: $
|
|
This file is part of the Free Pascal run time library.
|
|
Copyright (c) 2004 by Daniel Mantione
|
|
member of the Free Pascal development team
|
|
|
|
Implements a memory manager that makes use of the fact that
|
|
a program is running in a virtual address space where pages
|
|
can be allocated at random, instead of a more traditional
|
|
growing heap.
|
|
|
|
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.
|
|
|
|
**********************************************************************}
|
|
{$IFNDEF FPC_DOTTEDUNITS}
|
|
unit pagemem;
|
|
{$ENDIF FPC_DOTTEDUNITS}
|
|
|
|
{*****************************************************************************}
|
|
interface
|
|
{*****************************************************************************}
|
|
|
|
{*****************************************************************************}
|
|
implementation
|
|
{*****************************************************************************}
|
|
|
|
{$packrecords 1}
|
|
{$packenum 1}
|
|
|
|
type Tpage_type=(pt_8byte_with_bitmap,pt_suballocation,pt_direct_page);
|
|
Ppage_type=^Tpage_type;
|
|
|
|
Pcriterium=^Tcriterium;
|
|
Tcriterium=record
|
|
criterium1,criterium2:cardinal;
|
|
end;
|
|
|
|
Ptree_struct=^Ttree_struct;
|
|
Ttree_struct=record
|
|
left,right:ptruint;
|
|
end;
|
|
|
|
{This page layout is targeted at very short strings and linked lists
|
|
with very low payload. It uses fixed memory sizes of 8 byte. Memory
|
|
overhead should be avoided at all here. An allocation bitmap does this
|
|
very well, only 1 bit per memory block.}
|
|
Ppage_8byte_with_bitmap=^Tpage_8byte_with_bitmap;
|
|
Tpage_8byte_with_bitmap=record
|
|
page_type:Tpage_type;
|
|
search_index:byte;
|
|
free_count:word;
|
|
page_birthyear:cardinal;
|
|
freelist_prev,freelist_next:Ppage_8byte_with_bitmap;
|
|
block_allocation_map:array[0..15] of cardinal;
|
|
end;
|
|
|
|
Ppage_suballocation=^Tpage_suballocation;
|
|
Tpage_suballocation=record
|
|
page_type:Tpage_type;
|
|
reserved:array[1..3] of byte;
|
|
page_birthyear:cardinal;
|
|
end;
|
|
|
|
{This page layout is targeted at large memory blocks. We allocate
|
|
pages directly from the OS for such blocks.}
|
|
Ppage_direct=^Tpage_direct;
|
|
Tpage_direct=record
|
|
page_type:Tpage_type;
|
|
reserved:array[1..3] of byte;
|
|
size:cardinal;
|
|
end;
|
|
|
|
Pfree_block=^Tfree_block;
|
|
Tfree_block=record
|
|
size:cardinal;
|
|
tree_sizememloc:Ttree_struct;
|
|
tree_memlocation:Ttree_struct;
|
|
end;
|
|
|
|
Tsplay_status=(ts_not_found,ts_found_on_left,
|
|
ts_found_on_p,ts_found_on_right);
|
|
|
|
Psuballoc_header=^Tsuballoc_header;
|
|
Tsuballoc_header=record
|
|
alloc_size:ptruint;
|
|
end;
|
|
|
|
const tree_sizememloc_offset=4;
|
|
tree_memlocation_offset=12;
|
|
|
|
page_size=4096;
|
|
page_shift=12;
|
|
page_mask=$00000fff;
|
|
page_8byte_with_bitmap_maxspace=
|
|
(page_size-sizeof(Tpage_8byte_with_bitmap)) div 8;
|
|
|
|
memblock_align=4;
|
|
memblock_alignround=memblock_align-1;
|
|
|
|
min_suballoc_size=sizeof(Tfree_block);
|
|
|
|
const freelist_8byte_with_bitmap:Ppage_8byte_with_bitmap=nil;
|
|
page_8byte_with_bitmap_init:Tpage_8byte_with_bitmap=
|
|
(
|
|
page_type:pt_8byte_with_bitmap;
|
|
search_index:0;
|
|
free_count:page_8byte_with_bitmap_maxspace;
|
|
page_birthyear:0;
|
|
freelist_prev:nil;
|
|
freelist_next:nil;
|
|
block_allocation_map:($ffffffff,$ffffffff,$ffffffff,$ffffffff,
|
|
$ffffffff,$ffffffff,$ffffffff,$ffffffff,
|
|
$ffffffff,$ffffffff,$ffffffff,$ffffffff,
|
|
$ffffffff,$ffffffff,$ffffffff,$ffffffff)
|
|
);
|
|
|
|
var tree_sizememloc,tree_memlocation:Pfree_block;
|
|
|
|
{****************************************************************************
|
|
Page allocation/deallocation
|
|
****************************************************************************}
|
|
|
|
function fpmmap(adr:pointer;len,prot,flags,fd,off:sizeint):pointer;external name 'FPC_SYSC_MMAP';
|
|
function fpmunmap(adr:pointer;len:sizeint):pointer;external name 'FPC_SYSC_MUNMAP';
|
|
function geterrno:longint;external name 'FPC_SYS_GETERRNO';
|
|
|
|
const PROT_READ = $1; { page can be read }
|
|
PROT_WRITE = $2; { page can be written }
|
|
PROT_EXEC = $4; { page can be executed }
|
|
PROT_NONE = $0; { page can not be accessed }
|
|
|
|
MAP_SHARED = $1; { Share changes }
|
|
MAP_PRIVATE = $2; { Changes are private }
|
|
MAP_TYPE = $f; { Mask for type of mapping }
|
|
MAP_FIXED = $10; { Interpret addr exactly }
|
|
MAP_ANONYMOUS = $20; { don't use a file }
|
|
|
|
MAP_GROWSDOWN = $100; { stack-like segment }
|
|
MAP_DENYWRITE = $800; { ETXTBSY }
|
|
MAP_EXECUTABLE = $1000; { mark it as an executable }
|
|
MAP_LOCKED = $2000; { pages are locked }
|
|
MAP_NORESERVE = $4000; { don't check for reservations }
|
|
|
|
function req_pages(count:cardinal):pointer;
|
|
|
|
{Requests count consecutive pages from the OS.}
|
|
|
|
begin
|
|
req_pages:=fpmmap(nil,count shl page_shift,PROT_READ or PROT_WRITE,
|
|
MAP_PRIVATE or MAP_ANONYMOUS,0,0);
|
|
if geterrno<>0 then
|
|
req_pages:=nil; {This one can fail, so we can handle an out of memory
|
|
situation.}
|
|
end;
|
|
|
|
procedure sack_pages(p:pointer;count:cardinal);
|
|
|
|
begin
|
|
fpmunmap(p,count shl page_shift);
|
|
if geterrno<>0 then
|
|
runerror(204); {This one should succees.}
|
|
end;
|
|
|
|
{****************************************************************************
|
|
8-bit bitmap allocated memory
|
|
****************************************************************************}
|
|
|
|
procedure new_page_8byte_with_bitmap;
|
|
|
|
var page:Ppage_8byte_with_bitmap;
|
|
|
|
begin
|
|
page:=req_pages(1);
|
|
page^:=page_8byte_with_bitmap_init;
|
|
page^.freelist_next:=freelist_8byte_with_bitmap;
|
|
page^.freelist_prev:=nil;
|
|
if freelist_8byte_with_bitmap<>nil then
|
|
freelist_8byte_with_bitmap^.freelist_prev:=page;
|
|
freelist_8byte_with_bitmap:=page;
|
|
end;
|
|
|
|
function pgetmem_8byte_with_bitmap:pointer;
|
|
|
|
var page:Ppage_8byte_with_bitmap;
|
|
bit:cardinal;
|
|
|
|
begin
|
|
if freelist_8byte_with_bitmap=nil then
|
|
new_page_8byte_with_bitmap;
|
|
page:=freelist_8byte_with_bitmap;
|
|
with page^ do
|
|
begin
|
|
{Search a dword in which a bit is set.}
|
|
while block_allocation_map[search_index]=0 do
|
|
search_index:=(search_index+1) and 15;
|
|
ptrint(pgetmem_8byte_with_bitmap):=ptrint(page)+sizeof(page^)+search_index*256;
|
|
{Search for a set bit in the dword.}
|
|
bit:=1;
|
|
while block_allocation_map[search_index] and bit=0 do
|
|
begin
|
|
bit:=bit shl 1;
|
|
inc(ptrint(pgetmem_8byte_with_bitmap),8);
|
|
end;
|
|
{Allocate the block.}
|
|
block_allocation_map[search_index]:=block_allocation_map[search_index] and not bit;
|
|
dec(free_count);
|
|
if free_count=0 then
|
|
begin
|
|
{There is no space left in this page. Remove it from the freelist.}
|
|
if freelist_next<>nil then
|
|
freelist_next^.freelist_prev:=freelist_prev;
|
|
if freelist_prev<>nil then
|
|
freelist_prev^.freelist_next:=freelist_next;
|
|
if freelist_8byte_with_bitmap=page then
|
|
freelist_8byte_with_bitmap:=freelist_next;
|
|
freelist_prev:=nil;
|
|
freelist_next:=nil;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function pfreemem_8byte_with_bitmap(page:Ppage_8byte_with_bitmap;p:pointer):ptrint;
|
|
|
|
var index,bit:cardinal;
|
|
|
|
begin
|
|
index:=(ptrint(p)-ptrint(page)-sizeof(page^)) div 8;
|
|
bit:=index and 31;
|
|
index:=index shr 5;
|
|
with page^ do
|
|
begin
|
|
if free_count=0 then
|
|
begin
|
|
{Page will get free slots. Must be included in freelist.}
|
|
if freelist_8byte_with_bitmap=nil then
|
|
freelist_8byte_with_bitmap:=page
|
|
else
|
|
begin
|
|
freelist_next:=freelist_8byte_with_bitmap;
|
|
freelist_8byte_with_bitmap^.freelist_prev:=page;
|
|
freelist_8byte_with_bitmap:=page;
|
|
end;
|
|
{Make sure the next allocation finds the slot without much searching.}
|
|
search_index:=index;
|
|
end;
|
|
block_allocation_map[index]:=block_allocation_map[index] or (1 shl bit);
|
|
inc(free_count);
|
|
if free_count=page_8byte_with_bitmap_maxspace then
|
|
begin
|
|
{The page is completely free. It can be returned to the OS, but
|
|
remove it from the freelist first.}
|
|
if freelist_next<>nil then
|
|
freelist_next^.freelist_prev:=freelist_prev;
|
|
if freelist_prev<>nil then
|
|
freelist_prev^.freelist_next:=freelist_next;
|
|
if freelist_8byte_with_bitmap=page then
|
|
freelist_8byte_with_bitmap:=freelist_next;
|
|
sack_pages(page,1);
|
|
end;
|
|
end;
|
|
pfreemem_8byte_with_bitmap:=8;
|
|
end;
|
|
|
|
{****************************************************************************
|
|
Splay tree stuff
|
|
****************************************************************************}
|
|
|
|
{ $define debug}
|
|
{$ifdef debug}
|
|
procedure write_sizememloc_tree(tree:Pfree_block;level:cardinal);
|
|
|
|
var i:cardinal;
|
|
|
|
begin
|
|
if tree=nil then
|
|
exit;
|
|
write_sizememloc_tree(Pfree_block(tree^.tree_sizememloc.left),level+1);
|
|
for i:=1 to level do
|
|
write(' ');
|
|
writeln(tree^.size,' ',hexstr(ptruint(tree),8));
|
|
write_sizememloc_tree(Pfree_block(tree^.tree_sizememloc.right),level+1);
|
|
end;
|
|
|
|
procedure write_memlocation_tree(tree:Pfree_block;level:cardinal);
|
|
|
|
var i:cardinal;
|
|
|
|
begin
|
|
if tree=nil then
|
|
exit;
|
|
write_memlocation_tree(Pfree_block(tree^.tree_memlocation.left),level+1);
|
|
for i:=1 to level do
|
|
write(' ');
|
|
writeln(hexstr(ptruint(tree),8));
|
|
write_memlocation_tree(Pfree_block(tree^.tree_memlocation.right),level+1);
|
|
end;
|
|
{$endif}
|
|
|
|
procedure rotate_l(var p:ptruint;offset:cardinal);
|
|
|
|
var p1:ptruint;
|
|
|
|
begin
|
|
p1:=Ptree_struct(p+offset)^.right;
|
|
Ptree_struct(p+offset)^.right:=Ptree_struct(p1+offset)^.left;
|
|
Ptree_struct(p1+offset)^.left:=p;
|
|
p:=p1;
|
|
end;
|
|
|
|
procedure rotate_r(var p:ptruint;offset:cardinal);
|
|
|
|
var p1:ptruint;
|
|
|
|
begin
|
|
p1:=Ptree_struct(p+offset)^.left;
|
|
Ptree_struct(p+offset)^.left:=Ptree_struct(p1+offset)^.right;
|
|
Ptree_struct(p1+offset)^.right:=p;
|
|
p:=p1;
|
|
end;
|
|
|
|
procedure zigzig(var p:ptruint;offset:cardinal);inline;
|
|
|
|
begin
|
|
rotate_r(p,offset);
|
|
rotate_r(p,offset);
|
|
end;
|
|
|
|
procedure zigzag(var p:ptruint;offset:cardinal);inline;
|
|
|
|
begin
|
|
rotate_l(Ptree_struct(p+offset)^.left,offset);
|
|
rotate_r(p,offset);
|
|
end;
|
|
|
|
procedure zagzig(var p:ptruint;offset:cardinal);inline;
|
|
|
|
begin
|
|
rotate_r(Ptree_struct(p+offset)^.right,offset);
|
|
rotate_l(p,offset);
|
|
end;
|
|
|
|
procedure zagzag(var p:ptruint;offset:cardinal);inline;
|
|
|
|
begin
|
|
rotate_l(p,offset);
|
|
rotate_l(p,offset);
|
|
end;
|
|
|
|
procedure delete_from_tree(var p:ptruint;offset:cardinal);
|
|
|
|
var p1:ptruint;
|
|
pp1:^ptruint;
|
|
|
|
begin
|
|
if Ptree_struct(p+offset)^.left=0 then
|
|
p:=Ptree_struct(p+offset)^.right
|
|
else
|
|
begin
|
|
if Ptree_struct(p+offset)^.right<>0 then
|
|
begin
|
|
{Both are occupied. Move right to rightmost leaf of left.}
|
|
p1:=Ptree_struct(p+offset)^.left;
|
|
repeat
|
|
pp1:=@Ptree_struct(p1+offset)^.right;
|
|
p1:=pp1^;
|
|
until p1=0;
|
|
pp1^:=Ptree_struct(p+offset)^.right;
|
|
end;
|
|
p:=Ptree_struct(p+offset)^.left;
|
|
end;
|
|
end;
|
|
|
|
function find_sizememloc(size:ptruint;var p:Pfree_block):Tsplay_status;
|
|
|
|
begin
|
|
find_sizememloc:=ts_found_on_p;
|
|
if p=nil then
|
|
find_sizememloc:=ts_not_found
|
|
else if size<p^.size then {Do nothing if equal...}
|
|
case find_sizememloc(size,Pfree_block(p^.tree_sizememloc.left)) of
|
|
ts_not_found:
|
|
if p^.size<size then
|
|
find_sizememloc:=ts_not_found;
|
|
ts_found_on_left:
|
|
zigzig(ptruint(p),tree_sizememloc_offset);
|
|
ts_found_on_p:
|
|
find_sizememloc:=ts_found_on_left;
|
|
ts_found_on_right:
|
|
zigzag(ptruint(p),tree_sizememloc_offset);
|
|
end
|
|
else if size>p^.size then
|
|
case find_sizememloc(size,Pfree_block(p^.tree_sizememloc.right)) of
|
|
ts_not_found:
|
|
if p^.size<size then
|
|
find_sizememloc:=ts_not_found;
|
|
ts_found_on_left:
|
|
zagzig(ptruint(p),tree_sizememloc_offset);
|
|
ts_found_on_p:
|
|
find_sizememloc:=ts_found_on_right;
|
|
ts_found_on_right:
|
|
zagzag(ptruint(p),tree_sizememloc_offset);
|
|
end;
|
|
end;
|
|
|
|
{$if 0}
|
|
function find_sizememloc(size,loc:ptruint;var p:Pfree_block):Tsplay_status;
|
|
|
|
var on_left:boolean;
|
|
|
|
begin
|
|
find_sizememloc:=ts_found_on_p;
|
|
if p=nil then
|
|
find_sizememloc:=ts_not_found
|
|
else
|
|
begin
|
|
on_left:=size<p^.size;
|
|
if size=p^.size then
|
|
if loc=ptruint(p) then
|
|
exit
|
|
else
|
|
on_left:=loc<ptruint(p);
|
|
if on_left then
|
|
case find_sizememloc(size,loc,Pfree_block(p^.tree_sizememloc.left)) of
|
|
ts_not_found:
|
|
find_sizememloc:=ts_not_found;
|
|
ts_found_on_left:
|
|
zigzig(ptruint(p),tree_sizememloc_offset);
|
|
ts_found_on_p:
|
|
find_sizememloc:=ts_found_on_left;
|
|
ts_found_on_right:
|
|
zigzag(ptruint(p),tree_sizememloc_offset);
|
|
end
|
|
else
|
|
case find_sizememloc(size,loc,Pfree_block(p^.tree_sizememloc.right)) of
|
|
ts_not_found:
|
|
find_sizememloc:=ts_not_found;
|
|
ts_found_on_left:
|
|
zagzig(ptruint(p),tree_sizememloc_offset);
|
|
ts_found_on_p:
|
|
find_sizememloc:=ts_found_on_right;
|
|
ts_found_on_right:
|
|
zagzag(ptruint(p),tree_sizememloc_offset);
|
|
end;
|
|
end;
|
|
end;
|
|
{$endif}
|
|
|
|
function insert_sizememloc(node:Pfree_block;var p:Pfree_block):Tsplay_status;
|
|
|
|
{Preconditions:
|
|
|
|
node^.size is set
|
|
node^.tree_sizememloc.left is set to nil
|
|
node^.tree_sizememloc.right is set to nil}
|
|
|
|
var on_left:boolean;
|
|
|
|
begin
|
|
insert_sizememloc:=ts_found_on_p;
|
|
if p=nil then
|
|
p:=node
|
|
else
|
|
begin
|
|
on_left:=node^.size<p^.size;
|
|
if node^.size=p^.size then
|
|
on_left:=ptruint(node)<ptruint(p);
|
|
if on_left then
|
|
case insert_sizememloc(node,Pfree_block(p^.tree_sizememloc.left)) of
|
|
ts_found_on_left:
|
|
zigzig(ptruint(p),tree_sizememloc_offset);
|
|
ts_found_on_p:
|
|
insert_sizememloc:=ts_found_on_left;
|
|
ts_found_on_right:
|
|
zigzag(ptruint(p),tree_sizememloc_offset);
|
|
end
|
|
else
|
|
case insert_sizememloc(node,Pfree_block(p^.tree_sizememloc.right)) of
|
|
ts_found_on_left:
|
|
zagzig(ptruint(p),tree_sizememloc_offset);
|
|
ts_found_on_p:
|
|
insert_sizememloc:=ts_found_on_right;
|
|
ts_found_on_right:
|
|
zagzag(ptruint(p),tree_sizememloc_offset);
|
|
end;
|
|
end;
|
|
{$ifdef debug}
|
|
writeln('sizememlocboom na insert');
|
|
write_sizememloc_tree(tree_sizememloc,1);
|
|
{$endif}
|
|
end;
|
|
|
|
{$if 0}
|
|
function find_memlocation(location:ptruint;var p:Pfree_block;
|
|
find_smaller:boolean):Tsplay_status;
|
|
|
|
begin
|
|
find_memlocation:=ts_found_on_p;
|
|
if p=nil then
|
|
find_memlocation:=ts_not_found
|
|
else if location<ptruint(p) then {Do nothing if equal...}
|
|
case find_memlocation(location,Pfree_block(p^.tree_memlocation.left),
|
|
find_smaller) of
|
|
ts_not_found:
|
|
if (ptruint(p)>location) or not find_smaller then
|
|
find_memlocation:=ts_not_found;
|
|
ts_found_on_left:
|
|
zigzig(ptruint(p),tree_memlocation_offset);
|
|
ts_found_on_p:
|
|
find_memlocation:=ts_found_on_left;
|
|
ts_found_on_right:
|
|
zigzag(ptruint(p),tree_memlocation_offset);
|
|
end
|
|
else if location>ptruint(p) then
|
|
case find_memlocation(location,Pfree_block(p^.tree_memlocation.right),
|
|
find_smaller) of
|
|
ts_not_found:
|
|
if (ptruint(p)>location) or not find_smaller then
|
|
find_memlocation:=ts_not_found;
|
|
ts_found_on_left:
|
|
zagzig(ptruint(p),tree_memlocation_offset);
|
|
ts_found_on_p:
|
|
find_memlocation:=ts_found_on_right;
|
|
ts_found_on_right:
|
|
zagzag(ptruint(p),tree_memlocation_offset);
|
|
end;
|
|
end;
|
|
{$endif}
|
|
|
|
function insert_memlocation(node:Pfree_block;var p:Pfree_block):Tsplay_status;
|
|
|
|
{Preconditions:
|
|
|
|
node^.size is set
|
|
node^.tree_sizememloc.left is set to nil
|
|
node^.tree_sizememloc.right is set to nil}
|
|
|
|
begin
|
|
insert_memlocation:=ts_found_on_p;
|
|
if p=nil then
|
|
p:=node
|
|
else if ptruint(node)<=ptruint(p) then {Equal? Insert on left.}
|
|
case insert_memlocation(node,Pfree_block(p^.tree_memlocation.left)) of
|
|
ts_found_on_left:
|
|
zigzig(ptruint(p),tree_memlocation_offset);
|
|
ts_found_on_p:
|
|
insert_memlocation:=ts_found_on_left;
|
|
ts_found_on_right: zigzag(ptruint(p),tree_memlocation_offset);
|
|
end
|
|
else if ptruint(node)>ptruint(p) then
|
|
case insert_memlocation(node,Pfree_block(p^.tree_memlocation.right)) of
|
|
ts_found_on_left:
|
|
zagzig(ptruint(p),tree_memlocation_offset);
|
|
ts_found_on_p:
|
|
insert_memlocation:=ts_found_on_right;
|
|
ts_found_on_right:
|
|
zagzag(ptruint(p),tree_memlocation_offset);
|
|
end;
|
|
{$ifdef debug}
|
|
writeln('memlocationboom na insert');
|
|
write_memlocation_tree(tree_memlocation,1);
|
|
{$endif}
|
|
end;
|
|
|
|
function get_memlocation(node:Pfree_block):Pfree_block;
|
|
|
|
{Iteratively delete node from tree without splaying.}
|
|
|
|
var p:^Pfree_block;
|
|
|
|
begin
|
|
p:=@tree_memlocation;
|
|
while (p^<>nil) and (p^<>node) do
|
|
if ptruint(node)<ptruint(p^) then
|
|
p:=@p^^.tree_memlocation.left
|
|
else
|
|
p:=@p^^.tree_memlocation.right;
|
|
get_memlocation:=p^;
|
|
if p^<>nil then
|
|
delete_from_tree(ptruint(p^),tree_memlocation_offset);
|
|
end;
|
|
|
|
function get_sizememloc(node:Pfree_block):Pfree_block;
|
|
|
|
{Iteratively delete node from tree without splaying.}
|
|
|
|
var p:^Pfree_block;
|
|
on_left:boolean;
|
|
|
|
begin
|
|
p:=@tree_sizememloc;
|
|
while (p^<>nil) and (p^<>node) do
|
|
begin
|
|
on_left:=node^.size<p^^.size;
|
|
if node^.size=p^^.size then
|
|
on_left:=ptruint(node)<ptruint(p^);
|
|
if on_left then
|
|
p:=@p^^.tree_sizememloc.left
|
|
else
|
|
p:=@p^^.tree_sizememloc.right;
|
|
end;
|
|
get_sizememloc:=p^;
|
|
if p^<>nil then
|
|
delete_from_tree(ptruint(p^),tree_sizememloc_offset);
|
|
end;
|
|
|
|
function get_block_by_size(size:cardinal):Pfree_block;
|
|
|
|
var what:^ptruint;
|
|
|
|
begin
|
|
case find_sizememloc(size,tree_sizememloc) of
|
|
ts_not_found:
|
|
begin
|
|
get_block_by_size:=nil;
|
|
exit;
|
|
end;
|
|
ts_found_on_left:
|
|
what:=@tree_sizememloc^.tree_sizememloc.left;
|
|
ts_found_on_p:
|
|
what:=@tree_sizememloc;
|
|
ts_found_on_right:
|
|
what:=@tree_sizememloc^.tree_sizememloc.right;
|
|
end;
|
|
get_block_by_size:=Pfree_block(what^);
|
|
delete_from_tree(what^,tree_sizememloc_offset);
|
|
if get_memlocation(get_block_by_size)=nil then
|
|
runerror(204);
|
|
end;
|
|
|
|
function get_block_by_memlocation(location:ptruint):Pfree_block;
|
|
|
|
var what:^ptruint;
|
|
|
|
begin
|
|
get_block_by_memlocation:=get_memlocation(Pfree_block(location));
|
|
if get_block_by_memlocation<>nil then
|
|
begin
|
|
get_sizememloc(get_block_by_memlocation);
|
|
{ case find_sizememloc(get_block_by_memlocation^.size,
|
|
ptruint(get_block_by_memlocation),tree_sizememloc) of
|
|
ts_not_found:
|
|
runerror(204);
|
|
ts_found_on_left:
|
|
what:=@tree_sizememloc^.tree_sizememloc.left;
|
|
ts_found_on_p:
|
|
what:=@tree_sizememloc;
|
|
ts_found_on_right:
|
|
what:=@tree_sizememloc^.tree_sizememloc.right;
|
|
end;
|
|
delete_from_tree(what^,tree_sizememloc_offset);}
|
|
end;
|
|
end;
|
|
|
|
function get_smaller_neighbour(location:ptruint):Pfree_block;
|
|
|
|
var p,what:^ptruint;
|
|
|
|
begin
|
|
{Find a smaller block. Don't splay as it will be deleted.}
|
|
p:=@tree_memlocation;
|
|
what:=nil;
|
|
while (p^<>0) do
|
|
if location<=p^ then
|
|
p:=@Pfree_block(p^)^.tree_memlocation.left
|
|
else
|
|
begin
|
|
what:=p;
|
|
p:=@Pfree_block(p^)^.tree_memlocation.right;
|
|
end;
|
|
if (what=nil) or (ptruint(what^)+Pfree_block(what^)^.size<>location) then
|
|
begin
|
|
get_smaller_neighbour:=nil;
|
|
exit;
|
|
end;
|
|
get_smaller_neighbour:=Pfree_block(what^);
|
|
delete_from_tree(ptruint(what^),tree_memlocation_offset);
|
|
get_sizememloc(get_smaller_neighbour);
|
|
end;
|
|
|
|
{function pgetmem_directpage(memsize:ptrint):pointer;
|
|
|
|
var npages:ptrint;
|
|
|
|
begin
|
|
npages:=(memsize+sizeof(Tpage_direct)+page_size-1) div page_size;
|
|
pgetmem_directpage:=req_pages(npages);
|
|
with Ppage_direct(pgetmem_directpage)^ do
|
|
begin
|
|
page_type:=pt_direct_page;
|
|
size:=memsize;
|
|
end;
|
|
end;
|
|
}
|
|
|
|
function pgetmem_suballocpage(memsize:ptrint):pointer;
|
|
|
|
var free_block:Pfree_block;
|
|
page:pointer;
|
|
needsize,remaining,block_start:ptruint;
|
|
|
|
begin
|
|
{$ifdef debug}
|
|
writeln('-------Getmem------- ',memsize);
|
|
{$endif}
|
|
{Constant parts on left because of constant evaluation.}
|
|
needsize:=(sizeof(Tsuballoc_header)+memblock_alignround+memsize) and not memblock_alignround;
|
|
if needsize<min_suballoc_size then
|
|
needsize:=min_suballoc_size;
|
|
{$ifdef debug}
|
|
writeln('sizememlocboom voor get:');
|
|
write_sizememloc_tree(tree_sizememloc,2);
|
|
{$endif}
|
|
free_block:=get_block_by_size(needsize);
|
|
if free_block=nil then
|
|
begin
|
|
page:=req_pages(1);
|
|
Ppage_suballocation(page)^.page_type:=pt_suballocation;
|
|
{Allocate at the end of the page, a free block at the start.}
|
|
free_block:=Pfree_block(ptruint(page)+sizeof(Tpage_suballocation));
|
|
remaining:=page_size-needsize-sizeof(Tpage_suballocation);
|
|
block_start:=ptruint(page)+page_size-needsize;
|
|
Psuballoc_header(block_start)^.alloc_size:=needsize;
|
|
pgetmem_suballocpage:=pointer(block_start+sizeof(Tsuballoc_header));
|
|
end
|
|
else
|
|
begin
|
|
block_start:=ptruint(free_block);
|
|
remaining:=free_block^.size-needsize;
|
|
if (remaining<min_suballoc_size) then
|
|
begin
|
|
needsize:=free_block^.size;
|
|
free_block:=nil;
|
|
end
|
|
else
|
|
inc(ptruint(free_block),needsize);
|
|
Psuballoc_header(block_start)^.alloc_size:=needsize;
|
|
pgetmem_suballocpage:=pointer(block_start+sizeof(Tsuballoc_header));
|
|
end;
|
|
if free_block<>nil then
|
|
begin
|
|
with free_block^ do
|
|
begin
|
|
size:=remaining;
|
|
tree_sizememloc.left:=0;
|
|
tree_sizememloc.right:=0;
|
|
tree_memlocation.left:=0;
|
|
tree_memlocation.right:=0;
|
|
end;
|
|
insert_sizememloc(free_block,tree_sizememloc);
|
|
insert_memlocation(free_block,tree_memlocation);
|
|
end;
|
|
end;
|
|
|
|
function pfreemem_suballoc_page(page:Ppage_direct;p:pointer):ptrint;
|
|
|
|
var free_block,neighbour:Pfree_block;
|
|
headerp:Psuballoc_header;
|
|
asize:ptruint;
|
|
|
|
begin
|
|
{$Ifdef debug}
|
|
write('-------Freemem------- ');
|
|
{$endif}
|
|
headerp:=Psuballoc_header(ptrint(p)-sizeof(Tsuballoc_header));
|
|
asize:=headerp^.alloc_size;
|
|
{$ifdef debug}
|
|
writeln(hexstr(ptruint(page),8),' ',asize);
|
|
{$endif}
|
|
free_block:=Pfree_block(headerp);
|
|
{Search neighbour to coalesce with above block.}
|
|
neighbour:=get_block_by_memlocation(ptruint(free_block)+asize);
|
|
if neighbour<>nil then
|
|
inc(asize,neighbour^.size);
|
|
{Search neighbour to coalesce with below block.}
|
|
neighbour:=get_smaller_neighbour(ptruint(free_block));
|
|
if neighbour<>nil then
|
|
begin
|
|
inc(asize,neighbour^.size);
|
|
free_block:=neighbour;
|
|
end;
|
|
{Page empty??}
|
|
if (ptruint(free_block) and page_mask=sizeof(Tpage_suballocation)) and
|
|
(asize=page_size-sizeof(Tpage_suballocation)) then
|
|
sack_pages(pointer(ptruint(free_block) and not page_mask),1)
|
|
else
|
|
begin
|
|
with free_block^ do
|
|
begin
|
|
size:=asize;
|
|
tree_sizememloc.left:=0;
|
|
tree_sizememloc.right:=0;
|
|
tree_memlocation.left:=0;
|
|
tree_memlocation.right:=0;
|
|
end;
|
|
insert_sizememloc(free_block,tree_sizememloc);
|
|
insert_memlocation(free_block,tree_memlocation);
|
|
end;
|
|
end;
|
|
|
|
function pgetmem(size:ptrint):pointer;
|
|
|
|
begin
|
|
if size<=8 then
|
|
pgetmem:=pgetmem_8byte_with_bitmap
|
|
else
|
|
pgetmem:=pgetmem_suballocpage(size);
|
|
end;
|
|
|
|
function pallocmem(size:ptrint):pointer;
|
|
|
|
begin
|
|
if size<=8 then
|
|
begin
|
|
pallocmem:=pgetmem_8byte_with_bitmap;
|
|
fillchar(Pbyte(pallocmem)^,8,0);
|
|
end
|
|
else
|
|
{Freshly allocated pages are allways already cleared.}
|
|
{ pgallocmem:=pgallocmem_directpage(size)};
|
|
end;
|
|
|
|
|
|
function pfreemem(p:pointer):ptrint;
|
|
|
|
var page:pointer;
|
|
|
|
begin
|
|
page:=pointer(ptrint(p) and not page_mask);
|
|
case Ppage_type(page)^ of
|
|
pt_8byte_with_bitmap:
|
|
pfreemem:=pfreemem_8byte_with_bitmap(page,p);
|
|
pt_suballocation:
|
|
pfreemem:=pfreemem_suballoc_page(page,p);
|
|
else
|
|
runerror(204);
|
|
end;
|
|
end;
|
|
|
|
function pfreememsize(p:pointer;size:ptrint):ptrint;
|
|
|
|
begin
|
|
{ runerror(204);}
|
|
pfreemem(p);
|
|
end;
|
|
|
|
function preallocmem(var p:pointer;size:ptrint):pointer;
|
|
|
|
begin
|
|
runerror(204);
|
|
end;
|
|
|
|
function pmemsize(p:pointer):ptrint;
|
|
|
|
begin
|
|
runerror(204);
|
|
end;
|
|
|
|
const page_memory_manager:Tmemorymanager=
|
|
(
|
|
needlock:false;
|
|
getmem:@pgetmem;
|
|
freemem:@pfreemem;
|
|
freememsize:@pfreememsize;
|
|
allocmem:@pallocmem;
|
|
reallocmem:@preallocmem;
|
|
memsize:@pmemsize;
|
|
{ memavail:@pmemavail;}
|
|
{ maxavail:@pmaxavail;}
|
|
{ heapsize:@pheapsize;}
|
|
);
|
|
|
|
var oldmemman:Tmemorymanager;
|
|
|
|
initialization
|
|
getmemorymanager(oldmemman);
|
|
setmemorymanager(page_memory_manager);
|
|
finalization
|
|
setmemorymanager(oldmemman);
|
|
end.
|
|
|