MEM.CPP 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086
  1. //
  2. // Copyright 2020 Electronic Arts Inc.
  3. //
  4. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free
  5. // software: you can redistribute it and/or modify it under the terms of
  6. // the GNU General Public License as published by the Free Software Foundation,
  7. // either version 3 of the License, or (at your option) any later version.
  8. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
  9. // in the hope that it will be useful, but with permitted additional restrictions
  10. // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
  11. // distributed with this program. You should have received a copy of the
  12. // GNU General Public License along with permitted additional restrictions
  13. // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
  14. /***************************************************************************
  15. ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S **
  16. ***************************************************************************
  17. * *
  18. * Project Name : Westwood Library *
  19. * *
  20. * File Name : MEM.C *
  21. * *
  22. * Programmer : Joe L. Bostic *
  23. * Scott K. Bowen *
  24. * *
  25. * Start Date : March 31, 1993 *
  26. * *
  27. * Last Update : September 8, 1994 [IML] *
  28. * *
  29. *-------------------------------------------------------------------------*
  30. * Functions: *
  31. * Mem_Free -- Free a block of memory from system. *
  32. * Mem_Alloc -- Allocate a block of memory from the special memory pool. *
  33. * Mem_Init -- Initialize the private memory allocation pool. *
  34. * Mem_Reference -- Updates the reference time for the specified memory blo*
  35. * Mem_Find -- Returns with pointer to specified memory block. *
  36. * Mem_Find_Oldest -- Returns with the memory block with the oldest time st*
  37. * Mem_Free_Oldest -- Find and free the oldest memory block. *
  38. * Mem_Avail -- Returns the amount of free memory available in the cache.*
  39. * Mem_Cleanup -- Performes a garbage collection on the memory cache. *
  40. * MemNode_Unlink -- Unlinks a node from the cache. *
  41. * MemNode_Insert -- Inserts a node into a cache chain. *
  42. * Mem_Largest_Avail -- Largest free block available. *
  43. * Mem_Lock_Block -- Locks a block so that it cannot be moved in cleanup.*
  44. * Mem_In_Use -- Makes it so a block will never be returned as oldest*
  45. * Mem_Pool_Size -- Returns total amount of memory in pool. *
  46. * Mem_Get_ID -- Returns ID of node. *
  47. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  48. #include <wwstd.h>
  49. #include "wwmem.h"
  50. #include <timer.h>
  51. #include <stddef.h>
  52. //#include <mem.h>
  53. #define DEBUG_FILL FALSE
  54. ////////////////////////////////////////////////////////////////////////////
  55. /*******************************************************************************
  56. ** A allocated block may have one of three meanings in the Time field. The first
  57. ** is the time stamp of the last time it was referenced. The other two values
  58. ** are defined below. MEM_BLOCK_IN_USE means that it will never be returned as the
  59. ** oldest since there is no valid time stamp. LOCKED_BLOCK has the same meaning as
  60. ** MEM_BLOCK_IN_USE with the added feature that the block will not be moved in a
  61. ** Mem_Cleanup(). Therefore, there may be some fragmentation after the cleanup
  62. ** if any blocks are LOCKED. It would be good practice to seldomly lock blocks,
  63. ** for instance, only when a sample is being played.
  64. ** WARNING: If these values change to anything else, logic will need to be changed
  65. ** in Mem_Find_Oldest since it relies on these being small values.
  66. */
  67. #define MEM_BLOCK_IN_USE 0x00
  68. #define MEM_BLOCK_LOCKED 0x01
  69. /*
  70. ** Each block of memory in the pool is headed by this structure.
  71. */
  72. typedef struct MemChain {
  73. struct MemChain *Next; // Pointer to next memory chain node.
  74. struct MemChain *Prev; // Pointer to previous memory chain node.
  75. unsigned long ID; // ID number of block.
  76. unsigned short Time; // TickCount of latest reference.
  77. unsigned long Size; // Size of memory block (in paragraphs).
  78. } MemChain_Type;
  79. /*
  80. ** Holding tank memory management data.
  81. */
  82. typedef struct MemPool {
  83. MemChain_Type *FreeChain; // Pointer to first node in free chain.
  84. MemChain_Type *UsedChain; // Pointer to first node in used chain.
  85. unsigned long FreeMem; // Current amount of free ram (in paragraphs).
  86. unsigned long TotalMem; // Total quantity of memory.
  87. long pad2;
  88. } MemPool_Type;
  89. /*=========================================================================*/
  90. /* The following PRIVATE functions are in this file: */
  91. /*=========================================================================*/
  92. PRIVATE void MemNode_Unlink(MemPool_Type *pool, int freechain, MemChain_Type *node);
  93. PRIVATE void MemNode_Insert(MemPool_Type *pool, int freechain, MemChain_Type *node, unsigned int size, unsigned long id, int merge);
  94. /*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
  95. /***************************************************************************
  96. * Mem_Init -- Initialize the private memory allocation pool. *
  97. * *
  98. * This routine is used to initialize the private memory allocation *
  99. * pool. *
  100. * *
  101. * INPUT: buffer -- Pointer to the buffer that is the allocation pool. *
  102. * *
  103. * size -- Size of the buffer in bytes. *
  104. * *
  105. * OUTPUT: TRUE/FALSE; Was it initialized successfully? *
  106. * *
  107. * WARNINGS: none *
  108. * *
  109. * HISTORY: *
  110. * 03/31/1993 JLB : Created. *
  111. * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, *
  112. * optimized for low memory only. *
  113. *=========================================================================*/
  114. int Mem_Init(void *buffer, long size)
  115. {
  116. MemChain_Type *mem; // Working memory chain node.
  117. MemPool_Type *pool; // Memory pool control structure.
  118. /*
  119. ** The buffer is rounded down to the nearest paragraph.
  120. */
  121. size = size & 0xFFFFFFF0L;
  122. if (!buffer || !size) return(FALSE);
  123. /*
  124. ** Initialize the pool control structure.
  125. */
  126. pool = (MemPool_Type *)buffer;
  127. pool->FreeMem = (size - sizeof(MemPool_Type)) >> 4;
  128. pool->UsedChain = NULL;
  129. pool->TotalMem = pool->FreeMem;
  130. mem = pool->FreeChain = (MemChain_Type *) (pool + 1);
  131. /*
  132. ** Initialize the free memory chain.
  133. */
  134. mem->Next = NULL;
  135. mem->Prev = NULL;
  136. mem->Size = pool->FreeMem;
  137. mem->ID = -1;
  138. mem->Time = 0;
  139. return(TRUE);
  140. }
  141. /***************************************************************************
  142. * Mem_Alloc -- Allocate a block of memory from the special memory pool. *
  143. * *
  144. * This routine will allocate a block of memory from the special *
  145. * memory allocation pool. *
  146. * *
  147. * INPUT: poolptr -- Pointer to the memory pool base address. *
  148. * *
  149. * size -- The size of the memory block to allocate. *
  150. * *
  151. * id -- ID number to give this memory block. *
  152. * *
  153. * OUTPUT: Returns with a pointer to the allocated block. If there was *
  154. * insufficient room, then NULL is returned. *
  155. * *
  156. * WARNINGS: Be sure to check for the NULL return case. *
  157. * *
  158. * HISTORY: *
  159. * 03/31/1993 JLB : Created. *
  160. * 08/06/1993 JLB : Optimized for low memory caches. *
  161. * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, *
  162. * optimized for low memory only. *
  163. *=========================================================================*/
  164. void *Mem_Alloc(void *poolptr, long lsize, unsigned long id)
  165. {
  166. MemPool_Type *pool;
  167. MemChain_Type *node; // Pointer to current memory node.
  168. unsigned int remainder=0; // Remaining bytes that are still free.
  169. int found;
  170. unsigned int size; // Paragraph size of allocation.
  171. /*
  172. ** If there is no free memory then the allocation will
  173. ** always fail.
  174. */
  175. if (!poolptr || !lsize) return(NULL);
  176. pool = (MemPool_Type *) poolptr;
  177. /*
  178. ** Allocations are forced to be paragraph sized.
  179. */
  180. lsize += sizeof(MemChain_Type); // Account for header.
  181. lsize = (lsize + 0x0FL) & 0xFFFFFFF0L;
  182. size = lsize >> 4;
  183. /*
  184. ** If the total free is less than the size of the desired allocation,
  185. ** then we KNOW that an allocation will fail -- just return.
  186. */
  187. if (pool->TotalMem < size) {
  188. return(NULL);
  189. }
  190. /*
  191. ** Walk down free chain looking for the first block that will
  192. ** accomodate the allocation.
  193. */
  194. node = pool->FreeChain;
  195. found = FALSE;
  196. while (!found && node) {
  197. /*
  198. ** Fetch free memory chunk block and see if it is big enough.
  199. */
  200. if (node->Size >= size) {
  201. found = TRUE;
  202. break;
  203. }
  204. node = node->Next;
  205. }
  206. if (!found) {
  207. return(NULL);
  208. }
  209. /*
  210. ** Determine if this allocation would split the block.
  211. */
  212. remainder = node->Size - size;
  213. /*
  214. ** If only a very small free chunk would remain, just tack it on
  215. ** to the current allocation.
  216. */
  217. if (remainder <= 2) {
  218. remainder = 0;
  219. size = node->Size;
  220. }
  221. /*
  222. ** Remove the primary block from the free memory list.
  223. */
  224. MemNode_Unlink(pool, TRUE, node);
  225. /*
  226. ** If a smaller block remains, then link it back into
  227. ** the free memory list.
  228. */
  229. if (remainder) {
  230. MemNode_Insert(pool, TRUE, (MemChain_Type *)Add_Long_To_Pointer(node, (long)size << 4), remainder, -1, FALSE);
  231. }
  232. /*
  233. ** Link in the allocated node into the used memory list.
  234. */
  235. MemNode_Insert(pool, FALSE, node, size, id, FALSE);
  236. /*
  237. ** Reflect the change to the total free count.
  238. */
  239. pool->FreeMem -= size;
  240. /*
  241. ** Return a pointer to the block of allocated memory just past
  242. ** the header.
  243. */
  244. #if DEBUG_FILL
  245. memset(node + 1, id, (size-1) << 4);
  246. #endif
  247. return((void *) (node + 1));
  248. }
  249. /***************************************************************************
  250. * Mem_Free -- Free a block of memory from system. *
  251. * *
  252. * This routine will free a block of memory from the special memory *
  253. * buffer. *
  254. * *
  255. * INPUT: poolptr -- Pointer to the memory pool base address. *
  256. * *
  257. * buffer -- Pointer to memory block to free. *
  258. * *
  259. * OUTPUT: TRUE/FALSE; Was the deallocation successful? *
  260. * *
  261. * WARNINGS: Be sure to only pass in to this routine a buffer that was *
  262. * returned from Mem_Alloc(). *
  263. * *
  264. * HISTORY: *
  265. * 03/31/1993 JLB : Created. *
  266. * 08/06/1993 JLB : Optimized for low memory caches. *
  267. * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, *
  268. * optimized for low memory only. *
  269. *=========================================================================*/
  270. int Mem_Free(void *poolptr, void *buffer)
  271. {
  272. MemPool_Type *pool; // pointer to structure.
  273. MemChain_Type *node; // Copy of current memory node.
  274. unsigned int size; // Size of the block being freed.
  275. /*
  276. ** One can't free what isn't there.
  277. */
  278. if (!buffer || !poolptr) {
  279. return(FALSE);
  280. }
  281. pool = (MemPool_Type *) poolptr;
  282. /*
  283. ** The node pointer is actually back a bit from the "normal" pointer.
  284. */
  285. node = (MemChain_Type *) buffer;
  286. node--;
  287. /*
  288. ** Get pointer to actual allocated node and unlink it from the used
  289. ** memory chain.
  290. */
  291. size = node->Size;
  292. MemNode_Unlink(pool, FALSE, node);
  293. MemNode_Insert(pool, TRUE, node, size, -1, TRUE);
  294. /*
  295. ** Reflect the new free memory into the total memory count.
  296. */
  297. pool->FreeMem += size;
  298. return(TRUE);
  299. }
  300. /***************************************************************************
  301. * Mem_Reference -- Updates the reference time for the specified memory blo*
  302. * *
  303. * This routine is used to update the memory reference time for the *
  304. * specified memory node. Typically, this is called every time a *
  305. * memory block is used in order to make sure the memory block time *
  306. * tracking (Last Recently Used) system works properly. *
  307. * *
  308. * INPUT: node -- Pointer to memory block returned from Mem_Find. *
  309. * *
  310. * OUTPUT: none *
  311. * *
  312. * WARNINGS: The node pointer must be valid. For maximum safety this *
  313. * routine should be called right after Mem_Find(). *
  314. * *
  315. * HISTORY: *
  316. * 08/06/1993 JLB : Created. *
  317. * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, *
  318. * optimized for low memory only. *
  319. *=========================================================================*/
  320. void Mem_Reference(void *node)
  321. {
  322. MemChain_Type *nodeptr; // Pointer of current memory node.
  323. if (!node) return;
  324. // Get to the node header.
  325. nodeptr = (MemChain_Type *) node;
  326. nodeptr--;
  327. nodeptr->Time = (unsigned short)(TickCount.Time() >> 4);
  328. }
  329. /***************************************************************************
  330. * MEM_LOCK_BLOCK -- Locks a block so that it cannot be moved in cleanup. *
  331. * By marking a memory block in use, the memory system will never return*
  332. * it as the oldest memory block. It also makes it so that the block *
  333. * will never be moved during a Cleanup process. *
  334. * *
  335. * INPUT: node -- Pointer to memory block returned from Mem_Find. *
  336. * *
  337. * OUTPUT: none *
  338. * *
  339. * WARNINGS: If one or more blocks are locked in a heap, Mem_Avail might *
  340. * not equal Mem_Largest_Avail after a call to Mem_Cleanup. *
  341. * *
  342. * HISTORY: *
  343. * 04/15/1994 SKB : Created. *
  344. *=========================================================================*/
  345. void Mem_Lock_Block(void *node)
  346. {
  347. MemChain_Type *nodeptr; // Pointer of current memory node.
  348. if (!node) return;
  349. // Get to the node header.
  350. nodeptr = (MemChain_Type *) node;
  351. nodeptr--;
  352. nodeptr->Time = MEM_BLOCK_LOCKED;
  353. }
  354. /***************************************************************************
  355. * MEM_IN_USE -- Makes it so a block will never be returned as oldest *
  356. * By marking a memory block in use, the memory system will never return*
  357. * it as the oldest memory block. It still can be moved in the Cleanup *
  358. * code. *
  359. * *
  360. * INPUT: node -- Pointer to memory block returned from Mem_Find. *
  361. * *
  362. * OUTPUT: none *
  363. * *
  364. * WARNINGS: Mem_Find_Oldest() will return NULL if only IN_USE blocks are *
  365. * in memory. *
  366. * HISTORY: *
  367. * 04/15/1994 SKB : Created. *
  368. *=========================================================================*/
  369. void Mem_In_Use(void *node)
  370. {
  371. MemChain_Type *nodeptr; // Pointer of current memory node.
  372. if (!node) return;
  373. // Get to the node header.
  374. nodeptr = (MemChain_Type *) node - 1;
  375. nodeptr->Time = MEM_BLOCK_IN_USE;
  376. }
  377. /***************************************************************************
  378. * Mem_Find -- Returns with pointer to specified memory block. *
  379. * *
  380. * Use this routine to convert a memory ID value into an actual memory *
  381. * pointer. It sweeps through all of the 'cached' memory blocks and *
  382. * returns with the matching block pointer. *
  383. * *
  384. * INPUT: poolptr -- Pointer to the memory cache block. *
  385. * *
  386. * id -- The ID of the block desired. *
  387. * *
  388. * OUTPUT: Returns with the pointer to the memory block. If NULL is *
  389. * returned then the desired block is not in the memory cache. *
  390. * *
  391. * WARNINGS: This routine may return NULL if the memory block is not *
  392. * present in the cache. *
  393. * *
  394. * HISTORY: *
  395. * 08/06/1993 JLB : Created. *
  396. * 08/06/1993 JLB : Optimized for low memory caches. *
  397. * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, *
  398. * optimized for low memory only. *
  399. *=========================================================================*/
  400. void *Mem_Find(void *poolptr, unsigned long id)
  401. {
  402. MemPool_Type *pool; // pointer to structure.
  403. MemChain_Type *node; // Working node structure.
  404. if (!poolptr) return(NULL);
  405. pool = (MemPool_Type *) poolptr;
  406. /*
  407. ** Cannot free a node that is not on the UsedChain list.
  408. */
  409. if (!pool->UsedChain) {
  410. return(NULL);
  411. }
  412. /*
  413. ** Sweep through entire allocation chain to find
  414. ** the one with the matching ID.
  415. */
  416. node = pool->UsedChain;
  417. while (node) {
  418. if (node->ID == id) {
  419. return(node + 1);
  420. }
  421. node = node->Next;
  422. }
  423. return(NULL);
  424. }
  425. /***************************************************************************
  426. * MEM_GET_ID -- Returns ID of node. *
  427. * *
  428. * INPUT: void *node - pointer to node. *
  429. * *
  430. * OUTPUT: The ID of the node that was supplied by user during Mem_Alloc().*
  431. * *
  432. * WARNINGS: pointer to node must be one that Mem_Alloc or *
  433. * Mem_Find returned. **
  434. * *
  435. * HISTORY: *
  436. * 04/18/1994 SKB : Created. *
  437. *=========================================================================*/
  438. unsigned long Mem_Get_ID(void *node)
  439. {
  440. MemChain_Type *nodeptr; // Pointer of current memory node.
  441. if (!node) return (0L);
  442. // Get to the node header.
  443. nodeptr = (MemChain_Type *) node - 1;
  444. return (nodeptr->ID);
  445. }
  446. /***************************************************************************
  447. * Mem_Find_Oldest -- Returns with the memory block with the oldest time st*
  448. * *
  449. * Use this routine to find the memory block with the oldest time stamp *
  450. * value. Typically, this is used when freeing memory blocks in the *
  451. * cache in order to make room for a new memory block. *
  452. * *
  453. * INPUT: poolptr -- Pointer to the memory cache. *
  454. * *
  455. * OUTPUT: Returns with the pointer to the oldest memory block. If NULL *
  456. * is returned, then the memory cache is empty. *
  457. * *
  458. * WARNINGS: This routine could return NULL. *
  459. * *
  460. * HISTORY: *
  461. * 08/06/1993 JLB : Created. *
  462. * 08/06/1993 JLB : Optimized for low memory caches. *
  463. * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, *
  464. * optimized for low memory only. *
  465. * 04/15/1994 SKB : Handle time wrap, locked blocks, and no_refenece blocks*
  466. *=========================================================================*/
  467. void *Mem_Find_Oldest(void *poolptr)
  468. {
  469. MemChain_Type *node; // Working node pointer.
  470. MemChain_Type *oldnode; // Pointer to oldest block.
  471. unsigned int oldtime; // Time of oldest block.
  472. unsigned int basetime; // Time to mark our base time with.
  473. unsigned int time; // basetime + time of node.
  474. if (!poolptr) return(NULL);
  475. /*
  476. ** Sweep through entire allocation chain to find
  477. ** the oldest referenced memory block.
  478. */
  479. oldnode = NULL;
  480. oldtime = 0;
  481. node = ((MemPool_Type*) poolptr)->UsedChain;
  482. basetime = (unsigned int)(TickCount.Time() >> 4);
  483. while (node) {
  484. /*
  485. ** Don't allow MEM_BLOCK_IN_USE or MEM_BLOCK_LOCKED to be returned.
  486. */
  487. if (node->Time > MEM_BLOCK_LOCKED) {
  488. /*
  489. ** Adjust time for wrap around (after about 5 hrs).
  490. ** times less then the base time will wrap up high while
  491. ** and times greater then base time will then be lower since
  492. ** any time greater has been on the thing a long time.
  493. */
  494. time = node->Time - basetime ;
  495. if (time < oldtime || !oldnode) {
  496. oldtime = time;
  497. oldnode = node;
  498. }
  499. }
  500. node = node->Next;
  501. }
  502. /*
  503. ** Return with the value that matches the pointer that
  504. ** was allocated by the system previously.
  505. */
  506. if (oldnode) {
  507. oldnode++;
  508. }
  509. return(oldnode);
  510. }
  511. /***************************************************************************
  512. * Mem_Free_Oldest -- Find and free the oldest memory block. *
  513. * *
  514. * This routine is used to free the oldest memory block in the memory *
  515. * cache. This routine is typcially used in order to create more room *
  516. * in the cache for a new allocation. *
  517. * *
  518. * INPUT: poolptr -- Pointer to the memory cache. *
  519. * *
  520. * OUTPUT: Returns with the node that it freed. Although this node is *
  521. * is no longer valid, it may be used to mark that pointer as *
  522. * invalid in the main code. *
  523. * *
  524. * WARNINGS: If this routine returns NULL, then no memory was freed. *
  525. * *
  526. * HISTORY: *
  527. * 08/06/1993 JLB : Created. *
  528. * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, *
  529. * optimized for low memory only. *
  530. *=========================================================================*/
  531. void *Mem_Free_Oldest(void *poolptr)
  532. {
  533. MemChain_Type *node; // Copy of pointer to oldest node.
  534. if (!poolptr) return(NULL);
  535. node = (MemChain *) Mem_Find_Oldest(poolptr);
  536. if (Mem_Free(poolptr, node)) {
  537. return(node);
  538. }
  539. return(NULL);
  540. }
  541. /***************************************************************************
  542. * MEM_POOL_SIZE -- Returns total amount of memory in pool. *
  543. * *
  544. * INPUT: poolptr -- Pointer to the memory cache. *
  545. * *
  546. * OUTPUT: long total size of pool. i.e. largest possible allocation if *
  547. * no memory was allocated. *
  548. * *
  549. * WARNINGS: *
  550. * *
  551. * HISTORY: *
  552. * 04/18/1994 SKB : Created. *
  553. *=========================================================================*/
  554. long Mem_Pool_Size(void *poolptr)
  555. {
  556. MemPool_Type *pool; // Memory pool control structure.
  557. long memtotal; // Total amount of memory free.
  558. if (!poolptr) return(NULL);
  559. pool = (MemPool_Type *) poolptr;
  560. memtotal = ((long)pool->TotalMem) << 4;
  561. memtotal -= sizeof(MemChain_Type);
  562. memtotal = MAX(memtotal, (long)0);
  563. return(memtotal);
  564. }
  565. /***************************************************************************
  566. * Mem_Avail -- Returns the amount of free memory available in the cache. *
  567. * *
  568. * This routine examines the memory cache and returns the amount of *
  569. * free memory available. This memory total MAY be fragmented but *
  570. * after Mem_Cleanup() is called, an allocation of the amount returned *
  571. * by this function is guaranteed. *
  572. * *
  573. * INPUT: poolptr -- Pointer to the memory cache. *
  574. * *
  575. * OUTPUT: Returns the largest allocation possible from the memory cache. *
  576. * *
  577. * WARNINGS: The value returned may represent the FRAGMENTED total *
  578. * amount of memory free in the cache. *
  579. * *
  580. * HISTORY: *
  581. * 08/06/1993 JLB : Created. *
  582. * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, *
  583. * optimized for low memory only. *
  584. *=========================================================================*/
  585. long Mem_Avail(void *poolptr)
  586. {
  587. MemPool_Type *pool; // Memory pool control structure.
  588. long memtotal; // Total amount of memory free.
  589. if (!poolptr) return(NULL);
  590. pool = (MemPool_Type *) poolptr;
  591. memtotal = ((long)pool->FreeMem) << 4;
  592. memtotal -= sizeof(MemChain_Type);
  593. //memtotal -= sizeof(MemChain_Type) + 15;
  594. memtotal = MAX(memtotal, (long)0);
  595. return(memtotal);
  596. }
  597. /***************************************************************************
  598. * MEM_LARGEST_AVAIL -- Largest free block available. *
  599. * This routine examines the free node list to find the largest block *
  600. * available. User can Mem_Alloc() this return size successfully. *
  601. * *
  602. * INPUT: poolptr -- Pointer to the memory cache. *
  603. * *
  604. * OUTPUT: Returns largest allocation currently possible from the cache. *
  605. * *
  606. * WARNINGS: *
  607. * *
  608. * HISTORY: *
  609. * 04/15/1994 SKB : Created. *
  610. *=========================================================================*/
  611. long Mem_Largest_Avail(void *poolptr)
  612. {
  613. MemChain_Type *node; // Pointer to current memory node.
  614. unsigned int size;
  615. long truesize;
  616. /*
  617. ** Make sure that it is a buffer.
  618. */
  619. if (!poolptr) return(NULL);
  620. /*
  621. ** Go through the entire free chain looking for the largest block.
  622. */
  623. node = ((MemPool_Type *)poolptr)->FreeChain;
  624. size = 0;
  625. while (node) {
  626. /*
  627. ** Fetch free memory chunk block and see if it is big enough.
  628. */
  629. if (node->Size >= size) {
  630. size = node->Size;
  631. }
  632. node = node->Next;
  633. }
  634. truesize = (long)size << 4;
  635. truesize -= sizeof(MemChain_Type);
  636. truesize = MAX(truesize, 0L);
  637. return (truesize);
  638. }
  639. /***************************************************************************
  640. * Mem_Cleanup -- Performs a garbage collection on the memory cache. *
  641. * *
  642. * This routine is used to coalesce all adjacent free blocks of *
  643. * memory in the specified cache. As a result, all previous pointers *
  644. * provided by Mem_Find() are invalidated. This routine consumes a *
  645. * fair amount of time and should be called as infrequently as *
  646. * possible. *
  647. * *
  648. * INPUT: poolptr -- Pointer to the memory cache. *
  649. * *
  650. * OUTPUT: none *
  651. * *
  652. * WARNINGS: This routine takes a significant amount of time! *
  653. * If there are locked block in memory, the pool may still *
  654. * be fragmented. *
  655. * *
  656. * HISTORY: *
  657. * 08/06/1993 JLB : Created. *
  658. * 08/06/1993 JLB : Updated for low memory caches. *
  659. * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, *
  660. * optimized for low memory only. *
  661. *=========================================================================*/
  662. void Mem_Cleanup(void *poolptr)
  663. {
  664. MemPool_Type *pool; // Memory pool control structure.
  665. MemChain_Type *free, // Pointer to first free area.
  666. *cur; // Pointer to first used block that is after free.
  667. unsigned long size;
  668. unsigned long freesize;// Size of free heap at the end of the block.
  669. if (!poolptr) return;
  670. /*
  671. ** Fetch working copy of pool control structure.
  672. */
  673. pool = (MemPool_Type *) poolptr;
  674. /*
  675. ** Basic parameter and condition legality checks. If the memory pool
  676. ** has no free space, no free blocks, or no allocated blocks, then
  677. ** memory cleanup is unnecessary -- just exit.
  678. */
  679. if (!pool->FreeMem || !pool->FreeChain || !pool->UsedChain) return;
  680. freesize = pool->FreeMem;
  681. free = pool->FreeChain;
  682. pool->FreeChain = NULL;
  683. cur = pool->UsedChain;
  684. while (TRUE) {
  685. /*
  686. ** Setup pointers so that free points to the first free block and cur
  687. ** points to the next used block after the free block.
  688. */
  689. while (cur < free && cur) {
  690. cur = cur->Next;
  691. }
  692. // All used blocks are at the front of the free. We are done.
  693. if (!cur) {
  694. break;
  695. }
  696. /*
  697. ** Do not allow a locked block to be moved.
  698. */
  699. if (cur->Time == MEM_BLOCK_LOCKED) {
  700. /*
  701. ** Figure the size of the new free block that we are creating.
  702. ** Subtract off the total block size.
  703. ** Add the node to the free list.
  704. */
  705. size = (char *) cur - (char *) free;
  706. size >>= 4;
  707. freesize -= size;
  708. MemNode_Insert(pool, TRUE, free, (unsigned int) size, -1, FALSE);
  709. /*
  710. ** Time to find a new free position to start working from.
  711. ** Cur will be in the position just following.
  712. */
  713. free = (MemChain_Type *) Add_Long_To_Pointer(cur, (unsigned long)cur->Size << 4);
  714. cur = cur->Next;
  715. while (free == cur) {
  716. free = (MemChain_Type *) Add_Long_To_Pointer(cur, (unsigned long)cur->Size << 4);
  717. cur = cur->Next;
  718. }
  719. // All used blocks are at the front of the free. We are done.
  720. if (!cur) {
  721. break;
  722. }
  723. } else {
  724. // Copy the block up.
  725. size = (unsigned long)cur->Size << 4;
  726. Mem_Copy(cur, free, size);
  727. cur = free;
  728. // Change pointers of surrounding blocks.
  729. if (cur->Next) {
  730. cur->Next->Prev = cur;
  731. }
  732. if (cur->Prev) {
  733. cur->Prev->Next = cur;
  734. } else {
  735. pool->UsedChain = cur;
  736. }
  737. // Change to next new free area.
  738. free = (MemChain_Type *) Add_Long_To_Pointer(cur, size);
  739. }
  740. }
  741. /*
  742. ** Now build the single free chunk.
  743. */
  744. MemNode_Insert(pool, TRUE, free, freesize, -1, FALSE);
  745. }
  746. /***************************************************************************
  747. * MemNode_Unlink -- Unlinks a node from the cache. *
  748. * *
  749. * A private routine the actually unlinks a memory block from the *
  750. * memory cache. It doesn't perform a complete update of the memory *
  751. * cache. *
  752. * *
  753. * INPUT: pool -- Pointer to the memory cache header (copy in real *
  754. * memory). *
  755. * *
  756. * freechain-- Is the block part of the free memory chain? *
  757. * *
  758. * node -- Pointer to the node that will be unlinked. *
  759. * *
  760. * OUTPUT: none *
  761. * *
  762. * WARNINGS: This routine doesn't update memory totals. It is a support *
  763. * function. *
  764. * *
  765. * HISTORY: *
  766. * 08/06/1993 JLB : Created. *
  767. * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, *
  768. * optimized for low memory only. *
  769. *=========================================================================*/
  770. PRIVATE void MemNode_Unlink(MemPool_Type *pool, int freechain, MemChain_Type *node)
  771. {
  772. MemChain_Type *other; // Copy of node data to unlink.
  773. MemChain_Type **chain; // A pointer to one of the chains pointer.
  774. /*
  775. ** Check for parameter validity.
  776. */
  777. if (!pool || !node) return;
  778. /*
  779. ** Setup working pointer for the particular chain desired.
  780. */
  781. if (freechain) {
  782. chain = &pool->FreeChain;
  783. } else {
  784. chain = &pool->UsedChain;
  785. }
  786. /*
  787. ** Make adjustments to the previous node. If the pointer
  788. ** to the previous node is NULL then this indicates the
  789. ** first node in the list and thus the chain pointer needs
  790. ** to be updated instead.
  791. */
  792. if (node->Prev) {
  793. other = node->Prev;
  794. other->Next = node->Next;
  795. } else {
  796. *chain = node->Next;
  797. }
  798. if (node->Next) {
  799. other = node->Next;
  800. other->Prev = node->Prev;
  801. }
  802. }
  803. /***************************************************************************
  804. * MemNode_Insert -- Inserts a node into a cache chain. *
  805. * *
  806. * This routine is used to add a node to a cache chain. Since nodes *
  807. * do not contain double links, they must be placed in sequence. *
  808. * *
  809. * INPUT: pool -- Pointer to memory pool (must be in real memory). *
  810. * *
  811. * freechain-- Is the node to be inserted into the free chain? *
  812. * *
  813. * node -- Pointer to the node to insert. *
  814. * *
  815. * size -- Size of the memory block (in paragraphs). *
  816. * *
  817. * id -- The ID number to associate with this block. *
  818. * *
  819. * merge -- Merge inserted block with adjacent blocks. *
  820. * *
  821. * OUTPUT: return *
  822. * *
  823. * WARNINGS: This is a support routine. *
  824. * *
  825. * HISTORY: *
  826. * 08/06/1993 JLB : Created. *
  827. *=========================================================================*/
  828. PRIVATE void MemNode_Insert(MemPool_Type *pool, int freechain, MemChain_Type *node, unsigned int size, unsigned long id, int merge)
  829. {
  830. MemChain_Type **chain; // Pointer to chain that will be linked.
  831. MemChain_Type *prev, // Successor node pointer.
  832. *next; // Predecessor node pointer.
  833. int doit=TRUE; // Link the node into the list.
  834. /*
  835. ** Determine if the parameters are valid.
  836. */
  837. if (!pool || !node || !size) return;
  838. /*
  839. ** Setup working pointer for the particular chain desired.
  840. */
  841. if (freechain) {
  842. chain = &pool->FreeChain;
  843. } else {
  844. chain = &pool->UsedChain;
  845. }
  846. /*
  847. ** Handle the "no node in list" condition (easiest).
  848. */
  849. if (!*chain) {
  850. node->Next = NULL;
  851. node->Prev = NULL;
  852. node->Size = size;
  853. node->Time = (unsigned short)(TickCount.Time() >> 4);
  854. node->ID = id;
  855. *chain = node;
  856. return;
  857. }
  858. /*
  859. ** Sweep through the memory chain looking for a likely spot
  860. ** to insert the new node. It will stop with "next" pointing
  861. ** to the node to come after the block to be inserted and "prev"
  862. ** will point to the node right before.
  863. */
  864. prev = NULL;
  865. next = *chain;
  866. while (next && (next < node)) {
  867. /*
  868. ** Move up the memory chain.
  869. */
  870. prev = next;
  871. next = next->Next;
  872. }
  873. /*
  874. ** Coallescing of adjacent blocks (if requested).
  875. */
  876. if (merge) {
  877. /*
  878. ** If the previous block is touching the block to insert
  879. ** then merely adjust the size of the previous block and
  880. ** that is all that is necessary.
  881. */
  882. if (prev) {
  883. if (((char *)prev + ((long)prev->Size << 4)) == ((char *) node)) {
  884. prev->Size += size;
  885. size = prev->Size;
  886. node = prev;
  887. prev = prev->Prev;
  888. doit = FALSE;
  889. }
  890. }
  891. /*
  892. ** If the following block is touching the block to insert
  893. ** then remove the following block and increase the size of
  894. ** the original insertion block by the size of the other
  895. ** block.
  896. */
  897. if (next) {
  898. if (((char *)node + ((long)size << 4)) == (char *)next) {
  899. if (!doit) {
  900. /*
  901. ** If the node was already merged with the previous block
  902. ** then merely increase the previous block's size
  903. ** and adjust it's next pointer appropriately.
  904. */
  905. node->Size += next->Size;
  906. node->Next = next->Next;
  907. next = next->Next;
  908. } else {
  909. /*
  910. ** Increase the size of the current block and adjust
  911. ** the "next" pointer so that it gets fixed up
  912. ** accordingly.
  913. */
  914. size += next->Size;
  915. next = next->Next;
  916. }
  917. }
  918. }
  919. }
  920. #if DEBUG_FILL
  921. if (doit) {
  922. memset(node + 1, 0xFF, (size - 1) << 4);
  923. } else {
  924. memset(node + 1, 0xFF, (node->Size - 1) << 4);
  925. }
  926. #endif
  927. /*
  928. ** Fixup the node pointers.
  929. */
  930. if (prev) {
  931. prev->Next = node;
  932. }else{
  933. *chain = node;
  934. }
  935. if (next) {
  936. next->Prev = node;
  937. }
  938. if (doit) {
  939. node->Prev = prev;
  940. node->Next = next;
  941. node->Size = size;
  942. node->Time = (unsigned short)(TickCount.Time() >> 4);
  943. node->ID = id;
  944. }
  945. }