home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / Runimage / Delphi50 / Source / Rtl / Sys / GETMEM.INC < prev    next >
Encoding:
Text File  |  1999-08-11  |  38.9 KB  |  1,527 lines

  1. // Three layers:
  2. // - Address space administration
  3. // - Committed space administration
  4. // - Suballocator
  5. //
  6. // Helper module: administrating block descriptors
  7. //
  8.  
  9.  
  10. //
  11. // Operating system interface
  12. //
  13. const
  14.   LMEM_FIXED = 0;
  15.   LMEM_ZEROINIT = $40;
  16.  
  17.   MEM_COMMIT   = $1000;
  18.   MEM_RESERVE  = $2000;
  19.   MEM_DECOMMIT = $4000;
  20.   MEM_RELEASE  = $8000;
  21.  
  22.   PAGE_NOACCESS  = 1;
  23.   PAGE_READWRITE = 4;
  24.  
  25. type
  26.   DWORD = Integer;
  27.   BOOL  = LongBool;
  28.  
  29.   TRTLCriticalSection = packed record
  30.     DebugInfo: Pointer;
  31.     LockCount: Longint;
  32.     RecursionCount: Longint;
  33.     OwningThread: Integer;
  34.     LockSemaphore: Integer;
  35.     Reserved: DWORD;
  36.   end;
  37.  
  38. function LocalAlloc(flags, size: Integer): Pointer; stdcall;
  39.   external kernel name 'LocalAlloc';
  40. function LocalFree(addr: Pointer): Pointer; stdcall;
  41.   external kernel name 'LocalFree';
  42.  
  43. function VirtualAlloc(lpAddress: Pointer;
  44.   dwSize, flAllocationType, flProtect: DWORD): Pointer; stdcall;
  45.   external kernel name 'VirtualAlloc';
  46. function VirtualFree(lpAddress: Pointer; dwSize, dwFreeType: DWORD): BOOL; stdcall;
  47.   external kernel name 'VirtualFree';
  48.  
  49. procedure InitializeCriticalSection(var lpCriticalSection: TRTLCriticalSection); stdcall;
  50.   external kernel name 'InitializeCriticalSection';
  51. procedure EnterCriticalSection(var lpCriticalSection: TRTLCriticalSection); stdcall;
  52.   external kernel name 'EnterCriticalSection';
  53. procedure LeaveCriticalSection(var lpCriticalSection: TRTLCriticalSection); stdcall;
  54.   external kernel name 'LeaveCriticalSection';
  55. procedure DeleteCriticalSection(var lpCriticalSection: TRTLCriticalSection); stdcall;
  56.   external kernel name 'DeleteCriticalSection';
  57.  
  58. // Common Data structure:
  59.  
  60. type
  61.   TBlock = packed record
  62.     addr: PChar;
  63.     size: Integer;
  64.   end;
  65.  
  66. // Heap error codes
  67.  
  68. const
  69.   cHeapOk           = 0;          // everything's fine
  70.   cReleaseErr       = 1;          // operating system returned an error when we released
  71.   cDecommitErr      = 2;          // operating system returned an error when we decommited
  72.   cBadCommittedList = 3;          // list of committed blocks looks bad
  73.   cBadFiller1       = 4;          // filler block is bad
  74.   cBadFiller2       = 5;          // filler block is bad
  75.   cBadFiller3       = 6;          // filler block is bad
  76.   cBadCurAlloc      = 7;          // current allocation zone is bad
  77.   cCantInit         = 8;          // couldn't initialize
  78.   cBadUsedBlock     = 9;          // used block looks bad
  79.   cBadPrevBlock     = 10;         // prev block before a used block is bad
  80.   cBadNextBlock     = 11;         // next block after a used block is bad
  81.   cBadFreeList      = 12;         // free list is bad
  82.   cBadFreeBlock     = 13;         // free block is bad
  83.   cBadBalance       = 14;         // free list doesn't correspond to blocks marked free
  84.  
  85. var
  86.   initialized   : Boolean;
  87.   heapErrorCode : Integer;
  88.   heapLock      : TRTLCriticalSection;
  89.  
  90. //
  91. // Helper module: administrating block descriptors.
  92. //
  93. type
  94.   PBlockDesc = ^TBlockDesc;
  95.   TBlockDesc = packed record
  96.     next: PBlockDesc;
  97.     prev: PBlockDesc;
  98.     addr: PChar;
  99.     size: Integer;
  100.   end;
  101.  
  102. type
  103.   PBlockDescBlock = ^TBlockDescBlock;
  104.   TBlockDescBlock = packed record
  105.     next: PBlockDescBlock;
  106.     data: array [0..99] of TBlockDesc;
  107.   end;
  108.  
  109. var
  110.   blockDescBlockList: PBlockDescBlock;
  111.   blockDescFreeList : PBlockDesc;
  112.  
  113.  
  114. function GetBlockDesc: PBlockDesc;
  115. // Get a block descriptor.
  116. // Will return nil for failure.
  117. var
  118.   bd:  PBlockDesc;
  119.   bdb: PBlockDescBlock;
  120.   i:   Integer;
  121. begin
  122.   if blockDescFreeList = nil then begin
  123.     bdb := LocalAlloc(LMEM_FIXED, sizeof(bdb^));
  124.     if bdb = nil then begin
  125.       result := nil;
  126.       exit;
  127.     end;
  128.     bdb.next := blockDescBlockList;
  129.     blockDescBlockList := bdb;
  130.     for i := low(bdb.data) to high(bdb.data) do begin
  131.       bd := @bdb.data[i];
  132.       bd.next := blockDescFreeList;
  133.       blockDescFreeList := bd;
  134.     end;
  135.   end;
  136.   bd := blockDescFreeList;
  137.   blockDescFreeList := bd.next;
  138.   result := bd;
  139. end;
  140.  
  141.  
  142. procedure MakeEmpty(bd: PBlockDesc);
  143. begin
  144.   bd.next := bd;
  145.   bd.prev := bd;
  146. end;
  147.  
  148.  
  149. function AddBlockAfter(prev: PBlockDesc; const b: TBlock): Boolean;
  150. var
  151.   next, bd: PBlockDesc;
  152. begin
  153.   bd := GetBlockDesc;
  154.   if bd = nil then
  155.     result := False
  156.   else begin
  157.     bd.addr := b.addr;
  158.     bd.size := b.size;
  159.  
  160.     next := prev.next;
  161.     bd.next := next;
  162.     bd.prev := prev;
  163.     next.prev := bd;
  164.     prev.next := bd;
  165.  
  166.     result := True;
  167.   end;
  168. end;
  169.  
  170.  
  171. procedure DeleteBlock(bd: PBlockDesc);
  172. var
  173.   prev, next: PBlockDesc;
  174. begin
  175.   prev := bd.prev;
  176.   next := bd.next;
  177.   prev.next := next;
  178.   next.prev := prev;
  179.   bd.next := blockDescFreeList;
  180.   blockDescFreeList := bd;
  181. end;
  182.  
  183.  
  184. function MergeBlockAfter(prev: PBlockDesc; const b: TBlock) : TBlock;
  185. var
  186.   bd, bdNext: PBlockDesc;
  187. begin
  188.   bd := prev.next;
  189.   result := b;
  190.   repeat
  191.     bdNext := bd.next;
  192.     if bd.addr + bd.size = result.addr then begin
  193.       DeleteBlock(bd);
  194.       result.addr := bd.addr;
  195.       inc(result.size, bd.size);
  196.     end else if result.addr + result.size = bd.addr then begin
  197.       DeleteBlock(bd);
  198.       inc(result.size, bd.size);
  199.     end;
  200.     bd := bdNext;
  201.   until bd = prev;
  202.   if not AddBlockAfter(prev, result) then
  203.     result.addr := nil;
  204. end;
  205.  
  206.  
  207. function RemoveBlock(bd: PBlockDesc; const b: TBlock): Boolean;
  208. var
  209.   n: TBlock;
  210.   start: PBlockDesc;
  211. begin
  212.   start := bd;
  213.   repeat
  214.     if (bd.addr <= b.addr) and (bd.addr + bd.size >= b.addr + b.size) then begin
  215.       if bd.addr = b.addr then begin
  216.         Inc(bd.addr, b.size);
  217.         Dec(bd.size, b.size);
  218.         if bd.size = 0 then
  219.           DeleteBlock(bd);
  220.       end else if bd.addr + bd.size = b.addr + b.size then
  221.         Dec(bd.size, b.size)
  222.       else begin
  223.         n.addr := b.addr + b.size;
  224.         n.size := bd.addr + bd.size - n.addr;
  225.         bd.size := b.addr - bd.addr;
  226.         if not AddBlockAfter(bd, n) then begin
  227.           result := False;
  228.           exit;
  229.         end;
  230.       end;
  231.       result := True;
  232.       exit;
  233.     end;
  234.     bd := bd.next;
  235.   until bd = start;
  236.   result := False;
  237. end;
  238.  
  239.  
  240.  
  241. //
  242. // Address space administration:
  243. //
  244.  
  245. const
  246.   cSpaceAlign = 64*1024;
  247.   cSpaceMin   = 1024*1024;
  248.   cPageAlign  = 4*1024;
  249.  
  250. var
  251.   spaceRoot: TBlockDesc;
  252.  
  253.  
  254. function GetSpace(minSize: Integer): TBlock;
  255. // Get at least minSize bytes address space.
  256. // Success: returns a block, possibly much bigger than requested.
  257. // Will not fail - will raise an exception or terminate program.
  258. begin
  259.   if minSize < cSpaceMin then
  260.     minSize := cSpaceMin
  261.   else
  262.     minSize := (minSize + (cSpaceAlign-1)) and not (cSpaceAlign-1);
  263.  
  264.   result.size := minSize;
  265.   result.addr := VirtualAlloc(nil, minSize, MEM_RESERVE, PAGE_NOACCESS);
  266.   if result.addr = nil then
  267.     exit;
  268.  
  269.   if not AddBlockAfter(@spaceRoot, result) then begin
  270.     VirtualFree(result.addr, 0, MEM_RELEASE);
  271.     result.addr := nil;
  272.     exit;
  273.   end;
  274. end;
  275.  
  276.  
  277. function GetSpaceAt(addr: PChar; minSize: Integer): TBlock;
  278. // Get at least minSize bytes address space at addr.
  279. // Return values as above.
  280. // Failure: returns block with addr = nil.
  281. begin
  282.   result.size := cSpaceMin;
  283.   result.addr := VirtualAlloc(addr, cSpaceMin, MEM_RESERVE, PAGE_READWRITE);
  284.   if result.addr = nil then begin
  285.     minSize := (minSize + (cSpaceAlign-1)) and not (cSpaceAlign-1);
  286.     result.size := minSize;
  287.     result.addr := VirtualAlloc(addr, minSize, MEM_RESERVE, PAGE_READWRITE);
  288.   end;
  289.   if result.addr <> nil then begin
  290.     if not AddBlockAfter(@spaceRoot, result) then begin
  291.       VirtualFree(result.addr, 0, MEM_RELEASE);
  292.       result.addr := nil;
  293.     end;
  294.   end;
  295. end;
  296.  
  297.  
  298. function FreeSpace(addr: Pointer; maxSize: Integer): TBlock;
  299. // Free at most maxSize bytes of address space at addr.
  300. // Returns the block that was actually freed.
  301. var
  302.   bd, bdNext: PBlockDesc;
  303.   minAddr, maxAddr, startAddr, endAddr: PChar;
  304. begin
  305.   minAddr := PChar($FFFFFFFF);
  306.   maxAddr := nil;
  307.   startAddr := addr;
  308.   endAddr   := startAddr + maxSize;
  309.   bd := spaceRoot.next;
  310.   while bd <> @spaceRoot do begin
  311.     bdNext := bd.next;
  312.     if (bd.addr >= startAddr) and (bd.addr + bd.size <= endAddr) then begin
  313.       if minAddr > bd.addr then
  314.         minAddr := bd.addr;
  315.       if maxAddr < bd.addr + bd.size then
  316.         maxAddr := bd.addr + bd.size;
  317.       if not VirtualFree(bd.addr, 0, MEM_RELEASE) then
  318.         heapErrorCode := cReleaseErr;
  319.       DeleteBlock(bd);
  320.     end;
  321.     bd := bdNext;
  322.   end;
  323.   result.addr := nil;
  324.   if maxAddr <> nil then begin
  325.     result.addr := minAddr;
  326.     result.size := maxAddr - minAddr;
  327.   end;
  328. end;
  329.  
  330.  
  331. function Commit(addr: Pointer; minSize: Integer): TBlock;
  332. // Commits memory.
  333. // Returns the block that was actually committed.
  334. // Will return a block with addr = nil on failure.
  335. var
  336.   bd: PBlockDesc;
  337.   loAddr, hiAddr, startAddr, endAddr: PChar;
  338. begin
  339.   startAddr := PChar(Integer(addr) and not (cPageAlign-1));
  340.   endAddr := PChar(((Integer(addr) + minSize) + (cPageAlign-1)) and not (cPageAlign-1));
  341.   result.addr := startAddr;
  342.   result.size := endAddr - startAddr;
  343.   bd := spaceRoot.next;
  344.   while bd <> @spaceRoot do begin
  345.     // Commit the intersection of the block described by bd and [startAddr..endAddr)
  346.     loAddr := bd.addr;
  347.     hiAddr := loAddr + bd.size;
  348.     if loAddr < startAddr then
  349.       loAddr := startAddr;
  350.     if hiAddr > endAddr then
  351.       hiAddr := endAddr;
  352.     if loAddr < hiAddr then begin
  353.       if VirtualAlloc(loAddr, hiAddr - loAddr, MEM_COMMIT, PAGE_READWRITE) = nil then begin
  354.         result.addr := nil;
  355.         exit;
  356.       end;
  357.     end;
  358.     bd := bd.next;
  359.   end;
  360. end;
  361.  
  362.  
  363. function Decommit(addr: Pointer; maxSize: Integer): TBlock;
  364. // Decommits address space.
  365. // Returns the block that was actually decommitted.
  366. var
  367.   bd: PBlockDesc;
  368.   loAddr, hiAddr, startAddr, endAddr: PChar;
  369. begin
  370.   startAddr := PChar((Integer(addr) + + (cPageAlign-1)) and not (cPageAlign-1));
  371.   endAddr := PChar((Integer(addr) + maxSize) and not (cPageAlign-1));
  372.   result.addr := startAddr;
  373.   result.size := endAddr - startAddr;
  374.   bd := spaceRoot.next;
  375.   while bd <> @spaceRoot do begin
  376.     // Decommit the intersection of the block described by bd and [startAddr..endAddr)
  377.     loAddr := bd.addr;
  378.     hiAddr := loAddr + bd.size;
  379.     if loAddr < startAddr then
  380.       loAddr := startAddr;
  381.     if hiAddr > endAddr then
  382.       hiAddr := endAddr;
  383.     if loAddr < hiAddr then begin
  384.       if not VirtualFree(loAddr, hiAddr - loAddr, MEM_DECOMMIT) then
  385.         heapErrorCode := cDecommitErr;
  386.     end;
  387.     bd := bd.next;
  388.   end;
  389. end;
  390.  
  391.  
  392. //
  393. // Committed space administration
  394. //
  395. const
  396.   cCommitAlign = 16*1024;
  397.  
  398. var
  399.   decommittedRoot: TBlockDesc;
  400.  
  401.  
  402. function GetCommitted(minSize: Integer): TBlock;
  403. // Get a block of committed memory.
  404. // Returns a committed memory block, possibly much bigger than requested.
  405. // Will return a block with a nil addr on failure.
  406. var
  407.   bd: PBlockDesc;
  408. begin
  409.   minSize := (minSize + (cCommitAlign-1)) and not (cCommitAlign-1);
  410.   repeat
  411.     bd := decommittedRoot.next;
  412.     while bd <> @decommittedRoot do begin
  413.       if bd.size >= minSize then begin
  414.         result := Commit(bd.addr, minSize);
  415.         if result.addr = nil then
  416.           exit;
  417.         Inc(bd.addr, result.size);
  418.         Dec(bd.size, result.size);
  419.         if bd.size = 0 then
  420.           DeleteBlock(bd);
  421.         exit;
  422.       end;
  423.       bd := bd.next;
  424.     end;
  425.     result := GetSpace(minSize);
  426.     if result.addr = nil then
  427.       exit;
  428.     if MergeBlockAfter(@decommittedRoot, result).addr = nil then begin
  429.       FreeSpace(result.addr, result.size);
  430.       result.addr := nil;
  431.       exit;
  432.     end;
  433.   until False;
  434. end;
  435.  
  436.  
  437. function GetCommittedAt(addr: PChar; minSize: Integer): TBlock;
  438. // Get at least minSize bytes committed space at addr.
  439. // Success: returns a block, possibly much bigger than requested.
  440. // Failure: returns a block with addr = nil.
  441. var
  442.   bd: PBlockDesc;
  443.   b: TBlock;
  444. begin
  445.   minSize := (minSize + (cCommitAlign-1)) and not (cCommitAlign-1);
  446.   repeat
  447.  
  448.     bd := decommittedRoot.next;
  449.     while (bd <> @decommittedRoot) and (bd.addr <> addr) do
  450.       bd := bd.next;
  451.  
  452.     if bd.addr = addr then begin
  453.       if bd.size >= minSize then
  454.         break;
  455.       b := GetSpaceAt(bd.addr + bd.size, minSize - bd.size);
  456.       if b.addr <> nil then begin
  457.         if MergeBlockAfter(@decommittedRoot, b).addr <> nil then
  458.           continue
  459.         else begin
  460.           FreeSpace(b.addr, b.size);
  461.           result.addr := nil;
  462.           exit;
  463.         end;
  464.       end;
  465.     end;
  466.  
  467.     b := GetSpaceAt(addr, minSize);
  468.     if b.addr = nil then
  469.       break;
  470.  
  471.     if MergeBlockAfter(@decommittedRoot, b).addr = nil then begin
  472.       FreeSpace(b.addr, b.size);
  473.       result.addr := nil;
  474.       exit;
  475.     end;
  476.   until false;
  477.  
  478.   if (bd.addr = addr) and (bd.size >= minSize) then begin
  479.     result := Commit(bd.addr, minSize);
  480.     if result.addr = nil then
  481.       exit;
  482.     Inc(bd.addr, result.size);
  483.     Dec(bd.size, result.size);
  484.     if bd.size = 0 then
  485.       DeleteBlock(bd);
  486.   end else
  487.     result.addr := nil;
  488. end;
  489.  
  490.  
  491. function FreeCommitted(addr: PChar; maxSize: Integer): TBlock;
  492. // Free at most maxSize bytes of address space at addr.
  493. // Returns the block that was actually freed.
  494. var
  495.   startAddr, endAddr: PChar;
  496.   b: TBlock;
  497. begin
  498.   startAddr := PChar(Integer(addr + (cCommitAlign-1)) and not (cCommitAlign-1));
  499.   endAddr := PChar(Integer(addr + maxSize) and not (cCommitAlign-1));
  500.   if endAddr > startAddr then begin
  501.     result := Decommit(startAddr, endAddr - startAddr);
  502.     b := MergeBlockAfter(@decommittedRoot, result);
  503.     if b.addr <> nil then
  504.       b := FreeSpace(b.addr, b.size);
  505.     if b.addr <> nil then
  506.       RemoveBlock(@decommittedRoot, b);
  507.   end else
  508.     result.addr := nil;
  509. end;
  510.  
  511.  
  512. //
  513. // Suballocator (what the user program actually calls)
  514. //
  515.  
  516. type
  517.   PFree = ^TFree;
  518.   TFree = packed record
  519.     prev: PFree;
  520.     next: PFree;
  521.     size: Integer;
  522.   end;
  523.   PUsed = ^TUsed;
  524.   TUsed = packed record
  525.     sizeFlags: Integer;
  526.   end;
  527.  
  528. const
  529.   cAlign        = 4;
  530.   cThisUsedFlag = 2;
  531.   cPrevFreeFlag = 1;
  532.   cFillerFlag   = Integer($80000000);
  533.   cFlags        = cThisUsedFlag or cPrevFreeFlag or cFillerFlag;
  534.   cSmallSize    = 4*1024;
  535.   cDecommitMin  = 15*1024;
  536.  
  537. type
  538.   TSmallTab    = array [sizeof(TFree) div cAlign .. cSmallSize div cAlign] of PFree;
  539.  
  540. VAR
  541.   avail        : TFree;
  542.   rover        : PFree;
  543.   remBytes     : Integer;
  544.   curAlloc     : PChar;
  545.   smallTab     : ^TSmallTab;
  546.   committedRoot: TBlockDesc;
  547.  
  548.  
  549. function InitAllocator: Boolean;
  550. // Initialize. No other calls legal before that.
  551. var
  552.   i: Integer;
  553.   a: PFree;
  554. begin
  555.   try
  556.     InitializeCriticalSection(heapLock);
  557.     if IsMultiThread then EnterCriticalSection(heapLock);
  558.  
  559.     MakeEmpty(@spaceRoot);
  560.     MakeEmpty(@decommittedRoot);
  561.     MakeEmpty(@committedRoot);
  562.  
  563.     smallTab := LocalAlloc(LMEM_FIXED, sizeof(smallTab^));
  564.     if smallTab <> nil then begin
  565.       for i:= low(smallTab^) to high(smallTab^) do
  566.         smallTab[i] := nil;
  567.  
  568.       a := @avail;
  569.       a.next := a;
  570.       a.prev := a;
  571.       rover := a;
  572.  
  573.       initialized := True;
  574.     end;
  575.   finally
  576.     if IsMultiThread then LeaveCriticalSection(heapLock);
  577.   end;
  578.   result := initialized;
  579. end;
  580.  
  581.  
  582. procedure UninitAllocator;
  583. // Shutdown.
  584. var
  585.   bdb: PBlockDescBlock;
  586.   bd : PBlockDesc;
  587. begin
  588.   if initialized then begin
  589.     try
  590.       if IsMultiThread then EnterCriticalSection(heapLock);
  591.  
  592.       initialized := False;
  593.  
  594.       LocalFree(smallTab);
  595.       smallTab := nil;
  596.  
  597.       bd := spaceRoot.next;
  598.       while bd <> @spaceRoot do begin
  599.         VirtualFree(bd.addr, 0, MEM_RELEASE);
  600.         bd := bd.next;
  601.       end;
  602.  
  603.       MakeEmpty(@spaceRoot);
  604.       MakeEmpty(@decommittedRoot);
  605.       MakeEmpty(@committedRoot);
  606.  
  607.       bdb := blockDescBlockList;
  608.       while bdb <> nil do begin
  609.         blockDescBlockList := bdb^.next;
  610.         LocalFree(bdb);
  611.         bdb := blockDescBlockList;
  612.       end;
  613.     finally
  614.       if IsMultiThread then LeaveCriticalSection(heapLock);
  615.       DeleteCriticalSection(heapLock);
  616.     end;
  617.   end;
  618. end;
  619.  
  620.  
  621. procedure DeleteFree(f: PFree);
  622. var
  623.   n, p: PFree;
  624.   size: Integer;
  625. begin
  626.   if rover = f then
  627.     rover := f.next;
  628.   n := f.next;
  629.   size := f.size;
  630.   if size <= cSmallSize then begin
  631.     if n = f then
  632.       smallTab[size div cAlign] := nil
  633.     else begin
  634.       smallTab[size div cAlign] := n;
  635.       p := f.prev;
  636.       n.prev := p;
  637.       p.next := n;
  638.     end;
  639.   end else begin
  640.     p := f.prev;
  641.     n.prev := p;
  642.     p.next := n;
  643.   end;
  644. end;
  645.  
  646.  
  647. procedure InsertFree(a: Pointer; size: Integer); forward;
  648.  
  649.  
  650. function FindCommitted(addr: PChar): PBlockDesc;
  651. begin
  652.   result := committedRoot.next;
  653.   while result <> @committedRoot do begin
  654.     if (addr >= result.addr) and (addr < result.addr + result.size) then
  655.       exit;
  656.     result := result.next;
  657.   end;
  658.   heapErrorCode := cBadCommittedList;
  659.   result := nil;
  660. end;
  661.  
  662.  
  663. procedure FillBeforeGap(a: PChar; size: Integer);
  664. var
  665.   rest: Integer;
  666.   e: PUsed;
  667. begin
  668.   rest := size - sizeof(TUsed);
  669.   e := PUsed(a + rest);
  670.   if size >= sizeof(TFree) + sizeof(TUsed) then begin
  671.     e.sizeFlags := sizeof(TUsed) or cThisUsedFlag or cPrevFreeFlag or cFillerFlag;
  672.     InsertFree(a, rest);
  673.   end else if size >= sizeof(TUsed) then begin
  674.     PUsed(a).sizeFlags := size or (cThisUsedFlag or cFillerFlag);
  675.     e.sizeFlags := size or (cThisUsedFlag or cFillerFlag);
  676.   end;
  677. end;
  678.  
  679.  
  680. procedure InternalFreeMem(a: PChar);
  681. begin
  682.   Inc(AllocMemCount);
  683.   Inc(AllocMemSize,PUsed(a-sizeof(TUsed)).sizeFlags and not cFlags - sizeof(TUsed));
  684.   SysFreeMem(a);
  685. end;
  686.  
  687.  
  688. procedure FillAfterGap(a: PChar; size: Integer);
  689. begin
  690.   if size >= sizeof(TFree) then begin
  691.     PUsed(a).sizeFlags := size or cThisUsedFlag;
  692.     InternalFreeMem(a + sizeof(TUsed));
  693.   end else begin
  694.     if size >= sizeof(TUsed) then
  695.       PUsed(a).sizeFlags := size or (cThisUsedFlag or cFillerFlag);
  696.     Inc(a,size);
  697.     PUsed(a).sizeFlags := PUsed(a).sizeFlags and not cPrevFreeFlag;
  698.   end;
  699. end;
  700.  
  701.  
  702. function FillerSizeBeforeGap(a: PChar): Integer;
  703. var
  704.   sizeFlags : Integer;
  705.   freeSize  : Integer;
  706.   f : PFree;
  707. begin
  708.   sizeFlags := PUsed(a - sizeof(TUsed)).sizeFlags;
  709.   if (sizeFlags and (cThisUsedFlag or cFillerFlag)) <> (cThisUsedFlag or cFillerFlag) then
  710.     heapErrorCode := cBadFiller1;
  711.   result := sizeFlags and not cFlags;
  712.   Dec(a, result);
  713.   if ((PUsed(a).sizeFlags xor sizeFlags) and not cPrevFreeFlag) <> 0 then
  714.     HeapErrorCode := cBadFiller2;
  715.   if (PUsed(a).sizeFlags and cPrevFreeFlag) <> 0 then begin
  716.     freeSize := PFree(a - sizeof(TFree)).size;
  717.     f := PFree(a - freeSize);
  718.     if f.size <> freeSize then
  719.       heapErrorCode := cBadFiller3;
  720.     DeleteFree(f);
  721.     Inc(result, freeSize);
  722.   end;
  723. end;
  724.  
  725.  
  726. function FillerSizeAfterGap(a: PChar): Integer;
  727. var
  728.   sizeFlags: Integer;
  729.   f : PFree;
  730. begin
  731.   result := 0;
  732.   sizeFlags := PUsed(a).sizeFlags;
  733.   if (sizeFlags and cFillerFlag) <> 0 then begin
  734.     sizeFlags := sizeFlags and not cFlags;
  735.     Inc(result,sizeFlags);
  736.     Inc(a, sizeFlags);
  737.     sizeFlags := PUsed(a).sizeFlags;
  738.   end;
  739.   if (sizeFlags and cThisUsedFlag) = 0 then begin
  740.     f := PFree(a);
  741.     DeleteFree(f);
  742.     Inc(result, f.size);
  743.     Inc(a, f.size);
  744.     PUsed(a).sizeFlags := PUsed(a).sizeFlags and not cPrevFreeFlag;
  745.   end;
  746. end;
  747.  
  748.  
  749. function DecommitFree(a: PChar; size: Integer): Boolean;
  750. var
  751.   b: TBlock;
  752.   bd: PBlockDesc;
  753. begin
  754.   bd := FindCommitted(a);
  755.   if bd.addr + bd.size - (a + size) <= sizeof(TFree) then
  756.     size := bd.addr + bd.size - a;
  757.  
  758.   if a - bd.addr < sizeof(TFree) then
  759.     b := FreeCommitted(bd.addr, size + (a - bd.addr))
  760.   else
  761.     b := FreeCommitted(a + sizeof(TUsed), size - sizeof(TUsed));
  762.  
  763.   if b.addr = nil then
  764.     result := False
  765.   else begin
  766.     FillBeforeGap(a, b.addr - a);
  767.     if bd.addr + bd.size > b.addr + b.size then
  768.       FillAfterGap(b.addr + b.size, a + size - (b.addr + b.size));
  769.     RemoveBlock(bd,b);
  770.     result := True;
  771.   end;
  772. end;
  773.  
  774.  
  775. procedure InsertFree(a: Pointer; size: Integer);
  776. var
  777.   f, n, p: PFree;
  778. begin
  779.   f := PFree(a);
  780.   f.size := size;
  781.   PFree(PChar(f) + size - sizeof(TFree)).size := size;
  782.   if size <= cSmallSize then begin
  783.     n := smallTab[size div cAlign];
  784.     if n = nil then begin
  785.       smallTab[size div cAlign] := f;
  786.       f.next := f;
  787.       f.prev := f;
  788.     end else begin
  789.       p := n.prev;
  790.       f.next := n;
  791.       f.prev := p;
  792.       n.prev := f;
  793.       p.next := f;
  794.     end;
  795.   end else if (size < cDecommitMin) or not DecommitFree(a, size) then begin
  796.     n := rover;
  797.     rover := f;
  798.     p := n.prev;
  799.     f.next := n;
  800.     f.prev := p;
  801.     n.prev := f;
  802.     p.next := f;
  803.   end;
  804. end;
  805.  
  806.  
  807. procedure FreeCurAlloc;
  808. begin
  809.   if remBytes > 0 then begin
  810.     if remBytes < sizeof(TFree) then
  811.       heapErrorCode := cBadCurAlloc
  812.     else begin
  813.       PUsed(curAlloc).sizeFlags := remBytes or cThisUsedFlag;
  814.       InternalFreeMem(curAlloc + sizeof(TUsed));
  815.       curAlloc := nil;
  816.       remBytes := 0;
  817.     end;
  818.   end;
  819. end;
  820.  
  821.  
  822. function MergeCommit(b: TBlock): Boolean;
  823. var
  824.   merged: TBlock;
  825.   fSize: Integer;
  826. begin
  827.   FreeCurAlloc;
  828.   merged := MergeBlockAfter(@committedRoot, b);
  829.   if merged.addr = nil then begin
  830.     result := False;
  831.     exit;
  832.   end;
  833.  
  834.   if merged.addr < b.addr then begin
  835.     fSize := FillerSizeBeforeGap(b.addr);
  836.     Dec(b.addr, fSize);
  837.     Inc(b.size, fSize);
  838.   end;
  839.  
  840.   if merged.addr + merged.size > b.addr + b.size then begin
  841.     fSize := FillerSizeAfterGap(b.addr + b.size);
  842.     Inc(b.size, fSize);
  843.   end;
  844.  
  845.   if merged.addr + merged.size = b.addr + b.size then begin
  846.     FillBeforeGap(b.addr + b.size - sizeof(TUsed), sizeof(TUsed));
  847.     Dec(b.size, sizeof(TUsed));
  848.   end;
  849.  
  850.   curAlloc := b.addr;
  851.   remBytes := b.size;
  852.  
  853.   result := True;
  854. end;
  855.  
  856.  
  857. function NewCommit(minSize: Integer): Boolean;
  858. var
  859.   b: TBlock;
  860. begin
  861.   b := GetCommitted(minSize+sizeof(TUsed));
  862.   result := (b.addr <> nil) and MergeCommit(b);
  863. end;
  864.  
  865.  
  866. function NewCommitAt(addr: Pointer; minSize: Integer): Boolean;
  867. var
  868.   b: TBlock;
  869. begin
  870.   b := GetCommittedAt(addr, minSize+sizeof(TUsed));
  871.   result := (b.addr <> nil) and MergeCommit(b);
  872. end;
  873.  
  874.  
  875. function SearchSmallBlocks(size: Integer): PFree;
  876. var
  877.   i: Integer;
  878. begin
  879.   result := nil;
  880.   for i := size div cAlign to High(smallTab^) do begin
  881.     result := smallTab[i];
  882.     if result <> nil then
  883.       exit;
  884.   end;
  885. end;
  886.  
  887.  
  888. function TryHarder(size: Integer): Pointer;
  889. var
  890.   u: PUsed; f:PFree; saveSize, rest: Integer;
  891. begin
  892.  
  893.   repeat
  894.  
  895.     f := avail.next;
  896.     if (size <= f.size) then
  897.       break;
  898.  
  899.     f := rover;
  900.     if f.size >= size then
  901.       break;
  902.  
  903.     saveSize := f.size;
  904.     f.size := size;
  905.     repeat
  906.       f := f.next
  907.     until f.size >= size;
  908.     rover.size := saveSize;
  909.     if f <> rover then begin
  910.       rover := f;
  911.       break;
  912.     end;
  913.  
  914.     if size <= cSmallSize then begin
  915.       f := SearchSmallBlocks(size);
  916.       if f <> nil then
  917.         break;
  918.     end;
  919.  
  920.     if not NewCommit(size) then begin
  921.       result := nil;
  922.       exit;
  923.     end;
  924.  
  925.     if remBytes >= size then begin
  926.       Dec(remBytes, size);
  927.       if remBytes < sizeof(TFree) then begin
  928.         Inc(size, remBytes);
  929.         remBytes := 0;
  930.       end;
  931.       u := PUsed(curAlloc);
  932.       Inc(curAlloc, size);
  933.       u.sizeFlags := size or cThisUsedFlag;
  934.       result := PChar(u) + sizeof(TUsed);
  935.       Inc(AllocMemCount);
  936.       Inc(AllocMemSize,size - sizeof(TUsed));
  937.       exit;
  938.     end;
  939.  
  940.   until False;
  941.  
  942.   DeleteFree(f);
  943.  
  944.   rest := f.size - size;
  945.   if rest >= sizeof(TFree) then begin
  946.     InsertFree(PChar(f) + size, rest);
  947.   end else begin
  948.     size := f.size;
  949.     if f = rover then
  950.       rover := f.next;
  951.     u := PUsed(PChar(f) + size);
  952.     u.sizeFlags := u.sizeFlags and not cPrevFreeFlag;
  953.   end;
  954.  
  955.   u := PUsed(f);
  956.   u.sizeFlags := size or cThisUsedFlag;
  957.  
  958.   result := PChar(u) + sizeof(TUsed);
  959.   Inc(AllocMemCount);
  960.   Inc(AllocMemSize,size - sizeof(TUsed));
  961.  
  962. end;
  963.  
  964.  
  965. function SysGetMem(size: Integer): Pointer;
  966. // Allocate memory block.
  967. var
  968.   f, prev, next: PFree;
  969.   u: PUsed;
  970. begin
  971.  
  972.   if not initialized and not InitAllocator then begin
  973.     result := nil;
  974.     exit;
  975.   end;
  976.  
  977.   try
  978.     if IsMultiThread then EnterCriticalSection(heapLock);
  979.  
  980.     Inc(size, sizeof(TUsed) + (cAlign-1));
  981.     size := size and not (cAlign-1);
  982.     if size < sizeof(TFree) then
  983.       size := sizeof(TFree);
  984.  
  985.     if size <= cSmallSize then begin
  986.       f := smallTab[size div cAlign];
  987.       if f <> nil then begin
  988.         u := PUsed(PChar(f) + size);
  989.         u.sizeFlags := u.sizeFlags and not cPrevFreeFlag;
  990.         next := f.next;
  991.         if next = f then
  992.           smallTab[size div cAlign] := nil
  993.         else begin
  994.           smallTab[size div cAlign] := next;
  995.           prev := f.prev;
  996.           prev.next := next;
  997.           next.prev := prev;
  998.         end;
  999.         u := PUsed(f);
  1000.         u.sizeFlags := f.size or cThisUsedFlag;
  1001.         result := PChar(u) + sizeof(TUsed);
  1002.         Inc(AllocMemCount);
  1003.         Inc(AllocMemSize,size - sizeof(TUsed));
  1004.         exit;
  1005.       end;
  1006.     end;
  1007.  
  1008.     if size <= remBytes then begin
  1009.       Dec(remBytes, size);
  1010.       if remBytes < sizeof(TFree) then begin
  1011.         Inc(size, remBytes);
  1012.         remBytes := 0;
  1013.       end;
  1014.       u := PUsed(curAlloc);
  1015.       Inc(curAlloc, size);
  1016.       u.sizeFlags := size or cThisUsedFlag;
  1017.       result := PChar(u) + sizeof(TUsed);
  1018.       Inc(AllocMemCount);
  1019.       Inc(AllocMemSize,size - sizeof(TUsed));
  1020.       exit;
  1021.     end;
  1022.  
  1023.     result := TryHarder(size);
  1024.  
  1025.   finally
  1026.     if IsMultiThread then LeaveCriticalSection(heapLock);
  1027.   end;
  1028.  
  1029. end;
  1030.  
  1031.  
  1032. function SysFreeMem(p: Pointer): Integer;
  1033. // Deallocate memory block.
  1034. label
  1035.   abort;
  1036. var
  1037.   u, n : PUsed;
  1038.   f : PFree;
  1039.   prevSize, nextSize, size : Integer;
  1040. begin
  1041.   heapErrorCode := cHeapOk;
  1042.  
  1043.   if not initialized and not InitAllocator then begin
  1044.     heapErrorCode := cCantInit;
  1045.     result := cCantInit;
  1046.     exit;
  1047.   end;
  1048.  
  1049.   try
  1050.     if IsMultiThread then EnterCriticalSection(heapLock);
  1051.  
  1052.     u := p;
  1053.     u := PUsed(PChar(u) - sizeof(TUsed)); { inv: u = address of allocated block being freed }
  1054.     size := u.sizeFlags;
  1055.     { inv: size = SET(block size) + [block flags] }
  1056.  
  1057.     { validate that the interpretation of this block as a used block is correct }
  1058.     if (size and cThisUsedFlag) = 0 then begin
  1059.       heapErrorCode := cBadUsedBlock;
  1060.       goto abort;
  1061.     end;
  1062.  
  1063.     { inv: the memory block addressed by 'u' and 'p' is an allocated block }
  1064.  
  1065.     Dec(AllocMemCount);
  1066.     Dec(AllocMemSize,size and not cFlags - sizeof(TUsed));
  1067.  
  1068.     if (size and cPrevFreeFlag) <> 0 then begin
  1069.       { previous block is free, coalesce }
  1070.       prevSize := PFree(PChar(u)-sizeof(TFree)).size;
  1071.       if (prevSize < sizeof(TFree)) or ((prevSize and cFlags) <> 0) then begin
  1072.         heapErrorCode := cBadPrevBlock;
  1073.         goto abort;
  1074.       end;
  1075.  
  1076.       f := PFree(PChar(u) - prevSize);
  1077.       if f^.size <> prevSize then begin
  1078.         heapErrorCode := cBadPrevBlock;
  1079.         goto abort;
  1080.       end;
  1081.  
  1082.       inc(size, prevSize);
  1083.       u := PUsed(f);
  1084.       DeleteFree(f);
  1085.     end;
  1086.  
  1087.     size := size and not cFlags;
  1088.     { inv: size = block size }
  1089.  
  1090.     n := PUsed(PChar(u) + size);
  1091.     { inv: n = block following the block to free }
  1092.  
  1093.     if PChar(n) = curAlloc then begin
  1094.       { inv: u = last block allocated }
  1095.       dec(curAlloc, size);
  1096.       inc(remBytes, size);
  1097.       if remBytes > cDecommitMin then
  1098.         FreeCurAlloc;
  1099.       result := cHeapOk;
  1100.       exit;
  1101.     end;
  1102.  
  1103.     if (n.sizeFlags and cThisUsedFlag) <> 0 then begin
  1104.       { inv: n is a used block }
  1105.       if (n.sizeFlags and not cFlags) < sizeof(TUsed) then begin
  1106.         heapErrorCode := cBadNextBlock;
  1107.         goto abort;
  1108.       end;
  1109.       n.sizeFlags := n.sizeFlags or cPrevFreeFlag
  1110.     end else begin
  1111.       { inv: block u & n are both free; coalesce }
  1112.       f := PFree(n);
  1113.       if (f.next = nil) or (f.prev = nil) or (f.size < sizeof(TFree)) then begin
  1114.         heapErrorCode := cBadNextBlock;
  1115.         goto abort;
  1116.       end;
  1117.       nextSize := f.size;
  1118.       inc(size, nextSize);
  1119.       DeleteFree(f);
  1120.       { inv: last block (which was free) is not on free list }
  1121.     end;
  1122.  
  1123.     InsertFree(u, size);
  1124. abort:
  1125.     result := heapErrorCode;
  1126.   finally
  1127.     if IsMultiThread then LeaveCriticalSection(heapLock);
  1128.   end;
  1129. end;
  1130.  
  1131.  
  1132. function ResizeInPlace(p: Pointer; newSize: Integer): Boolean;
  1133. var u, n: PUsed; f: PFree; oldSize, blkSize, neededSize: Integer;
  1134. begin
  1135.   Inc(newSize, sizeof(TUsed)+cAlign-1);
  1136.   newSize := newSize and not (cAlign-1);
  1137.   if newSize < sizeof(TFree) then
  1138.     newSize := sizeof(TFree);
  1139.   u := PUsed(PChar(p) - sizeof(TUsed));
  1140.   oldSize := u.sizeFlags and not cFlags;
  1141.   n := PUsed( PChar(u) + oldSize );
  1142.   if newSize <= oldSize then begin
  1143.     blkSize := oldSize - newSize;
  1144.     if PChar(n) = curAlloc then begin
  1145.       Dec(curAlloc, blkSize);
  1146.       Inc(remBytes, blkSize);
  1147.       if remBytes < sizeof(TFree) then begin
  1148.         Inc(curAlloc, blkSize);
  1149.         Dec(remBytes, blkSize);
  1150.         newSize := oldSize;
  1151.       end;
  1152.     end else begin
  1153.       n := PUsed(PChar(u) + oldSize);
  1154.       if n.sizeFlags and cThisUsedFlag = 0 then begin
  1155.         f := PFree(n);
  1156.         Inc(blkSize, f.size);
  1157.         DeleteFree(f);
  1158.       end;
  1159.       if blkSize >= sizeof(TFree) then begin
  1160.         n := PUsed(PChar(u) + newSize);
  1161.         n.sizeFlags := blkSize or cThisUsedFlag;
  1162.         InternalFreeMem(PChar(n) + sizeof(TUsed));
  1163.       end else
  1164.         newSize := oldSize;
  1165.     end;
  1166.   end else begin
  1167.     repeat
  1168.       neededSize := newSize - oldSize;
  1169.       if PChar(n) = curAlloc then begin
  1170.         if remBytes >= neededSize then begin
  1171.           Dec(remBytes, neededSize);
  1172.           Inc(curAlloc, neededSize);
  1173.           if remBytes < sizeof(TFree) then begin
  1174.             Inc(curAlloc, remBytes);
  1175.             Inc(newSize, remBytes);
  1176.             remBytes := 0;
  1177.           end;
  1178.           Inc(AllocMemSize, newSize - oldSize);
  1179.           u.sizeFlags := newSize or u.sizeFlags and cFlags;
  1180.           result := true;
  1181.           exit;
  1182.         end else begin
  1183.           FreeCurAlloc;
  1184.           n := PUsed( PChar(u) + oldSize );
  1185.         end;
  1186.       end;
  1187.  
  1188.       if n.sizeFlags and cThisUsedFlag = 0 then begin
  1189.         f := PFree(n);
  1190.         blkSize := f.size;
  1191.         if blkSize < neededSize then begin
  1192.           n := PUsed(PChar(n) + blkSize);
  1193.           Dec(neededSize, blkSize);
  1194.         end else begin
  1195.           DeleteFree(f);
  1196.           Dec(blkSize, neededSize);
  1197.           if blkSize >= sizeof(TFree) then
  1198.             InsertFree(PChar(u) + newSize, blkSize)
  1199.           else begin
  1200.             Inc(newSize, blkSize);
  1201.             n := PUsed(PChar(u) + newSize);
  1202.             n.sizeFlags := n.sizeFlags and not cPrevFreeFlag;
  1203.           end;
  1204.           break;
  1205.         end;
  1206.       end;
  1207.  
  1208.       if n.sizeFlags and cFillerFlag <> 0 then begin
  1209.         n := PUsed(PChar(n) + n.sizeFlags and not cFlags);
  1210.         if NewCommitAt(n, neededSize) then begin
  1211.           n := PUsed( PChar(u) + oldSize );
  1212.           continue;
  1213.         end;
  1214.       end;
  1215.  
  1216.       result := False;
  1217.       exit;
  1218.  
  1219.     until False;
  1220.  
  1221.   end;
  1222.  
  1223.   Inc(AllocMemSize, newSize - oldSize);
  1224.   u.sizeFlags := newSize or u.sizeFlags and cFlags;
  1225.   result := True;
  1226.  
  1227. end;
  1228.  
  1229.  
  1230. function SysReallocMem(p: Pointer; size: Integer): Pointer;
  1231. // Resize memory block.
  1232. var
  1233.   n: Pointer; oldSize: Integer;
  1234. begin
  1235.  
  1236.   if not initialized and not InitAllocator then begin
  1237.     result := nil;
  1238.     exit;
  1239.   end;
  1240.  
  1241.   try
  1242.     if IsMultiThread then EnterCriticalSection(heapLock);
  1243.  
  1244.     if ResizeInPlace(p, size) then
  1245.       result := p
  1246.     else begin
  1247.       n := SysGetMem(size);
  1248.       oldSize := (PUsed(PChar(p)-sizeof(PUsed)).sizeFlags and not cFlags) - sizeof(TUsed);
  1249.       if oldSize > size then
  1250.         oldSize := size;
  1251.       if n <> nil then begin
  1252.         Move(p^, n^, oldSize);
  1253.         SysFreeMem(p);
  1254.       end;
  1255.       result := n;
  1256.     end;
  1257.   finally
  1258.     if IsMultiThread then LeaveCriticalSection(heapLock);
  1259.   end;
  1260.  
  1261. end;
  1262.  
  1263.  
  1264. function BlockSum(root: PBlockDesc): Integer;
  1265. var
  1266.   b : PBlockDesc;
  1267. begin
  1268.   result := 0;
  1269.   b := root.next;
  1270.   while b <> root do begin
  1271.     Inc(result, b.size);
  1272.     b := b.next;
  1273.   end;
  1274. end;
  1275.  
  1276.  
  1277. function GetHeapStatus: THeapStatus;
  1278. var
  1279.   size, freeSize, userSize: Cardinal;
  1280.   f: PFree;
  1281.   a, e: PChar;
  1282.   i: Integer;
  1283.   b: PBlockDesc;
  1284.   prevFree: Boolean;
  1285. begin
  1286.  
  1287.   heapErrorCode := cHeapOk;
  1288.  
  1289.   result.TotalAddrSpace   := 0;
  1290.   result.TotalUncommitted := 0;
  1291.   result.TotalCommitted   := 0;
  1292.   result.TotalAllocated   := 0;
  1293.   result.TotalFree        := 0;
  1294.   result.FreeSmall        := 0;
  1295.   result.FreeBig          := 0;
  1296.   result.Unused           := 0;
  1297.   result.Overhead         := 0;
  1298.   result.HeapErrorCode    := cHeapOk;
  1299.  
  1300.   if not initialized then exit;
  1301.  
  1302.   try
  1303.     if IsMultiThread then EnterCriticalSection(heapLock);
  1304.  
  1305.     result.totalAddrSpace   := BlockSum(@spaceRoot);
  1306.     result.totalUncommitted := BlockSum(@decommittedRoot);
  1307.     result.totalCommitted   := BlockSum(@committedRoot);
  1308.  
  1309.     size := 0;
  1310.     for i := Low(smallTab^) to High(smallTab^) do begin
  1311.       f := smallTab[i];
  1312.       if f <> nil then begin
  1313.         repeat
  1314.           Inc(size, f.size);
  1315.           if (f.prev.next <> f) or (f.next.prev <> f) or (f.size < sizeof(TFree)) then begin
  1316.             heapErrorCode := cBadFreeList;
  1317.             break;
  1318.           end;
  1319.           f := f.next;
  1320.         until f = smallTab[i];
  1321.       end;
  1322.     end;
  1323.     result.freeSmall := size;
  1324.  
  1325.     size := 0;
  1326.     f := avail.next;
  1327.     while f <> @avail do begin
  1328.       if (f.prev.next <> f) or (f.next.prev <> f) or (f.size < sizeof(TFree)) then begin
  1329.         heapErrorCode := cBadFreeList;
  1330.         break;
  1331.       end;
  1332.       Inc(size, f.size);
  1333.       f := f.next;
  1334.     end;
  1335.     result.freeBig := size;
  1336.  
  1337.     result.unused := remBytes;
  1338.     result.totalFree := result.freeSmall + result.freeBig + result.unused;
  1339.  
  1340.     freeSize := 0;
  1341.     userSize := 0;
  1342.     result.overhead := 0;
  1343.  
  1344.     b := committedRoot.next;
  1345.     prevFree := False;
  1346.     while b <> @committedRoot do begin
  1347.       a := b.addr;
  1348.       e := a + b.size;
  1349.       while a < e do begin
  1350.         if (a = curAlloc) and (remBytes > 0) then begin
  1351.           size := remBytes;
  1352.           Inc(freeSize, size);
  1353.           if prevFree then
  1354.             heapErrorCode := cBadCurAlloc;
  1355.           prevFree := False;
  1356.         end else begin
  1357.           if prevFree <> ((PUsed(a).sizeFlags and cPrevFreeFlag) <> 0) then
  1358.             heapErrorCode := cBadNextBlock;
  1359.           if (PUsed(a).sizeFlags and cThisUsedFlag) = 0 then begin
  1360.             f := PFree(a);
  1361.             if (f.prev.next <> f) or (f.next.prev <> f) or (f.size < sizeof(TFree)) then
  1362.               heapErrorCode := cBadFreeBlock;
  1363.             size := f.size;
  1364.             Inc(freeSize, size);
  1365.             prevFree := True;
  1366.           end else begin
  1367.             size := PUsed(a).sizeFlags and not cFlags;
  1368.             if (PUsed(a).sizeFlags and cFillerFlag) <> 0 then begin
  1369.               Inc(result.overhead, size);
  1370.               if (a > b.addr) and (a + size < e) then
  1371.                 heapErrorCode := cBadUsedBlock;
  1372.           end else begin
  1373.             Inc(userSize, size-sizeof(TUsed));
  1374.             Inc(result.overhead, sizeof(TUsed));
  1375.           end;
  1376.           prevFree := False;
  1377.         end;
  1378.       end;
  1379.       Inc(a, size);
  1380.       end;
  1381.       b := b.next;
  1382.     end;
  1383.     if result.totalFree <> freeSize then
  1384.       heapErrorCode := cBadBalance;
  1385.  
  1386.     result.totalAllocated := userSize;
  1387.     result.heapErrorCode := heapErrorCode;
  1388.   finally
  1389.     if IsMultiThread then LeaveCriticalSection(heapLock);
  1390.   end;
  1391. end;
  1392.  
  1393.  
  1394. //  this section goes into GetMem.Inc
  1395.  
  1396. {$IFDEF DEBUG_FUNCTIONS}
  1397. type
  1398.   THeapReportProc = procedure(HeapBlock: Pointer; AllocatedSize: Integer) of object;
  1399.  
  1400.  
  1401. procedure WalkHeap(HeapReportProc: THeapReportProc);
  1402. var
  1403.   size : Cardinal;
  1404.   f: PFree;
  1405.   a, e: PChar;
  1406.   b: PBlockDesc;
  1407. begin
  1408.  
  1409.   if not initialized then exit;
  1410.  
  1411.   try
  1412.     if IsMultiThread then EnterCriticalSection(heapLock);
  1413.  
  1414.     b := committedRoot.next;
  1415.     while b <> @committedRoot do begin
  1416.       a := b.addr;
  1417.       e := a + b.size;
  1418.       while a < e do begin
  1419.         if (a = curAlloc) and (remBytes > 0) then begin
  1420.           size := remBytes;
  1421.         end else begin
  1422.           if (PUsed(a).sizeFlags and cThisUsedFlag) = 0 then begin
  1423.             f := PFree(a);
  1424.             size := f.size;
  1425.           end else begin
  1426.             size := PUsed(a).sizeFlags and not cFlags;
  1427.             if (PUsed(a).sizeFlags and cFillerFlag) = 0 then begin
  1428.               HeapReportProc(a + sizeof(TUsed), size - sizeof(TUsed));
  1429.             end;
  1430.           end;
  1431.         end;
  1432.         Inc(a, size);
  1433.       end;
  1434.       b := b.next;
  1435.     end;
  1436.   finally
  1437.     if IsMultiThread then LeaveCriticalSection(heapLock);
  1438.   end;
  1439. end;
  1440.  
  1441. type
  1442.   THeapBlockCollector = class(TObject)
  1443.     FCount: Integer;
  1444.     FObjectTable: TObjectArray;
  1445.     FHeapBlockTable: THeapBlockArray;
  1446.     FClass: TClass;
  1447.     FFindDerived: Boolean;
  1448.     procedure CollectBlocks(HeapBlock: Pointer; AllocatedSize: Integer);
  1449.     procedure CollectObjects(HeapBlock: Pointer; AllocatedSize: Integer);
  1450.   end;
  1451.  
  1452.  
  1453. procedure THeapBlockCollector.CollectBlocks(HeapBlock: Pointer; AllocatedSize: Integer);
  1454. begin
  1455.   if FCount < Length(FHeapBlockTable) then
  1456.   begin
  1457.     FHeapBlockTable[FCount].Start := HeapBlock;
  1458.     FHeapBlockTable[FCount].Size  := AllocatedSize;
  1459.   end;
  1460.   Inc(FCount);
  1461. end;
  1462.  
  1463.  
  1464. procedure THeapBlockCollector.CollectObjects(HeapBlock: Pointer; AllocatedSize: Integer);
  1465. var
  1466.   AObject: TObject;
  1467.   AClass: TClass;
  1468. type
  1469.   PPointer = ^Pointer;
  1470. begin
  1471.   try
  1472.     if AllocatedSize < 4 then
  1473.       Exit;
  1474.     AObject := TObject(HeapBlock);
  1475.     AClass := AObject.ClassType;
  1476.     if (AClass = FClass)
  1477.       or (FFindDerived
  1478.         and (Integer(AClass) >= 64*1024)
  1479.         and (PPointer(PChar(AClass) + vmtSelfPtr)^ = Pointer(AClass))
  1480.         and (AObject is FClass)) then
  1481.     begin
  1482.       if FCount < Length(FObjectTable) then
  1483.         FObjectTable[FCount] := AObject;
  1484.       Inc(FCount);
  1485.     end;
  1486.   except
  1487.   //  Let's not worry about this block - it's obviously not a valid object
  1488.   end;
  1489. end;
  1490.  
  1491. var
  1492.   HeapBlockCollector: THeapBlockCollector;
  1493.  
  1494. function GetHeapBlocks: THeapBlockArray;
  1495. begin
  1496.   if not Assigned(HeapBlockCollector) then
  1497.     HeapBlockCollector := THeapBlockCollector.Create;
  1498.  
  1499.   Walkheap(HeapBlockCollector.CollectBlocks);
  1500.   SetLength(HeapBlockCollector.FHeapBlockTable, HeapBlockCollector.FCount);
  1501.   HeapBlockCollector.FCount := 0;
  1502.   Walkheap(HeapBlockCollector.CollectBlocks);
  1503.   Result := HeapBlockCollector.FHeapBlockTable;
  1504.   HeapBlockCollector.FCount := 0;
  1505.   HeapBlockCollector.FHeapBlockTable := nil;
  1506. end;
  1507.  
  1508.  
  1509. function FindObjects(AClass: TClass; FindDerived: Boolean): TObjectArray;
  1510. begin
  1511.   if not Assigned(HeapBlockCollector) then
  1512.     HeapBlockCollector := THeapBlockCollector.Create;
  1513.   HeapBlockCollector.FClass := AClass;
  1514.   HeapBlockCollector.FFindDerived := FindDerived;
  1515.  
  1516.   Walkheap(HeapBlockCollector.CollectObjects);
  1517.   SetLength(HeapBlockCollector.FObjectTable, HeapBlockCollector.FCount);
  1518.   HeapBlockCollector.FCount := 0;
  1519.   Walkheap(HeapBlockCollector.CollectObjects);
  1520.   Result := HeapBlockCollector.FObjectTable;
  1521.   HeapBlockCollector.FCount := 0;
  1522.   HeapBlockCollector.FObjectTable := nil;
  1523. end;
  1524. {$ENDIF}
  1525.  
  1526.  
  1527.