2
0

MEM.CPP 43 KB

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