tinyheap.inc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2011 by the Free Pascal development team.
  4. Tiny heap manager for the i8086 near heap, embedded targets, etc.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. { The heap, implemented here is TP7-compatible in the i8086 far data memory
  12. models. It's basically a linked list of free blocks, which are kept ordered by
  13. start address. The FreeList variable points to the start of the list. Each
  14. free block, except the last one, contains a TTinyHeapBlock structure, which
  15. holds the block size and a pointer to the next free block. The HeapPtr
  16. variable points to the last free block, indicating the end of the list. The
  17. last block is special in that it doesn't contain a TTinyHeapBlock structure.
  18. Instead its size is determined by the pointer difference (HeapEnd-HeapPtr).
  19. It *can* become zero sized, when all the memory inside of it is allocated, in
  20. which case, HeapPtr will become equal to HeapEnd. }
  21. {$ifdef cpui8086}
  22. {$if defined(FPC_X86_DATA_FAR) or defined(FPC_X86_DATA_HUGE)}
  23. {$define FPC_HEAP_HUGE}
  24. {$endif}
  25. {$endif cpui8086}
  26. {$ifdef FPC_HEAP_HUGE}
  27. {$HugePointerArithmeticNormalization On}
  28. {$HugePointerComparisonNormalization On}
  29. {$endif FPC_HEAP_HUGE}
  30. type
  31. { TTinyHeapMemBlockSize holds the size of an *allocated* memory block,
  32. and is written at position:
  33. memblockstart-sizeof(TTinyHeapMemBlockSize) }
  34. PTinyHeapMemBlockSize = ^TTinyHeapMemBlockSize; {$ifdef FPC_HEAP_HUGE}huge;{$endif}
  35. TTinyHeapMemBlockSize = PtrUInt;
  36. { TTinyHeapFreeBlockSize holds the size of a *free* memory block, as a
  37. part of the TTinyHeapBlock structure }
  38. {$ifdef FPC_HEAP_HUGE}
  39. TTinyHeapFreeBlockSize = record
  40. OfsSize: Word;
  41. SegSize: Word;
  42. end;
  43. {$else FPC_HEAP_HUGE}
  44. TTinyHeapFreeBlockSize = PtrUInt;
  45. {$endif FPC_HEAP_HUGE}
  46. TTinyHeapPointerArithmeticType = ^Byte; {$ifdef FPC_HEAP_HUGE}huge;{$endif}
  47. PTinyHeapBlock = ^TTinyHeapBlock;
  48. TTinyHeapBlock = record
  49. Next: PTinyHeapBlock;
  50. Size: TTinyHeapFreeBlockSize;
  51. end;
  52. const
  53. TinyHeapMinBlock = sizeof(TTinyHeapBlock);
  54. TinyHeapAllocGranularity = sizeof(TTinyHeapBlock);
  55. function EncodeTinyHeapFreeBlockSize(Size: PtrUInt): TTinyHeapFreeBlockSize; inline;
  56. begin
  57. {$ifdef FPC_HEAP_HUGE}
  58. EncodeTinyHeapFreeBlockSize.OfsSize := Size and 15;
  59. EncodeTinyHeapFreeBlockSize.SegSize := Size shr 4;
  60. {$else FPC_HEAP_HUGE}
  61. EncodeTinyHeapFreeBlockSize := Size;
  62. {$endif FPC_HEAP_HUGE}
  63. end;
  64. function DecodeTinyHeapFreeBlockSize(Size: TTinyHeapFreeBlockSize): PtrUInt; inline;
  65. begin
  66. {$ifdef FPC_HEAP_HUGE}
  67. DecodeTinyHeapFreeBlockSize := (PtrUInt(Size.SegSize) shl 4) + Size.OfsSize;
  68. {$else FPC_HEAP_HUGE}
  69. DecodeTinyHeapFreeBlockSize := Size;
  70. {$endif FPC_HEAP_HUGE}
  71. end;
  72. procedure InternalTinyFreeMem(Addr: Pointer; Size: PtrUInt); forward;
  73. function FindSize(p: pointer): TTinyHeapMemBlockSize;
  74. begin
  75. FindSize := PTinyHeapMemBlockSize(p)[-1];
  76. end;
  77. function SysTinyGetMem(Size: ptruint): pointer;
  78. var
  79. p, prev, p2: PTinyHeapBlock;
  80. AllocSize, RestSize: ptruint;
  81. begin
  82. {$ifdef DEBUG_TINY_HEAP}
  83. Write('SysTinyGetMem(', Size, ')=');
  84. {$endif DEBUG_TINY_HEAP}
  85. AllocSize := align(size+sizeof(TTinyHeapMemBlockSize), TinyHeapAllocGranularity);
  86. p := FreeList;
  87. prev := nil;
  88. while (p<>HeapPtr) and (DecodeTinyHeapFreeBlockSize(p^.Size) < AllocSize) do
  89. begin
  90. prev := p;
  91. p := p^.Next;
  92. end;
  93. if p<>HeapPtr then
  94. begin
  95. result := @PTinyHeapMemBlockSize(p)[1];
  96. if DecodeTinyHeapFreeBlockSize(p^.Size)-AllocSize >= TinyHeapMinBlock then
  97. RestSize := DecodeTinyHeapFreeBlockSize(p^.Size)-AllocSize
  98. else
  99. begin
  100. AllocSize := DecodeTinyHeapFreeBlockSize(p^.Size);
  101. RestSize := 0;
  102. end;
  103. if RestSize > 0 then
  104. begin
  105. p2 := pointer(TTinyHeapPointerArithmeticType(p)+AllocSize);
  106. p2^.Next := p^.Next;
  107. p2^.Size := EncodeTinyHeapFreeBlockSize(RestSize);
  108. if prev = nil then
  109. FreeList := p2
  110. else
  111. prev^.next := p2;
  112. end
  113. else
  114. begin
  115. if prev = nil then
  116. FreeList := p^.Next
  117. else
  118. prev^.next := p^.next;
  119. end;
  120. PTinyHeapMemBlockSize(p)^ := size;
  121. end
  122. else
  123. begin
  124. { p=HeapPtr }
  125. if PtrUInt(TTinyHeapPointerArithmeticType(HeapEnd)-TTinyHeapPointerArithmeticType(HeapPtr))<AllocSize then
  126. if ReturnNilIfGrowHeapFails then
  127. Result := nil
  128. else
  129. HandleError(203);
  130. result := @PTinyHeapMemBlockSize(HeapPtr)[1];
  131. PTinyHeapMemBlockSize(HeapPtr)^ := size;
  132. HeapPtr := pointer(TTinyHeapPointerArithmeticType(HeapPtr)+AllocSize);
  133. if prev = nil then
  134. FreeList := HeapPtr
  135. else
  136. prev^.next := HeapPtr;
  137. end;
  138. {$ifdef DEBUG_TINY_HEAP}
  139. Writeln(HexStr(Result));
  140. {$endif DEBUG_TINY_HEAP}
  141. end;
  142. function TinyGetAlignedMem(Size, Alignment: ptruint): pointer;
  143. var
  144. mem: Pointer;
  145. memp: ptruint;
  146. begin
  147. if alignment <= sizeof(pointer) then
  148. result := GetMem(size)
  149. else
  150. begin
  151. mem := GetMem(Size+Alignment-1);
  152. memp := align(ptruint(mem), Alignment);
  153. InternalTinyFreeMem(mem, TTinyHeapPointerArithmeticType(memp)-TTinyHeapPointerArithmeticType(mem));
  154. result := pointer(memp);
  155. end;
  156. end;
  157. procedure InternalTinyFreeMem(Addr: Pointer; Size: PtrUInt);
  158. var
  159. p, prev: PTinyHeapBlock;
  160. begin
  161. p := FreeList;
  162. prev := nil;
  163. while (p<>HeapPtr) and (TTinyHeapPointerArithmeticType(p) < TTinyHeapPointerArithmeticType(Addr)) do
  164. begin
  165. prev := p;
  166. p := p^.Next;
  167. end;
  168. { join with previous block? }
  169. if assigned(prev) and ((TTinyHeapPointerArithmeticType(prev)+DecodeTinyHeapFreeBlockSize(prev^.Size)) = TTinyHeapPointerArithmeticType(Addr)) then
  170. begin
  171. Addr:=prev;
  172. Size:=DecodeTinyHeapFreeBlockSize(prev^.size)+Size;
  173. end
  174. else
  175. if assigned(prev) then
  176. prev^.Next := Addr
  177. else
  178. FreeList := Addr;
  179. { join with next block? }
  180. if TTinyHeapPointerArithmeticType(p)=(TTinyHeapPointerArithmeticType(Addr)+Size) then
  181. begin
  182. if p=HeapPtr then
  183. HeapPtr:=Addr
  184. else
  185. begin
  186. PTinyHeapBlock(Addr)^.Next:=p^.Next;
  187. PTinyHeapBlock(Addr)^.Size:=EncodeTinyHeapFreeBlockSize(Size+DecodeTinyHeapFreeBlockSize(p^.Size));
  188. end;
  189. end
  190. else
  191. begin
  192. PTinyHeapBlock(Addr)^.Next:=p;
  193. PTinyHeapBlock(Addr)^.Size:=EncodeTinyHeapFreeBlockSize(Size);
  194. end;
  195. end;
  196. function SysTinyFreeMem(Addr: Pointer): ptruint;
  197. var
  198. sz: ptruint;
  199. begin
  200. {$ifdef DEBUG_TINY_HEAP}
  201. Writeln('SysTinyFreeMem(', HexStr(Addr), ')');
  202. {$endif DEBUG_TINY_HEAP}
  203. if addr=nil then
  204. begin
  205. result:=0;
  206. exit;
  207. end;
  208. if (TTinyHeapPointerArithmeticType(addr) < TTinyHeapPointerArithmeticType(HeapOrg)) or
  209. (TTinyHeapPointerArithmeticType(addr) >= TTinyHeapPointerArithmeticType(HeapPtr)) then
  210. HandleError(204);
  211. sz := Align(FindSize(addr)+SizeOf(TTinyHeapMemBlockSize), TinyHeapAllocGranularity);
  212. InternalTinyFreeMem(@PTinyHeapMemBlockSize(addr)[-1], sz);
  213. result := sz;
  214. end;
  215. function SysTinyFreeMemSize(Addr: Pointer; Size: Ptruint): ptruint;
  216. begin
  217. result := SysTinyFreeMem(addr);
  218. end;
  219. function SysTinyMemSize(p: pointer): ptruint;
  220. begin
  221. result := findsize(p);
  222. end;
  223. function SysTinyAllocMem(size: ptruint): pointer;
  224. begin
  225. result := SysTinyGetMem(size);
  226. if result<>nil then
  227. FillChar(result^,SysTinyMemSize(result),0);
  228. end;
  229. function SysTinyReAllocMem(var p: pointer; size: ptruint):pointer;
  230. var
  231. sz: ptruint;
  232. begin
  233. {$ifdef DEBUG_TINY_HEAP}
  234. Write('SysTinyReAllocMem(', HexStr(p), ',', size, ')=');
  235. {$endif DEBUG_TINY_HEAP}
  236. if size=0 then
  237. result := nil
  238. else
  239. result := AllocMem(size);
  240. if result <> nil then
  241. begin
  242. if p <> nil then
  243. begin
  244. sz := FindSize(p);
  245. if sz > size then
  246. sz := size;
  247. move(pbyte(p)^, pbyte(result)^, sz);
  248. end;
  249. end;
  250. SysTinyFreeMem(p);
  251. p := result;
  252. {$ifdef DEBUG_TINY_HEAP}
  253. Writeln(HexStr(result));
  254. {$endif DEBUG_TINY_HEAP}
  255. end;
  256. function MemAvail: PtrUInt;
  257. var
  258. p: PTinyHeapBlock;
  259. begin
  260. MemAvail := PtrUInt(TTinyHeapPointerArithmeticType(HeapEnd)-TTinyHeapPointerArithmeticType(HeapPtr));
  261. if MemAvail > 0 then
  262. Dec(MemAvail, SizeOf(TTinyHeapMemBlockSize));
  263. p := FreeList;
  264. while p <> HeapPtr do
  265. begin
  266. Inc(MemAvail, DecodeTinyHeapFreeBlockSize(p^.Size)-SizeOf(TTinyHeapMemBlockSize));
  267. p := p^.Next;
  268. end;
  269. end;
  270. function MaxAvail: PtrUInt;
  271. var
  272. p: PTinyHeapBlock;
  273. begin
  274. MaxAvail := PtrUInt(TTinyHeapPointerArithmeticType(HeapEnd)-TTinyHeapPointerArithmeticType(HeapPtr));
  275. p := FreeList;
  276. while p <> HeapPtr do
  277. begin
  278. if DecodeTinyHeapFreeBlockSize(p^.Size) > MaxAvail then
  279. MaxAvail := DecodeTinyHeapFreeBlockSize(p^.Size);
  280. p := p^.Next;
  281. end;
  282. if MaxAvail > 0 then
  283. Dec(MaxAvail, SizeOf(TTinyHeapMemBlockSize));
  284. end;
  285. procedure InternalTinyAlign(var AAddress: Pointer; ASize: PtrUInt);
  286. var
  287. alignment_inc: smallint;
  288. begin
  289. alignment_inc := TTinyHeapPointerArithmeticType(align(AAddress,TinyHeapAllocGranularity))-TTinyHeapPointerArithmeticType(AAddress);
  290. Inc(AAddress,alignment_inc);
  291. Dec(ASize,alignment_inc);
  292. Dec(ASize,ASize mod TinyHeapAllocGranularity);
  293. end;
  294. { Strongly simplified version of RegisterTinyHeapBlock, which can be used when
  295. the heap is only a single contiguous memory block. If you want to add
  296. multiple blocks to the heap, you should use RegisterTinyHeapBlock instead. }
  297. procedure RegisterTinyHeapBlock_Simple(AAddress: Pointer; ASize: PtrUInt);
  298. begin
  299. {$ifdef DEBUG_TINY_HEAP}
  300. Writeln('RegisterTinyHeapBlock_Simple(', HexStr(AAddress), ',', ASize, ')');
  301. {$endif DEBUG_TINY_HEAP}
  302. InternalTinyAlign(AAddress, ASize);
  303. HeapOrg:=AAddress;
  304. HeapPtr:=AAddress;
  305. FreeList:=AAddress;
  306. HeapEnd:=Pointer(TTinyHeapPointerArithmeticType(AAddress)+ASize);
  307. end;
  308. { Strongly simplified version of RegisterTinyHeapBlock, which can be used when
  309. the heap is only a single contiguous memory block and the address and size
  310. are already aligned on a TinyHeapAllocGranularity boundary. }
  311. procedure RegisterTinyHeapBlock_Simple_Prealigned(AAddress: Pointer; ASize: PtrUInt);
  312. begin
  313. {$ifdef DEBUG_TINY_HEAP}
  314. Writeln('RegisterTinyHeapBlock_Simple_Prealigned(', HexStr(AAddress), ',', ASize, ')');
  315. {$endif DEBUG_TINY_HEAP}
  316. HeapOrg:=AAddress;
  317. HeapPtr:=AAddress;
  318. FreeList:=AAddress;
  319. HeapEnd:=Pointer(TTinyHeapPointerArithmeticType(AAddress)+ASize);
  320. end;
  321. procedure RegisterTinyHeapBlock(AAddress: pointer; ASize: ptruint);
  322. var
  323. alignment_inc: smallint;
  324. p: PTinyHeapBlock;
  325. begin
  326. {$ifdef DEBUG_TINY_HEAP}
  327. Writeln('RegisterTinyHeapBlock(', HexStr(AAddress), ',', ASize, ')');
  328. {$endif DEBUG_TINY_HEAP}
  329. InternalTinyAlign(AAddress, ASize);
  330. if HeapOrg=nil then
  331. begin
  332. HeapOrg:=AAddress;
  333. HeapPtr:=AAddress;
  334. FreeList:=AAddress;
  335. HeapEnd:=Pointer(TTinyHeapPointerArithmeticType(AAddress)+ASize);
  336. end
  337. else
  338. begin
  339. if (TTinyHeapPointerArithmeticType(HeapOrg) > TTinyHeapPointerArithmeticType(AAddress)) then
  340. HeapOrg:=AAddress;
  341. if TTinyHeapPointerArithmeticType(AAddress) > TTinyHeapPointerArithmeticType(HeapEnd) then
  342. begin
  343. if TTinyHeapPointerArithmeticType(HeapPtr) = TTinyHeapPointerArithmeticType(HeapEnd) then
  344. begin
  345. if FreeList=HeapPtr then
  346. FreeList:=AAddress
  347. else
  348. begin
  349. p:=FreeList;
  350. while p^.Next<>HeapPtr do
  351. p:=p^.Next;
  352. PTinyHeapBlock(HeapPtr)^.Next:=AAddress;
  353. end;
  354. end
  355. else
  356. begin
  357. PTinyHeapBlock(HeapPtr)^.Size:=EncodeTinyHeapFreeBlockSize(TTinyHeapPointerArithmeticType(HeapEnd)-TTinyHeapPointerArithmeticType(HeapPtr));
  358. PTinyHeapBlock(HeapPtr)^.Next:=AAddress;
  359. end;
  360. HeapPtr:=AAddress;
  361. HeapEnd:=Pointer(TTinyHeapPointerArithmeticType(AAddress)+ASize);
  362. end
  363. else if TTinyHeapPointerArithmeticType(AAddress) = TTinyHeapPointerArithmeticType(HeapEnd) then
  364. HeapEnd:=Pointer(TTinyHeapPointerArithmeticType(AAddress)+ASize)
  365. else
  366. InternalTinyFreeMem(AAddress, ASize);
  367. end;
  368. end;
  369. const
  370. TinyHeapMemoryManager: TMemoryManager = (
  371. NeedLock: false; // Obsolete
  372. GetMem: @SysTinyGetMem;
  373. FreeMem: @SysTinyFreeMem;
  374. FreeMemSize: @SysTinyFreeMemSize;
  375. AllocMem: @SysTinyAllocMem;
  376. ReAllocMem: @SysTinyReAllocMem;
  377. MemSize: @SysTinyMemSize;
  378. InitThread: nil;
  379. DoneThread: nil;
  380. RelocateHeap: nil;
  381. GetHeapStatus: nil;
  382. GetFPCHeapStatus: nil;
  383. );