heap.inc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1993-99 by the Free Pascal development team.
  5. functions for heap management in the data segment
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {****************************************************************************}
  13. { Reuse bigger blocks instead of allocating a new block at freelist/heapptr.
  14. the tried bigger blocks are always multiple sizes of the current block }
  15. {$define REUSEBIGGER}
  16. { Allocate small blocks at heapptr instead of walking the freelist }
  17. {$define SMALLATHEAPPTR}
  18. { Try to find the best matching block in general freelist }
  19. {$define BESTMATCH}
  20. { DEBUG: Dump info when the heap needs to grow }
  21. { define DUMPGROW}
  22. { Default heap settings }
  23. const
  24. blocksize = 16; { at least size of freerecord }
  25. blockshr = 4; { shr value for blocksize=2^blockshr}
  26. maxblocksize = 512+blocksize; { 1024+8 needed for heaprecord }
  27. maxblock = maxblocksize div blocksize;
  28. maxreusebigger = 8; { max reuse bigger tries }
  29. usedmask = $80000000;
  30. sizemask = not usedmask;
  31. {****************************************************************************}
  32. {$ifdef DUMPGROW}
  33. {$define DUMPBLOCKS}
  34. {$endif}
  35. { Memory manager }
  36. const
  37. MemoryManager: TMemoryManager = (
  38. GetMem: SysGetMem;
  39. FreeMem: SysFreeMem;
  40. FreeMemSize: SysFreeMemSize;
  41. MemSize: SysMemSize
  42. );
  43. type
  44. ppfreerecord = ^pfreerecord;
  45. pfreerecord = ^tfreerecord;
  46. tfreerecord = record
  47. size : longint;
  48. next,
  49. prev : pfreerecord;
  50. end; { 12 bytes }
  51. pheaprecord = ^theaprecord;
  52. theaprecord = record
  53. { this should overlap with tfreerecord }
  54. size : longint;
  55. end; { 4 bytes }
  56. tfreelists = array[0..maxblock] of pfreerecord;
  57. pfreelists = ^tfreelists;
  58. var
  59. internal_memavail : longint;
  60. internal_heapsize : longint;
  61. freelists : tfreelists;
  62. {*****************************************************************************
  63. Memory Manager
  64. *****************************************************************************}
  65. procedure GetMemoryManager(var MemMgr:TMemoryManager);
  66. begin
  67. MemMgr:=MemoryManager;
  68. end;
  69. procedure SetMemoryManager(const MemMgr:TMemoryManager);
  70. begin
  71. MemoryManager:=MemMgr;
  72. end;
  73. function IsMemoryManagerSet:Boolean;
  74. begin
  75. IsMemoryManagerSet:=(MemoryManager.GetMem<>@SysGetMem) or
  76. (MemoryManager.FreeMem<>@SysFreeMem);
  77. end;
  78. procedure GetMem(Var p:pointer;Size:Longint);[public,alias:'FPC_GETMEM'];
  79. begin
  80. MemoryManager.GetMem(p,Size);
  81. end;
  82. procedure FreeMem(Var p:pointer);
  83. begin
  84. if p <> nil then
  85. MemoryManager.FreeMem(p);
  86. end;
  87. procedure FreeMem(Var p:pointer;Size:Longint);[public,alias:'FPC_FREEMEM'];
  88. begin
  89. MemoryManager.FreeMemSize(p,Size);
  90. end;
  91. function MemSize(p:pointer):Longint;
  92. begin
  93. MemSize:=MemoryManager.MemSize(p);
  94. end;
  95. { Needed for calls from Assembler }
  96. procedure AsmFreeMem(Var p:pointer);
  97. begin
  98. MemoryManager.FreeMem(p);
  99. end;
  100. {*****************************************************************************
  101. Heapsize,Memavail,MaxAvail
  102. *****************************************************************************}
  103. function heapsize : longint;
  104. begin
  105. heapsize:=internal_heapsize;
  106. end;
  107. function memavail : longint;
  108. begin
  109. memavail:=internal_memavail;
  110. end;
  111. function maxavail : longint;
  112. var
  113. hp : pfreerecord;
  114. begin
  115. maxavail:=heapend-heapptr;
  116. hp:=freelists[0];
  117. while assigned(hp) do
  118. begin
  119. if hp^.size>maxavail then
  120. maxavail:=hp^.size;
  121. hp:=hp^.next;
  122. end;
  123. end;
  124. {$ifdef DUMPBLOCKS}
  125. procedure DumpBlocks;
  126. var
  127. s,i,j : longint;
  128. hp : pfreerecord;
  129. begin
  130. for i:=1 to maxblock do
  131. begin
  132. hp:=freelists[i];
  133. j:=0;
  134. while assigned(hp) do
  135. begin
  136. inc(j);
  137. hp:=hp^.next;
  138. end;
  139. writeln('Block ',i*blocksize,': ',j);
  140. end;
  141. { freelist 0 }
  142. hp:=freelists[0];
  143. j:=0;
  144. s:=0;
  145. while assigned(hp) do
  146. begin
  147. inc(j);
  148. if hp^.size>s then
  149. s:=hp^.size;
  150. hp:=hp^.next;
  151. end;
  152. writeln('Main: ',j,' maxsize: ',s);
  153. end;
  154. {$endif}
  155. {*****************************************************************************
  156. SysGetMem
  157. *****************************************************************************}
  158. procedure SysGetMem(var p : pointer;size : longint);
  159. type
  160. heaperrorproc=function(size:longint):integer;
  161. var
  162. proc : heaperrorproc;
  163. pcurr : pfreerecord;
  164. again : boolean;
  165. s,s1,i,
  166. sizeleft : longint;
  167. {$ifdef BESTMATCH}
  168. pbest : pfreerecord;
  169. {$endif}
  170. begin
  171. { Something to allocate ? }
  172. if size<=0 then
  173. begin
  174. { give an error for < 0 }
  175. if size<0 then
  176. HandleError(204);
  177. { we always need to allocate something, using heapend is not possible,
  178. because heappend can be changed by growheap (PFV) }
  179. size:=1;
  180. end;
  181. { calc to multiply of 16 after adding the needed 8 bytes heaprecord }
  182. size:=(size+sizeof(theaprecord)+(blocksize-1)) and (not (blocksize-1));
  183. dec(internal_memavail,size);
  184. { try to find a block in one of the freelists per size }
  185. s:=size shr blockshr;
  186. if s<=maxblock then
  187. begin
  188. pcurr:=freelists[s];
  189. { correct size match ? }
  190. if assigned(pcurr) then
  191. begin
  192. { create the block we should return }
  193. p:=pointer(pcurr)+sizeof(theaprecord);
  194. { fix size }
  195. pcurr^.size:=pcurr^.size or usedmask;
  196. { update freelist }
  197. freelists[s]:=pcurr^.next;
  198. if assigned(freelists[s]) then
  199. freelists[s]^.prev:=nil;
  200. exit;
  201. end;
  202. {$ifdef SMALLATHEAPPTR}
  203. if heapend-heapptr>size then
  204. begin
  205. p:=heapptr;
  206. pheaprecord(p)^.size:=size;
  207. inc(p,sizeof(theaprecord));
  208. inc(heapptr,size);
  209. exit;
  210. end;
  211. {$endif}
  212. {$ifdef REUSEBIGGER}
  213. { try a bigger block }
  214. s1:=s+s;
  215. i:=0;
  216. while (s1<=maxblock) and (i<maxreusebigger) do
  217. begin
  218. pcurr:=freelists[s1];
  219. if assigned(pcurr) then
  220. begin
  221. s:=s1;
  222. break;
  223. end;
  224. inc(s1);
  225. inc(i);
  226. end;
  227. {$endif}
  228. end
  229. else
  230. pcurr:=nil;
  231. { not found, then check the main freelist for the first match }
  232. if not(assigned(pcurr)) then
  233. begin
  234. s:=0;
  235. {$ifdef BESTMATCH}
  236. pbest:=nil;
  237. {$endif}
  238. pcurr:=freelists[0];
  239. while assigned(pcurr) do
  240. begin
  241. {$ifdef BESTMATCH}
  242. if pcurr^.size=size then
  243. break
  244. else
  245. begin
  246. if (pcurr^.size>size) then
  247. begin
  248. if (not assigned(pbest)) or
  249. (pcurr^.size<pbest^.size) then
  250. pbest:=pcurr;
  251. end;
  252. end;
  253. {$else}
  254. if pcurr^.size>=size then
  255. break;
  256. {$endif}
  257. pcurr:=pcurr^.next;
  258. end;
  259. {$ifdef BESTMATCH}
  260. if not assigned(pcurr) then
  261. pcurr:=pbest;
  262. {$endif}
  263. end;
  264. { have we found a block, then get it and free up the other left part,
  265. if no blocks are found then allocated at the heapptr or grow the heap }
  266. if assigned(pcurr) then
  267. begin
  268. { get pointer of the block we should return }
  269. p:=pointer(pcurr);
  270. { remove the current block from the freelist }
  271. if assigned(pcurr^.next) then
  272. pcurr^.next^.prev:=pcurr^.prev;
  273. if assigned(pcurr^.prev) then
  274. pcurr^.prev^.next:=pcurr^.next
  275. else
  276. freelists[s]:=pcurr^.next;
  277. { create the left over freelist block, if at least 16 bytes are free }
  278. sizeleft:=pcurr^.size-size;
  279. if sizeleft>sizeof(tfreerecord) then
  280. begin
  281. pcurr:=pfreerecord(pointer(pcurr)+size);
  282. pcurr^.size:=sizeleft;
  283. { insert the block in the freelist }
  284. pcurr^.prev:=nil;
  285. s1:=sizeleft shr blockshr;
  286. if s1>maxblock then
  287. s1:=0;
  288. pcurr^.next:=freelists[s1];
  289. if assigned(freelists[s1]) then
  290. freelists[s1]^.prev:=pcurr;
  291. freelists[s1]:=pcurr;
  292. end;
  293. { create the block we need to return }
  294. pheaprecord(p)^.size:=size;
  295. inc(p,sizeof(theaprecord));
  296. exit;
  297. end;
  298. { Lastly, the top of the heap is checked, to see if there is }
  299. { still memory available. }
  300. repeat
  301. again:=false;
  302. if heapend-heapptr>size then
  303. begin
  304. p:=heapptr;
  305. pheaprecord(p)^.size:=size;
  306. inc(p,sizeof(theaprecord));
  307. inc(heapptr,size);
  308. exit;
  309. end;
  310. { Call the heaperror proc }
  311. if assigned(heaperror) then
  312. begin
  313. proc:=heaperrorproc(heaperror);
  314. case proc(size) of
  315. 0 : HandleError(203);
  316. 1 : p:=nil;
  317. 2 : again:=true;
  318. end;
  319. end
  320. else
  321. HandleError(203);
  322. until not again;
  323. end;
  324. {*****************************************************************************
  325. SysFreeMem
  326. *****************************************************************************}
  327. procedure SysFreeMem(var p : pointer);
  328. var
  329. s : longint;
  330. pcurr : pfreerecord;
  331. begin
  332. if p=nil then
  333. HandleError(204);
  334. { fix p to point to the heaprecord }
  335. pcurr:=pfreerecord(pointer(p)-sizeof(theaprecord));
  336. pcurr^.size:=pcurr^.size and sizemask;
  337. inc(internal_memavail,pcurr^.size);
  338. { insert the block in it's freelist }
  339. pcurr^.prev:=nil;
  340. s:=pcurr^.size shr blockshr;
  341. if s>maxblock then
  342. s:=0;
  343. pcurr^.next:=freelists[s];
  344. if assigned(pcurr^.next) then
  345. pcurr^.next^.prev:=pcurr;
  346. freelists[s]:=pcurr;
  347. p:=nil;
  348. end;
  349. procedure SysFreeMemSize(var p : pointer;size : longint);
  350. var
  351. s : longint;
  352. pcurr : pfreerecord;
  353. begin
  354. if size<=0 then
  355. begin
  356. if size<0 then
  357. HandleError(204);
  358. p:=nil;
  359. exit;
  360. end;
  361. if p=nil then
  362. HandleError(204);
  363. { fix p to point to the heaprecord }
  364. pcurr:=pfreerecord(pointer(p)-sizeof(theaprecord));
  365. pcurr^.size:=pcurr^.size and sizemask;
  366. inc(internal_memavail,pcurr^.size);
  367. { size check }
  368. size:=(size+sizeof(theaprecord)+(blocksize-1)) and (not (blocksize-1));
  369. if size<>pcurr^.size then
  370. HandleError(204);
  371. { insert the block in it's freelist }
  372. pcurr^.prev:=nil;
  373. s:=pcurr^.size shr blockshr;
  374. if s>maxblock then
  375. s:=0;
  376. pcurr^.next:=freelists[s];
  377. if assigned(pcurr^.next) then
  378. pcurr^.next^.prev:=pcurr;
  379. freelists[s]:=pcurr;
  380. p:=nil;
  381. end;
  382. {*****************************************************************************
  383. MemSize
  384. *****************************************************************************}
  385. function SysMemSize(p:pointer):longint;
  386. begin
  387. SysMemSize:=(pheaprecord(pointer(p)-sizeof(theaprecord))^.size-sizeof(theaprecord)) and sizemask;
  388. end;
  389. {*****************************************************************************
  390. Mark/Release
  391. *****************************************************************************}
  392. procedure release(var p : pointer);
  393. begin
  394. end;
  395. procedure mark(var p : pointer);
  396. begin
  397. end;
  398. {*****************************************************************************
  399. Grow Heap
  400. *****************************************************************************}
  401. function growheap(size :longint) : integer;
  402. var
  403. sizeleft,
  404. NewPos,
  405. wantedsize : longint;
  406. pcurr : pfreerecord;
  407. begin
  408. {$ifdef DUMPGROW}
  409. writeln('grow ',size);
  410. DumpBlocks;
  411. {$endif}
  412. wantedsize:=size;
  413. { Allocate by 64K size }
  414. size:=(size+$ffff) and $ffff0000;
  415. { first try 256K (default) }
  416. if size<=GrowHeapSize1 then
  417. begin
  418. NewPos:=Sbrk(GrowHeapSize1);
  419. if NewPos>0 then
  420. size:=GrowHeapSize1;
  421. end
  422. else
  423. { second try 1024K (default) }
  424. if size<=GrowHeapSize2 then
  425. begin
  426. NewPos:=Sbrk(GrowHeapSize2);
  427. if NewPos>0 then
  428. size:=GrowHeapSize2;
  429. end
  430. { else alloate the needed bytes }
  431. else
  432. NewPos:=SBrk(size);
  433. { try again }
  434. if NewPos=-1 then
  435. begin
  436. NewPos:=Sbrk(size);
  437. if NewPos=-1 then
  438. begin
  439. GrowHeap:=0;
  440. Exit;
  441. end;
  442. end;
  443. { increase heapend or add to freelist }
  444. if heapend=pointer(newpos) then
  445. begin
  446. heapend:=pointer(newpos+size);
  447. end
  448. else
  449. begin
  450. { create freelist entry for old heapptr-heapend }
  451. sizeleft:=heapend-heapptr;
  452. if sizeleft>sizeof(tfreerecord) then
  453. begin
  454. pcurr:=pfreerecord(heapptr);
  455. pcurr^.size:=sizeleft;
  456. { insert the block in the freelist }
  457. pcurr^.next:=freelists[0];
  458. pcurr^.prev:=nil;
  459. if assigned(freelists[0]) then
  460. freelists[0]^.prev:=pcurr;
  461. freelists[0]:=pcurr;
  462. end;
  463. { now set the new heapptr,heapend to the new block }
  464. heapptr:=pointer(newpos);
  465. heapend:=pointer(newpos+size);
  466. end;
  467. { set the total new heap size }
  468. inc(internal_memavail,size);
  469. inc(internal_heapsize,size);
  470. { try again }
  471. GrowHeap:=2;
  472. end;
  473. {*****************************************************************************
  474. InitHeap
  475. *****************************************************************************}
  476. { This function will initialize the Heap manager and need to be called from
  477. the initialization of the system unit }
  478. procedure InitHeap;
  479. begin
  480. FillChar(FreeLists,sizeof(TFreeLists),0);
  481. internal_heapsize:=GetHeapSize;
  482. internal_memavail:=internal_heapsize;
  483. HeapOrg:=GetHeapStart;
  484. HeapPtr:=HeapOrg;
  485. HeapEnd:=HeapOrg+internal_memavail;
  486. HeapError:=@GrowHeap;
  487. end;
  488. {
  489. $Log$
  490. Revision 1.20 1999-10-22 22:03:07 sg
  491. * FreeMem(p) is ignored if p is NIL, instead of throwing an
  492. runtime error 204. (Delphi ignores NIL FreeMem's, too)
  493. Revision 1.19 1999/10/01 07:55:54 peter
  494. * fixed memsize which forgot the sizemask
  495. Revision 1.18 1999/09/22 21:59:02 peter
  496. * best match for main freelist
  497. * removed root field, saves 4 bytes per block
  498. * fixed crash in dumpblocks
  499. Revision 1.17 1999/09/20 14:17:37 peter
  500. * fixed growheap freelist addition when heapend-heapptr<blocksize
  501. Revision 1.16 1999/09/17 17:14:12 peter
  502. + new heap manager supporting delphi freemem(pointer)
  503. }