CnC_Remastered_Collection

Command and Conquer: Red Alert
Log | Files | Refs | README | LICENSE

MEM.CPP (43574B)


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