home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 1997 April
/
Chip_1997-04_cd.bin
/
prezent
/
cb
/
data.z
/
GETMEM.INC
< prev
next >
Wrap
Text File
|
1997-01-16
|
34KB
|
1,381 lines
// Three layers:
// - Address space administration
// - Committed space administration
// - Suballocator
//
// Helper module: administrating block descriptors
//
//
// Operating system interface
//
const
LMEM_FIXED = 0;
LMEM_ZEROINIT = $40;
MEM_COMMIT = $1000;
MEM_RESERVE = $2000;
MEM_DECOMMIT = $4000;
MEM_RELEASE = $8000;
PAGE_NOACCESS = 1;
PAGE_READWRITE = 4;
type
DWORD = Integer;
BOOL = LongBool;
TRTLCriticalSection = record
DebugInfo: Pointer;
LockCount: Longint;
RecursionCount: Longint;
OwningThread: Integer;
LockSemaphore: Integer;
Reserved: DWORD;
end;
function LocalAlloc(flags, size: Integer): Pointer; stdcall;
external kernel name 'LocalAlloc';
function LocalFree(addr: Pointer): Pointer; stdcall;
external kernel name 'LocalFree';
function VirtualAlloc(lpAddress: Pointer;
dwSize, flAllocationType, flProtect: DWORD): Pointer; stdcall;
external kernel name 'VirtualAlloc';
function VirtualFree(lpAddress: Pointer; dwSize, dwFreeType: DWORD): BOOL; stdcall;
external kernel name 'VirtualFree';
procedure InitializeCriticalSection(var lpCriticalSection: TRTLCriticalSection); stdcall;
external kernel name 'InitializeCriticalSection';
procedure EnterCriticalSection(var lpCriticalSection: TRTLCriticalSection); stdcall;
external kernel name 'EnterCriticalSection';
procedure LeaveCriticalSection(var lpCriticalSection: TRTLCriticalSection); stdcall;
external kernel name 'LeaveCriticalSection';
procedure DeleteCriticalSection(var lpCriticalSection: TRTLCriticalSection); stdcall;
external kernel name 'DeleteCriticalSection';
// Common Data structure:
type
TBlock = record
addr: PChar;
size: Integer;
end;
// Heap error codes
const
cHeapOk = 0; // everything's fine
cReleaseErr = 1; // operating system returned an error when we released
cDecommitErr = 2; // operating system returned an error when we decommited
cBadCommittedList = 3; // list of committed blocks looks bad
cBadFiller1 = 4; // filler block is bad
cBadFiller2 = 5; // filler block is bad
cBadFiller3 = 6; // filler block is bad
cBadCurAlloc = 7; // current allocation zone is bad
cCantInit = 8; // couldn't initialize
cBadUsedBlock = 9; // used block looks bad
cBadPrevBlock = 10; // prev block before a used block is bad
cBadNextBlock = 11; // next block after a used block is bad
cBadFreeList = 12; // free list is bad
cBadFreeBlock = 13; // free block is bad
cBadBalance = 14; // free list doesn't correspond to blocks marked free
var
initialized : Boolean;
heapErrorCode : Integer;
heapLock : TRTLCriticalSection;
//
// Helper module: administrating block descriptors.
//
type
PBlockDesc = ^TBlockDesc;
TBlockDesc = record
next: PBlockDesc;
prev: PBlockDesc;
addr: PChar;
size: Integer;
end;
type
PBlockDescBlock = ^TBlockDescBlock;
TBlockDescBlock = record
next: PBlockDescBlock;
data: array [0..99] of TBlockDesc;
end;
var
blockDescBlockList: PBlockDescBlock;
blockDescFreeList : PBlockDesc;
function GetBlockDesc: PBlockDesc;
// Get a block descriptor.
// Will return nil for failure.
var
bd: PBlockDesc;
bdb: PBlockDescBlock;
i: Integer;
begin
if blockDescFreeList = nil then begin
bdb := LocalAlloc(LMEM_FIXED, sizeof(bdb^));
if bdb = nil then begin
result := nil;
exit;
end;
bdb.next := blockDescBlockList;
blockDescBlockList := bdb;
for i := low(bdb.data) to high(bdb.data) do begin
bd := @bdb.data[i];
bd.next := blockDescFreeList;
blockDescFreeList := bd;
end;
end;
bd := blockDescFreeList;
blockDescFreeList := bd.next;
result := bd;
end;
procedure MakeEmpty(bd: PBlockDesc);
begin
bd.next := bd;
bd.prev := bd;
end;
function AddBlockAfter(prev: PBlockDesc; const b: TBlock): Boolean;
var
next, bd: PBlockDesc;
begin
bd := GetBlockDesc;
if bd = nil then
result := False
else begin
bd.addr := b.addr;
bd.size := b.size;
next := prev.next;
bd.next := next;
bd.prev := prev;
next.prev := bd;
prev.next := bd;
result := True;
end;
end;
procedure DeleteBlock(bd: PBlockDesc);
var
prev, next: PBlockDesc;
begin
prev := bd.prev;
next := bd.next;
prev.next := next;
next.prev := prev;
bd.next := blockDescFreeList;
blockDescFreeList := bd;
end;
function MergeBlockAfter(prev: PBlockDesc; const b: TBlock) : TBlock;
var
bd, bdNext: PBlockDesc;
begin
bd := prev.next;
result := b;
repeat
bdNext := bd.next;
if bd.addr + bd.size = result.addr then begin
DeleteBlock(bd);
result.addr := bd.addr;
inc(result.size, bd.size);
end else if result.addr + result.size = bd.addr then begin
DeleteBlock(bd);
inc(result.size, bd.size);
end;
bd := bdNext;
until bd = prev;
if not AddBlockAfter(prev, result) then
result.addr := nil;
end;
function RemoveBlock(bd: PBlockDesc; const b: TBlock): Boolean;
var
n: TBlock;
start: PBlockDesc;
begin
start := bd;
repeat
if (bd.addr <= b.addr) and (bd.addr + bd.size >= b.addr + b.size) then begin
if bd.addr = b.addr then begin
Inc(bd.addr, b.size);
Dec(bd.size, b.size);
if bd.size = 0 then
DeleteBlock(bd);
end else if bd.addr + bd.size = b.addr + b.size then
Dec(bd.size, b.size)
else begin
n.addr := b.addr + b.size;
n.size := bd.addr + bd.size - n.addr;
bd.size := b.addr - bd.addr;
if not AddBlockAfter(bd, n) then begin
result := False;
exit;
end;
end;
result := True;
exit;
end;
bd := bd.next;
until bd = start;
result := False;
end;
//
// Address space administration:
//
const
cSpaceAlign = 64*1024;
cSpaceMin = 1024*1024;
cPageAlign = 4*1024;
var
spaceRoot: TBlockDesc;
function GetSpace(minSize: Integer): TBlock;
// Get at least minSize bytes address space.
// Success: returns a block, possibly much bigger than requested.
// Will not fail - will raise an exception or terminate program.
begin
if minSize < cSpaceMin then
minSize := cSpaceMin
else
minSize := (minSize + (cSpaceAlign-1)) and not (cSpaceAlign-1);
result.size := minSize;
result.addr := VirtualAlloc(nil, minSize, MEM_RESERVE, PAGE_NOACCESS);
if result.addr = nil then
exit;
if not AddBlockAfter(@spaceRoot, result) then begin
VirtualFree(result.addr, 0, MEM_RELEASE);
result.addr := nil;
exit;
end;
end;
function GetSpaceAt(addr: PChar; minSize: Integer): TBlock;
// Get at least minSize bytes address space at addr.
// Return values as above.
// Failure: returns block with addr = nil.
begin
result.size := cSpaceMin;
result.addr := VirtualAlloc(addr, cSpaceMin, MEM_RESERVE, PAGE_READWRITE);
if result.addr = nil then begin
minSize := (minSize + (cSpaceAlign-1)) and not (cSpaceAlign-1);
result.size := minSize;
result.addr := VirtualAlloc(addr, minSize, MEM_RESERVE, PAGE_READWRITE);
end;
if result.addr <> nil then begin
if not AddBlockAfter(@spaceRoot, result) then begin
VirtualFree(result.addr, 0, MEM_RELEASE);
result.addr := nil;
end;
end;
end;
function FreeSpace(addr: Pointer; maxSize: Integer): TBlock;
// Free at most maxSize bytes of address space at addr.
// Returns the block that was actually freed.
var
bd, bdNext: PBlockDesc;
minAddr, maxAddr, startAddr, endAddr: PChar;
begin
minAddr := PChar($FFFFFFFF);
maxAddr := nil;
startAddr := addr;
endAddr := startAddr + maxSize;
bd := spaceRoot.next;
while bd <> @spaceRoot do begin
bdNext := bd.next;
if (bd.addr >= startAddr) and (bd.addr + bd.size <= endAddr) then begin
if minAddr > bd.addr then
minAddr := bd.addr;
if maxAddr < bd.addr + bd.size then
maxAddr := bd.addr + bd.size;
if not VirtualFree(bd.addr, 0, MEM_RELEASE) then
heapErrorCode := cReleaseErr;
DeleteBlock(bd);
end;
bd := bdNext;
end;
result.addr := nil;
if maxAddr <> nil then begin
result.addr := minAddr;
result.size := maxAddr - minAddr;
end;
end;
function Commit(addr: Pointer; minSize: Integer): TBlock;
// Commits memory.
// Returns the block that was actually committed.
// Will return a block with addr = nil on failure.
var
bd: PBlockDesc;
loAddr, hiAddr, startAddr, endAddr: PChar;
begin
startAddr := PChar(Integer(addr) and not (cPageAlign-1));
endAddr := PChar(((Integer(addr) + minSize) + (cPageAlign-1)) and not (cPageAlign-1));
result.addr := startAddr;
result.size := endAddr - startAddr;
bd := spaceRoot.next;
while bd <> @spaceRoot do begin
// Commit the intersection of the block described by bd and [startAddr..endAddr)
loAddr := bd.addr;
hiAddr := loAddr + bd.size;
if loAddr < startAddr then
loAddr := startAddr;
if hiAddr > endAddr then
hiAddr := endAddr;
if loAddr < hiAddr then begin
if VirtualAlloc(loAddr, hiAddr - loAddr, MEM_COMMIT, PAGE_READWRITE) = nil then begin
result.addr := nil;
exit;
end;
end;
bd := bd.next;
end;
end;
function Decommit(addr: Pointer; maxSize: Integer): TBlock;
// Decommits address space.
// Returns the block that was actually decommitted.
var
bd: PBlockDesc;
loAddr, hiAddr, startAddr, endAddr: PChar;
begin
startAddr := PChar((Integer(addr) + + (cPageAlign-1)) and not (cPageAlign-1));
endAddr := PChar((Integer(addr) + maxSize) and not (cPageAlign-1));
result.addr := startAddr;
result.size := endAddr - startAddr;
bd := spaceRoot.next;
while bd <> @spaceRoot do begin
// Decommit the intersection of the block described by bd and [startAddr..endAddr)
loAddr := bd.addr;
hiAddr := loAddr + bd.size;
if loAddr < startAddr then
loAddr := startAddr;
if hiAddr > endAddr then
hiAddr := endAddr;
if loAddr < hiAddr then begin
if not VirtualFree(loAddr, hiAddr - loAddr, MEM_DECOMMIT) then
heapErrorCode := cDecommitErr;
end;
bd := bd.next;
end;
end;
//
// Committed space administration
//
const
cCommitAlign = 16*1024;
var
decommittedRoot: TBlockDesc;
function GetCommitted(minSize: Integer): TBlock;
// Get a block of committed memory.
// Returns a committed memory block, possibly much bigger than requested.
// Will return a block with a nil addr on failure.
var
bd: PBlockDesc;
begin
minSize := (minSize + (cCommitAlign-1)) and not (cCommitAlign-1);
repeat
bd := decommittedRoot.next;
while bd <> @decommittedRoot do begin
if bd.size >= minSize then begin
result := Commit(bd.addr, minSize);
if result.addr = nil then
exit;
Inc(bd.addr, result.size);
Dec(bd.size, result.size);
if bd.size = 0 then
DeleteBlock(bd);
exit;
end;
bd := bd.next;
end;
result := GetSpace(minSize);
if result.addr = nil then
exit;
if MergeBlockAfter(@decommittedRoot, result).addr = nil then begin
FreeSpace(result.addr, result.size);
result.addr := nil;
exit;
end;
until False;
end;
function GetCommittedAt(addr: PChar; minSize: Integer): TBlock;
// Get at least minSize bytes committed space at addr.
// Success: returns a block, possibly much bigger than requested.
// Failure: returns a block with addr = nil.
var
bd: PBlockDesc;
b: TBlock;
begin
minSize := (minSize + (cCommitAlign-1)) and not (cCommitAlign-1);
repeat
bd := decommittedRoot.next;
while (bd <> @decommittedRoot) and (bd.addr <> addr) do
bd := bd.next;
if bd.addr = addr then begin
if bd.size >= minSize then
break;
b := GetSpaceAt(bd.addr + bd.size, minSize - bd.size);
if b.addr <> nil then begin
if MergeBlockAfter(@decommittedRoot, b).addr <> nil then
continue
else begin
FreeSpace(b.addr, b.size);
result.addr := nil;
exit;
end;
end;
end;
b := GetSpaceAt(addr, minSize);
if b.addr = nil then
break;
if MergeBlockAfter(@decommittedRoot, b).addr = nil then begin
FreeSpace(b.addr, b.size);
result.addr := nil;
exit;
end;
until false;
if (bd.addr = addr) and (bd.size >= minSize) then begin
result := Commit(bd.addr, minSize);
if result.addr = nil then
exit;
Inc(bd.addr, result.size);
Dec(bd.size, result.size);
if bd.size = 0 then
DeleteBlock(bd);
end else
result.addr := nil;
end;
function FreeCommitted(addr: PChar; maxSize: Integer): TBlock;
// Free at most maxSize bytes of address space at addr.
// Returns the block that was actually freed.
var
startAddr, endAddr: PChar;
b: TBlock;
begin
startAddr := PChar(Integer(addr + (cCommitAlign-1)) and not (cCommitAlign-1));
endAddr := PChar(Integer(addr + maxSize) and not (cCommitAlign-1));
if endAddr > startAddr then begin
result := Decommit(startAddr, endAddr - startAddr);
b := MergeBlockAfter(@decommittedRoot, result);
if b.addr <> nil then
b := FreeSpace(b.addr, b.size);
if b.addr <> nil then
RemoveBlock(@decommittedRoot, b);
end else
result.addr := nil;
end;
//
// Suballocator (what the user program actually calls)
//
type
PFree = ^TFree;
TFree = record
prev: PFree;
next: PFree;
size: Integer;
end;
PUsed = ^TUsed;
TUsed = record
sizeFlags: Integer;
end;
const
cAlign = 4;
cThisUsedFlag = 2;
cPrevFreeFlag = 1;
cFillerFlag = $80000000;
cFlags = cThisUsedFlag or cPrevFreeFlag or cFillerFlag;
cSmallSize = 4*1024;
cDecommitMin = 15*1024;
type
TSmallTab = array [sizeof(TFree) div cAlign .. cSmallSize div cAlign] of PFree;
VAR
avail : TFree;
rover : PFree;
remBytes : Integer;
curAlloc : PChar;
smallTab : ^TSmallTab;
committedRoot: TBlockDesc;
function InitAllocator: Boolean;
// Initialize. No other calls legal before that.
var
i: Integer;
a: PFree;
begin
try
InitializeCriticalSection(heapLock);
if IsMultiThread then EnterCriticalSection(heapLock);
MakeEmpty(@spaceRoot);
MakeEmpty(@decommittedRoot);
MakeEmpty(@committedRoot);
smallTab := LocalAlloc(LMEM_FIXED, sizeof(smallTab^));
if smallTab <> nil then begin
for i:= low(smallTab^) to high(smallTab^) do
smallTab[i] := nil;
a := @avail;
a.next := a;
a.prev := a;
rover := a;
initialized := True;
end;
finally
if IsMultiThread then LeaveCriticalSection(heapLock);
end;
result := initialized;
end;
procedure UninitAllocator;
// Shutdown.
var
bdb: PBlockDescBlock;
bd : PBlockDesc;
begin
if initialized then begin
try
if IsMultiThread then EnterCriticalSection(heapLock);
initialized := False;
LocalFree(smallTab);
smallTab := nil;
bd := spaceRoot.next;
while bd <> @spaceRoot do begin
VirtualFree(bd.addr, 0, MEM_RELEASE);
bd := bd.next;
end;
MakeEmpty(@spaceRoot);
MakeEmpty(@decommittedRoot);
MakeEmpty(@committedRoot);
bdb := blockDescBlockList;
while bdb <> nil do begin
blockDescBlockList := bdb^.next;
LocalFree(bdb);
bdb := blockDescBlockList;
end;
finally
if IsMultiThread then LeaveCriticalSection(heapLock);
DeleteCriticalSection(heapLock);
end;
end;
end;
procedure DeleteFree(f: PFree);
var
n, p: PFree;
size: Cardinal;
begin
if rover = f then
rover := f.next;
n := f.next;
size := f.size;
if size <= cSmallSize then begin
if n = f then
smallTab[size div cAlign] := nil
else begin
smallTab[size div cAlign] := n;
p := f.prev;
n.prev := p;
p.next := n;
end;
end else begin
p := f.prev;
n.prev := p;
p.next := n;
end;
end;
procedure InsertFree(a: Pointer; size: Cardinal); forward;
function FindCommitted(addr: PChar): PBlockDesc;
begin
result := committedRoot.next;
while result <> @committedRoot do begin
if (addr >= result.addr) and (addr < result.addr + result.size) then
exit;
result := result.next;
end;
heapErrorCode := cBadCommittedList;
result := nil;
end;
procedure FillBeforeGap(a: PChar; size: Cardinal);
var
rest: Cardinal;
e: PUsed;
begin
rest := size - sizeof(TUsed);
e := PUsed(a + rest);
if size >= sizeof(TFree) + sizeof(TUsed) then begin
e.sizeFlags := sizeof(TUsed) or cThisUsedFlag or cPrevFreeFlag or cFillerFlag;
InsertFree(a, rest);
end else if size >= sizeof(TUsed) then begin
PUsed(a).sizeFlags := size or (cThisUsedFlag or cFillerFlag);
e.sizeFlags := size or (cThisUsedFlag or cFillerFlag);
end;
end;
procedure InternalFreeMem(a: PChar);
begin
Inc(AllocMemCount);
Inc(AllocMemSize,PUsed(a-sizeof(TUsed)).sizeFlags and not cFlags - sizeof(TUsed));
SysFreeMem(a);
end;
procedure FillAfterGap(a: PChar; size: Cardinal);
begin
if size >= sizeof(TFree) then begin
PUsed(a).sizeFlags := size or cThisUsedFlag;
InternalFreeMem(a + sizeof(TUsed));
end else begin
if size >= sizeof(TUsed) then
PUsed(a).sizeFlags := size or (cThisUsedFlag or cFillerFlag);
Inc(a,size);
PUsed(a).sizeFlags := PUsed(a).sizeFlags and not cPrevFreeFlag;
end;
end;
function FillerSizeBeforeGap(a: PChar): Cardinal;
var
sizeFlags : Cardinal;
freeSize : Cardinal;
f : PFree;
begin
sizeFlags := PUsed(a - sizeof(TUsed)).sizeFlags;
if (sizeFlags and (cThisUsedFlag or cFillerFlag)) <> (cThisUsedFlag or cFillerFlag) then
heapErrorCode := cBadFiller1;
result := sizeFlags and not cFlags;
Dec(a, result);
if ((PUsed(a).sizeFlags xor sizeFlags) and not cPrevFreeFlag) <> 0 then
HeapErrorCode := cBadFiller2;
if (PUsed(a).sizeFlags and cPrevFreeFlag) <> 0 then begin
freeSize := PFree(a - sizeof(TFree)).size;
f := PFree(a - freeSize);
if f.size <> freeSize then
heapErrorCode := cBadFiller3;
DeleteFree(f);
Inc(result, freeSize);
end;
end;
function FillerSizeAfterGap(a: PChar): Cardinal;
var
sizeFlags: Cardinal;
f : PFree;
begin
result := 0;
sizeFlags := PUsed(a).sizeFlags;
if (sizeFlags and cFillerFlag) <> 0 then begin
sizeFlags := sizeFlags and not cFlags;
Inc(result,sizeFlags);
Inc(a, sizeFlags);
sizeFlags := PUsed(a).sizeFlags;
end;
if (sizeFlags and cThisUsedFlag) = 0 then begin
f := PFree(a);
DeleteFree(f);
Inc(result, f.size);
Inc(a, f.size);
PUsed(a).sizeFlags := PUsed(a).sizeFlags and not cPrevFreeFlag;
end;
end;
function DecommitFree(a: PChar; size: Cardinal): Boolean;
var
b: TBlock;
bd: PBlockDesc;
begin
bd := FindCommitted(a);
if bd.addr + bd.size - (a + size) <= sizeof(TFree) then
size := bd.addr + bd.size - a;
if a - bd.addr < sizeof(TFree) then
b := FreeCommitted(bd.addr, size + (a - bd.addr))
else
b := FreeCommitted(a + sizeof(TUsed), size - sizeof(TUsed));
if b.addr = nil then
result := False
else begin
FillBeforeGap(a, b.addr - a);
if bd.addr + bd.size > b.addr + b.size then
FillAfterGap(b.addr + b.size, a + size - (b.addr + b.size));
RemoveBlock(bd,b);
result := True;
end;
end;
procedure InsertFree(a: Pointer; size: Cardinal);
var
f, n, p: PFree;
begin
f := PFree(a);
f.size := size;
PFree(PChar(f) + size - sizeof(TFree)).size := size;
if size <= cSmallSize then begin
n := smallTab[size div cAlign];
if n = nil then begin
smallTab[size div cAlign] := f;
f.next := f;
f.prev := f;
end else begin
p := n.prev;
f.next := n;
f.prev := p;
n.prev := f;
p.next := f;
end;
end else if (size < cDecommitMin) or not DecommitFree(a, size) then begin
n := rover;
rover := f;
p := n.prev;
f.next := n;
f.prev := p;
n.prev := f;
p.next := f;
end;
end;
procedure FreeCurAlloc;
begin
if remBytes > 0 then begin
if remBytes < sizeof(TFree) then
heapErrorCode := cBadCurAlloc
else begin
PUsed(curAlloc).sizeFlags := remBytes or cThisUsedFlag;
InternalFreeMem(curAlloc + sizeof(TUsed));
curAlloc := nil;
remBytes := 0;
end;
end;
end;
function MergeCommit(b: TBlock): Boolean;
var
merged: TBlock;
fSize: Cardinal;
begin
FreeCurAlloc;
merged := MergeBlockAfter(@committedRoot, b);
if merged.addr = nil then begin
result := False;
exit;
end;
if merged.addr < b.addr then begin
fSize := FillerSizeBeforeGap(b.addr);
Dec(b.addr, fSize);
Inc(b.size, fSize);
end;
if merged.addr + merged.size > b.addr + b.size then begin
fSize := FillerSizeAfterGap(b.addr + b.size);
Inc(b.size, fSize);
end;
if merged.addr + merged.size = b.addr + b.size then begin
FillBeforeGap(b.addr + b.size - sizeof(TUsed), sizeof(TUsed));
Dec(b.size, sizeof(TUsed));
end;
curAlloc := b.addr;
remBytes := b.size;
result := True;
end;
function NewCommit(minSize: Cardinal): Boolean;
var
b: TBlock;
begin
b := GetCommitted(minSize+sizeof(TUsed));
result := (b.addr <> nil) and MergeCommit(b);
end;
function NewCommitAt(addr: Pointer; minSize: Cardinal): Boolean;
var
b: TBlock;
begin
b := GetCommittedAt(addr, minSize+sizeof(TUsed));
result := (b.addr <> nil) and MergeCommit(b);
end;
function SearchSmallBlocks(size: Cardinal): PFree;
var
i: Cardinal;
begin
result := nil;
for i := size div cAlign to High(smallTab^) do begin
result := smallTab[i];
if result <> nil then
exit;
end;
end;
function TryHarder(size: Cardinal): Pointer;
var
u: PUsed; f:PFree; saveSize, rest: Cardinal;
begin
repeat
f := avail.next;
if (size <= f.size) then
break;
f := rover;
if f.size >= size then
break;
saveSize := f.size;
f.size := size;
repeat
f := f.next
until f.size >= size;
rover.size := saveSize;
if f <> rover then begin
rover := f;
break;
end;
if size <= cSmallSize then begin
f := SearchSmallBlocks(size);
if f <> nil then
break;
end;
if not NewCommit(size) then begin
result := nil;
exit;
end;
if remBytes >= size then begin
Dec(remBytes, size);
if remBytes < sizeof(TFree) then begin
Inc(size, remBytes);
remBytes := 0;
end;
u := PUsed(curAlloc);
Inc(curAlloc, size);
u.sizeFlags := size or cThisUsedFlag;
result := PChar(u) + sizeof(TUsed);
Inc(AllocMemCount);
Inc(AllocMemSize,size - sizeof(TUsed));
exit;
end;
until False;
DeleteFree(f);
rest := f.size - size;
if rest >= sizeof(TFree) then begin
InsertFree(PChar(f) + size, rest);
end else begin
size := f.size;
if f = rover then
rover := f.next;
u := PUsed(PChar(f) + size);
u.sizeFlags := u.sizeFlags and not cPrevFreeFlag;
end;
u := PUsed(f);
u.sizeFlags := size or cThisUsedFlag;
result := PChar(u) + sizeof(TUsed);
Inc(AllocMemCount);
Inc(AllocMemSize,size - sizeof(TUsed));
end;
function SysGetMem(size: Integer): Pointer;
// Allocate memory block.
var
f, prev, next: PFree;
u: PUsed;
begin
if not initialized and not InitAllocator then begin
result := nil;
exit;
end;
try
if IsMultiThread then EnterCriticalSection(heapLock);
Inc(size, sizeof(TUsed) + (cAlign-1));
size := size and not (cAlign-1);
if size < sizeof(TFree) then
size := sizeof(TFree);
if size <= cSmallSize then begin
f := smallTab[size div cAlign];
if f <> nil then begin
u := PUsed(PChar(f) + size);
u.sizeFlags := u.sizeFlags and not cPrevFreeFlag;
next := f.next;
if next = f then
smallTab[size div cAlign] := nil
else begin
smallTab[size div cAlign] := next;
prev := f.prev;
prev.next := next;
next.prev := prev;
end;
u := PUsed(f);
u.sizeFlags := f.size or cThisUsedFlag;
result := PChar(u) + sizeof(TUsed);
Inc(AllocMemCount);
Inc(AllocMemSize,size - sizeof(TUsed));
exit;
end;
end;
if size <= remBytes then begin
Dec(remBytes, size);
if remBytes < sizeof(TFree) then begin
Inc(size, remBytes);
remBytes := 0;
end;
u := PUsed(curAlloc);
Inc(curAlloc, size);
u.sizeFlags := size or cThisUsedFlag;
result := PChar(u) + sizeof(TUsed);
Inc(AllocMemCount);
Inc(AllocMemSize,size - sizeof(TUsed));
exit;
end;
result := TryHarder(size);
finally
if IsMultiThread then LeaveCriticalSection(heapLock);
end;
end;
function SysFreeMem(p: Pointer): Integer;
// Deallocate memory block.
label
abort;
var
u, n : PUsed;
f : PFree;
prevSize, nextSize, size : Cardinal;
begin
heapErrorCode := cHeapOk;
if not initialized and not InitAllocator then begin
heapErrorCode := cCantInit;
result := cCantInit;
exit;
end;
try
if IsMultiThread then EnterCriticalSection(heapLock);
u := p;
u := PUsed(PChar(u) - sizeof(TUsed));
size := u.sizeFlags;
if (size and cThisUsedFlag) = 0 then begin
heapErrorCode := cBadUsedBlock;
goto abort;
end;
Dec(AllocMemCount);
Dec(AllocMemSize,size and not cFlags - sizeof(TUsed));
if (size and cPrevFreeFlag) <> 0 then begin
prevSize := PFree(PChar(u)-sizeof(TFree)).size;
if (prevSize < sizeof(TFree)) or ((prevSize and cFlags) <> 0) then begin
heapErrorCode := cBadPrevBlock;
goto abort;
end;
f := PFree(PChar(u) - prevSize);
if f^.size <> prevSize then begin
heapErrorCode := cBadPrevBlock;
goto abort;
end;
inc(size, prevSize);
u := PUsed(f);
DeleteFree(f);
end;
size := size and not cFlags;
n := PUsed(PChar(u) + size);
if PChar(n) = curAlloc then begin
dec(curAlloc, size);
inc(remBytes, size);
if remBytes > cDecommitMin then
FreeCurAlloc;
result := cHeapOk;
exit;
end;
if (n.sizeFlags and cThisUsedFlag) <> 0 then begin
if (n.sizeFlags and not cFlags) < sizeof(TUsed) then begin
heapErrorCode := cBadNextBlock;
goto abort;
end;
n.sizeFlags := n.sizeFlags or cPrevFreeFlag
end else begin
f := PFree(n);
if (f.next = nil) or (f.prev = nil) or (f.size < sizeof(TFree)) then begin
heapErrorCode := cBadNextBlock;
goto abort;
end;
nextSize := f.size;
inc(size,nextSize);
DeleteFree(f);
end;
InsertFree(u, size);
abort:
result := heapErrorCode;
finally
if IsMultiThread then LeaveCriticalSection(heapLock);
end;
end;
function ResizeInPlace(p: Pointer; newSize: Cardinal): Boolean;
var u, n: PUsed; f: PFree; oldSize, blkSize, neededSize: Cardinal;
begin
Inc(newSize, sizeof(TUsed)+cAlign-1);
newSize := newSize and not (cAlign-1);
if newSize < sizeof(TFree) then
newSize := sizeof(TFree);
u := PUsed(PChar(p) - sizeof(TUsed));
oldSize := u.sizeFlags and not cFlags;
n := PUsed( PChar(u) + oldSize );
if newSize <= oldSize then begin
blkSize := oldSize - newSize;
if PChar(n) = curAlloc then begin
Dec(curAlloc, blkSize);
Inc(remBytes, blkSize);
if remBytes < sizeof(TFree) then begin
Inc(curAlloc, blkSize);
Dec(remBytes, blkSize);
newSize := oldSize;
end;
end else begin
n := PUsed(PChar(u) + oldSize);
if n.sizeFlags and cThisUsedFlag = 0 then begin
f := PFree(n);
Inc(blkSize, f.size);
DeleteFree(f);
end;
if blkSize >= sizeof(TFree) then begin
n := PUsed(PChar(u) + newSize);
n.sizeFlags := blkSize or cThisUsedFlag;
InternalFreeMem(PChar(n) + sizeof(TUsed));
end else
newSize := oldSize;
end;
end else begin
repeat
neededSize := newSize - oldSize;
if PChar(n) = curAlloc then begin
if remBytes >= neededSize then begin
Dec(remBytes, neededSize);
Inc(curAlloc, neededSize);
if remBytes < sizeof(TFree) then begin
Inc(curAlloc, remBytes);
Inc(newSize, remBytes);
remBytes := 0;
end;
Inc(AllocMemSize, newSize - oldSize);
u.sizeFlags := newSize or u.sizeFlags and cFlags;
result := true;
exit;
end else begin
FreeCurAlloc;
n := PUsed( PChar(u) + oldSize );
end;
end;
if n.sizeFlags and cThisUsedFlag = 0 then begin
f := PFree(n);
blkSize := f.size;
if blkSize < neededSize then begin
n := PUsed(PChar(n) + blkSize);
Dec(neededSize, blkSize);
end else begin
DeleteFree(f);
Dec(blkSize, neededSize);
if blkSize >= sizeof(TFree) then
InsertFree(PChar(u) + newSize, blkSize)
else begin
Inc(newSize, blkSize);
n := PUsed(PChar(u) + newSize);
n.sizeFlags := n.sizeFlags and not cPrevFreeFlag;
end;
break;
end;
end;
if n.sizeFlags and cFillerFlag <> 0 then begin
n := PUsed(PChar(n) + n.sizeFlags and not cFlags);
if NewCommitAt(n, neededSize) then begin
n := PUsed( PChar(u) + oldSize );
continue;
end;
end;
result := False;
exit;
until False;
end;
Inc(AllocMemSize, newSize - oldSize);
u.sizeFlags := newSize or u.sizeFlags and cFlags;
result := True;
end;
function SysReallocMem(p: Pointer; size: Integer): Pointer;
// Resize memory block.
var
n: Pointer; oldSize: Cardinal;
begin
if not initialized and not InitAllocator then begin
result := nil;
exit;
end;
try
if IsMultiThread then EnterCriticalSection(heapLock);
if ResizeInPlace(p, size) then
result := p
else begin
n := SysGetMem(size);
oldSize := (PUsed(PChar(p)-sizeof(PUsed)).sizeFlags and not cFlags) - sizeof(TUsed);
if oldSize > size then
oldSize := size;
if n <> nil then begin
Move(p^, n^, oldSize);
SysFreeMem(p);
end;
result := n;
end;
finally
if IsMultiThread then LeaveCriticalSection(heapLock);
end;
end;
function BlockSum(root: PBlockDesc): Cardinal;
var
b : PBlockDesc;
begin
result := 0;
b := root.next;
while b <> root do begin
Inc(result, b.size);
b := b.next;
end;
end;
function GetHeapStatus: THeapStatus;
var
size, freeSize, userSize: Cardinal;
f: PFree;
a, e: PChar;
i: Integer;
b: PBlockDesc;
prevFree: Boolean;
begin
heapErrorCode := cHeapOk;
result.TotalAddrSpace := 0;
result.TotalUncommitted := 0;
result.TotalCommitted := 0;
result.TotalAllocated := 0;
result.TotalFree := 0;
result.FreeSmall := 0;
result.FreeBig := 0;
result.Unused := 0;
result.Overhead := 0;
result.HeapErrorCode := cHeapOk;
if not initialized then exit;
try
if IsMultiThread then EnterCriticalSection(heapLock);
result.totalAddrSpace := BlockSum(@spaceRoot);
result.totalUncommitted := BlockSum(@decommittedRoot);
result.totalCommitted := BlockSum(@committedRoot);
size := 0;
for i := Low(smallTab^) to High(smallTab^) do begin
f := smallTab[i];
if f <> nil then begin
repeat
Inc(size, f.size);
if (f.prev.next <> f) or (f.next.prev <> f) or (f.size < sizeof(TFree)) then begin
heapErrorCode := cBadFreeList;
break;
end;
f := f.next;
until f = smallTab[i];
end;
end;
result.freeSmall := size;
size := 0;
f := avail.next;
while f <> @avail do begin
if (f.prev.next <> f) or (f.next.prev <> f) or (f.size < sizeof(TFree)) then begin
heapErrorCode := cBadFreeList;
break;
end;
Inc(size, f.size);
f := f.next;
end;
result.freeBig := size;
result.unused := remBytes;
result.totalFree := result.freeSmall + result.freeBig + result.unused;
freeSize := 0;
userSize := 0;
result.overhead := 0;
b := committedRoot.next;
prevFree := False;
while b <> @committedRoot do begin
a := b.addr;
e := a + b.size;
while a < e do begin
if (a = curAlloc) and (remBytes > 0) then begin
size := remBytes;
Inc(freeSize, size);
if prevFree then
heapErrorCode := cBadCurAlloc;
prevFree := False;
end else begin
if prevFree <> ((PUsed(a).sizeFlags and cPrevFreeFlag) <> 0) then
heapErrorCode := cBadNextBlock;
if (PUsed(a).sizeFlags and cThisUsedFlag) = 0 then begin
f := PFree(a);
if (f.prev.next <> f) or (f.next.prev <> f) or (f.size < sizeof(TFree)) then
heapErrorCode := cBadFreeBlock;
size := f.size;
Inc(freeSize, size);
prevFree := True;
end else begin
size := PUsed(a).sizeFlags and not cFlags;
if (PUsed(a).sizeFlags and cFillerFlag) <> 0 then begin
Inc(result.overhead, size);
if (a > b.addr) and (a + size < e) then
heapErrorCode := cBadUsedBlock;
end else begin
Inc(userSize, size-sizeof(TUsed));
Inc(result.overhead, sizeof(TUsed));
end;
prevFree := False;
end;
end;
Inc(a, size);
end;
b := b.next;
end;
if result.totalFree <> freeSize then
heapErrorCode := cBadBalance;
result.totalAllocated := userSize;
result.heapErrorCode := heapErrorCode;
finally
if IsMultiThread then LeaveCriticalSection(heapLock);
end;
end;