tinyheap.inc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  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(HeapEnd)) 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. procedure InternalTinyAlign(var AAddress: Pointer; ASize: PtrUInt);
  257. var
  258. alignment_inc: smallint;
  259. begin
  260. alignment_inc := TTinyHeapPointerArithmeticType(align(AAddress,TinyHeapAllocGranularity))-TTinyHeapPointerArithmeticType(AAddress);
  261. Inc(AAddress,alignment_inc);
  262. Dec(ASize,alignment_inc);
  263. Dec(ASize,ASize mod TinyHeapAllocGranularity);
  264. end;
  265. { Strongly simplified version of RegisterTinyHeapBlock, which can be used when
  266. the heap is only a single contiguous memory block. If you want to add
  267. multiple blocks to the heap, you should use RegisterTinyHeapBlock instead. }
  268. procedure RegisterTinyHeapBlock_Simple(AAddress: Pointer; ASize: PtrUInt);
  269. begin
  270. {$ifdef DEBUG_TINY_HEAP}
  271. Writeln('RegisterTinyHeapBlock_Simple(', HexStr(AAddress), ',', ASize, ')');
  272. {$endif DEBUG_TINY_HEAP}
  273. InternalTinyAlign(AAddress, ASize);
  274. HeapOrg:=AAddress;
  275. HeapPtr:=AAddress;
  276. FreeList:=AAddress;
  277. HeapEnd:=Pointer(TTinyHeapPointerArithmeticType(AAddress)+ASize);
  278. end;
  279. procedure RegisterTinyHeapBlock(AAddress: pointer; ASize: ptruint);
  280. var
  281. alignment_inc: smallint;
  282. p: PTinyHeapBlock;
  283. begin
  284. {$ifdef DEBUG_TINY_HEAP}
  285. Writeln('RegisterTinyHeapBlock(', HexStr(AAddress), ',', ASize, ')');
  286. {$endif DEBUG_TINY_HEAP}
  287. InternalTinyAlign(AAddress, ASize);
  288. if HeapOrg=nil then
  289. begin
  290. HeapOrg:=AAddress;
  291. HeapPtr:=AAddress;
  292. FreeList:=AAddress;
  293. HeapEnd:=Pointer(TTinyHeapPointerArithmeticType(AAddress)+ASize);
  294. end
  295. else
  296. begin
  297. if (TTinyHeapPointerArithmeticType(HeapOrg) > TTinyHeapPointerArithmeticType(AAddress)) then
  298. HeapOrg:=AAddress;
  299. if TTinyHeapPointerArithmeticType(AAddress) > TTinyHeapPointerArithmeticType(HeapEnd) then
  300. begin
  301. if TTinyHeapPointerArithmeticType(HeapPtr) = TTinyHeapPointerArithmeticType(HeapEnd) then
  302. begin
  303. if FreeList=HeapPtr then
  304. FreeList:=AAddress
  305. else
  306. begin
  307. p:=FreeList;
  308. while p^.Next<>HeapPtr do
  309. p:=p^.Next;
  310. PTinyHeapBlock(HeapPtr)^.Next:=AAddress;
  311. end;
  312. end
  313. else
  314. begin
  315. PTinyHeapBlock(HeapPtr)^.Size:=EncodeTinyHeapFreeBlockSize(TTinyHeapPointerArithmeticType(HeapEnd)-TTinyHeapPointerArithmeticType(HeapPtr));
  316. PTinyHeapBlock(HeapPtr)^.Next:=AAddress;
  317. end;
  318. HeapPtr:=AAddress;
  319. HeapEnd:=Pointer(TTinyHeapPointerArithmeticType(AAddress)+ASize);
  320. end
  321. else if TTinyHeapPointerArithmeticType(AAddress) = TTinyHeapPointerArithmeticType(HeapEnd) then
  322. HeapEnd:=Pointer(TTinyHeapPointerArithmeticType(AAddress)+ASize)
  323. else
  324. InternalTinyFreeMem(AAddress, ASize);
  325. end;
  326. end;
  327. const
  328. TinyHeapMemoryManager: TMemoryManager = (
  329. NeedLock: false; // Obsolete
  330. GetMem: @SysTinyGetMem;
  331. FreeMem: @SysTinyFreeMem;
  332. FreeMemSize: @SysTinyFreeMemSize;
  333. AllocMem: @SysTinyAllocMem;
  334. ReAllocMem: @SysTinyReAllocMem;
  335. MemSize: @SysTinyMemSize;
  336. InitThread: nil;
  337. DoneThread: nil;
  338. RelocateHeap: nil;
  339. GetHeapStatus: nil;
  340. GetFPCHeapStatus: nil;
  341. );