| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091 |
- /*
- ** Command & Conquer Red Alert(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- /***************************************************************************
- ** 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 **
- ***************************************************************************
- * *
- * Project Name : Westwood Library *
- * *
- * File Name : MEM.C *
- * *
- * Programmer : Joe L. Bostic *
- * Scott K. Bowen *
- * *
- * Start Date : March 31, 1993 *
- * *
- * Last Update : September 8, 1994 [IML] *
- * *
- *-------------------------------------------------------------------------*
- * Functions: *
- * Mem_Free -- Free a block of memory from system. *
- * Mem_Alloc -- Allocate a block of memory from the special memory pool. *
- * Mem_Init -- Initialize the private memory allocation pool. *
- * Mem_Reference -- Updates the reference time for the specified memory blo*
- * Mem_Find -- Returns with pointer to specified memory block. *
- * Mem_Find_Oldest -- Returns with the memory block with the oldest time st*
- * Mem_Free_Oldest -- Find and free the oldest memory block. *
- * Mem_Avail -- Returns the amount of free memory available in the cache.*
- * Mem_Cleanup -- Performes a garbage collection on the memory cache. *
- * MemNode_Unlink -- Unlinks a node from the cache. *
- * MemNode_Insert -- Inserts a node into a cache chain. *
- * Mem_Largest_Avail -- Largest free block available. *
- * Mem_Lock_Block -- Locks a block so that it cannot be moved in cleanup.*
- * Mem_In_Use -- Makes it so a block will never be returned as oldest*
- * Mem_Pool_Size -- Returns total amount of memory in pool. *
- * Mem_Get_ID -- Returns ID of node. *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #include <wwstd.h>
- #include "wwmem.h"
- #include <timer.h>
- #include <stddef.h>
- #include <mem.h>
- extern TimerClass TickCount;
- #define DEBUG_FILL FALSE
- ////////////////////////////////////////////////////////////////////////////
- /*******************************************************************************
- ** A allocated block may have one of three meanings in the Time field. The first
- ** is the time stamp of the last time it was referenced. The other two values
- ** are defined below. MEM_BLOCK_IN_USE means that it will never be returned as the
- ** oldest since there is no valid time stamp. LOCKED_BLOCK has the same meaning as
- ** MEM_BLOCK_IN_USE with the added feature that the block will not be moved in a
- ** Mem_Cleanup(). Therefore, there may be some fragmentation after the cleanup
- ** if any blocks are LOCKED. It would be good practice to seldomly lock blocks,
- ** for instance, only when a sample is being played.
- ** WARNING: If these values change to anything else, logic will need to be changed
- ** in Mem_Find_Oldest since it relies on these being small values.
- */
- #define MEM_BLOCK_IN_USE 0x00
- #define MEM_BLOCK_LOCKED 0x01
- /*
- ** Each block of memory in the pool is headed by this structure.
- */
- typedef struct MemChain {
- struct MemChain *Next; // Pointer to next memory chain node.
- struct MemChain *Prev; // Pointer to previous memory chain node.
- unsigned long ID; // ID number of block.
- unsigned short Time; // TickCount of latest reference.
- unsigned short Size; // Size of memory block (in paragraphs).
- } MemChain_Type;
- /*
- ** Holding tank memory management data.
- */
- typedef struct MemPool {
- MemChain_Type *FreeChain; // Pointer to first node in free chain.
- MemChain_Type *UsedChain; // Pointer to first node in used chain.
- unsigned short FreeMem; // Current amount of free ram (in paragraphs).
- unsigned short TotalMem; // Total quantity of memory.
- long pad2;
- } MemPool_Type;
- /*=========================================================================*/
- /* The following PRIVATE functions are in this file: */
- /*=========================================================================*/
- PRIVATE void MemNode_Unlink(MemPool_Type *pool, int freechain, MemChain_Type *node);
- PRIVATE void MemNode_Insert(MemPool_Type *pool, int freechain, MemChain_Type *node, unsigned int size, unsigned long id, int merge);
- /*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
- /***************************************************************************
- * Mem_Init -- Initialize the private memory allocation pool. *
- * *
- * This routine is used to initialize the private memory allocation *
- * pool. *
- * *
- * INPUT: buffer -- Pointer to the buffer that is the allocation pool. *
- * *
- * size -- Size of the buffer in bytes. *
- * *
- * OUTPUT: TRUE/FALSE; Was it initialized successfully? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 03/31/1993 JLB : Created. *
- * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, *
- * optimized for low memory only. *
- *=========================================================================*/
- int Mem_Init(void *buffer, long size)
- {
- MemChain_Type *mem; // Working memory chain node.
- MemPool_Type *pool; // Memory pool control structure.
- /*
- ** The buffer is rounded down to the nearest paragraph.
- */
- size = size & 0xFFFFFFF0L;
- if (!buffer || !size) return(FALSE);
- /*
- ** Initialize the pool control structure.
- */
- pool = (MemPool_Type *)buffer;
- pool->FreeMem = (size - sizeof(MemPool_Type)) >> 4;
- pool->UsedChain = NULL;
- pool->TotalMem = pool->FreeMem;
- mem = pool->FreeChain = (MemChain_Type *) (pool + 1);
- /*
- ** Initialize the free memory chain.
- */
- mem->Next = NULL;
- mem->Prev = NULL;
- mem->Size = pool->FreeMem;
- mem->ID = -1;
- mem->Time = 0;
- return(TRUE);
- }
- /***************************************************************************
- * Mem_Alloc -- Allocate a block of memory from the special memory pool. *
- * *
- * This routine will allocate a block of memory from the special *
- * memory allocation pool. *
- * *
- * INPUT: poolptr -- Pointer to the memory pool base address. *
- * *
- * size -- The size of the memory block to allocate. *
- * *
- * id -- ID number to give this memory block. *
- * *
- * OUTPUT: Returns with a pointer to the allocated block. If there was *
- * insufficient room, then NULL is returned. *
- * *
- * WARNINGS: Be sure to check for the NULL return case. *
- * *
- * HISTORY: *
- * 03/31/1993 JLB : Created. *
- * 08/06/1993 JLB : Optimized for low memory caches. *
- * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, *
- * optimized for low memory only. *
- *=========================================================================*/
- void *Mem_Alloc(void *poolptr, long lsize, unsigned long id)
- {
- MemPool_Type *pool;
- MemChain_Type *node; // Pointer to current memory node.
- unsigned int remainder=0; // Remaining bytes that are still free.
- int found;
- int size; // Paragraph size of allocation.
- /*
- ** If there is no free memory then the allocation will
- ** always fail.
- */
- if (!poolptr || !lsize) return(NULL);
- pool = (MemPool_Type *) poolptr;
-
- /*
- ** Allocations are forced to be paragraph sized.
- */
- lsize += sizeof(MemChain_Type); // Account for header.
- lsize = (lsize + 0x0FL) & 0xFFFFFFF0L;
- size = (int)(lsize >> 4);
- /*
- ** If the total free is less than the size of the desired allocation,
- ** then we KNOW that an allocation will fail -- just return.
- */
- if (pool->TotalMem < size) {
- return(NULL);
- }
- /*
- ** Walk down free chain looking for the first block that will
- ** accomodate the allocation.
- */
- node = pool->FreeChain;
- found = FALSE;
- while (!found && node) {
- /*
- ** Fetch free memory chunk block and see if it is big enough.
- */
- if (node->Size >= size) {
- found = TRUE;
- break;
- }
- node = node->Next;
- }
- if (!found) {
- return(NULL);
- }
- /*
- ** Determine if this allocation would split the block.
- */
- remainder = node->Size - size;
- /*
- ** If only a very small free chunk would remain, just tack it on
- ** to the current allocation.
- */
- if (remainder <= 2) {
- remainder = 0;
- size = node->Size;
- }
- /*
- ** Remove the primary block from the free memory list.
- */
- MemNode_Unlink(pool, TRUE, node);
- /*
- ** If a smaller block remains, then link it back into
- ** the free memory list.
- */
- if (remainder) {
- MemNode_Insert(pool, TRUE, (MemChain_Type *)Add_Long_To_Pointer(node, (long)size << 4), remainder, -1, FALSE);
- }
- /*
- ** Link in the allocated node into the used memory list.
- */
- MemNode_Insert(pool, FALSE, node, size, id, FALSE);
- /*
- ** Reflect the change to the total free count.
- */
- pool->FreeMem -= size;
- /*
- ** Return a pointer to the block of allocated memory just past
- ** the header.
- */
- #if DEBUG_FILL
- memset(node + 1, id, (size-1) << 4);
- #endif
- return((void *) (node + 1));
- }
- /***************************************************************************
- * Mem_Free -- Free a block of memory from system. *
- * *
- * This routine will free a block of memory from the special memory *
- * buffer. *
- * *
- * INPUT: poolptr -- Pointer to the memory pool base address. *
- * *
- * buffer -- Pointer to memory block to free. *
- * *
- * OUTPUT: TRUE/FALSE; Was the deallocation successful? *
- * *
- * WARNINGS: Be sure to only pass in to this routine a buffer that was *
- * returned from Mem_Alloc(). *
- * *
- * HISTORY: *
- * 03/31/1993 JLB : Created. *
- * 08/06/1993 JLB : Optimized for low memory caches. *
- * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, *
- * optimized for low memory only. *
- *=========================================================================*/
- int Mem_Free(void *poolptr, void *buffer)
- {
- MemPool_Type *pool; // pointer to structure.
- MemChain_Type *node; // Copy of current memory node.
- unsigned int size; // Size of the block being freed.
- /*
- ** One can't free what isn't there.
- */
- if (!buffer || !poolptr) {
- return(FALSE);
- }
- pool = (MemPool_Type *) poolptr;
- /*
- ** The node pointer is actually back a bit from the "normal" pointer.
- */
- node = (MemChain_Type *) buffer;
- node--;
- /*
- ** Get pointer to actual allocated node and unlink it from the used
- ** memory chain.
- */
- size = node->Size;
- MemNode_Unlink(pool, FALSE, node);
- MemNode_Insert(pool, TRUE, node, size, -1, TRUE);
- /*
- ** Reflect the new free memory into the total memory count.
- */
- pool->FreeMem += size;
- return(TRUE);
- }
- /***************************************************************************
- * Mem_Reference -- Updates the reference time for the specified memory blo*
- * *
- * This routine is used to update the memory reference time for the *
- * specified memory node. Typically, this is called every time a *
- * memory block is used in order to make sure the memory block time *
- * tracking (Last Recently Used) system works properly. *
- * *
- * INPUT: node -- Pointer to memory block returned from Mem_Find. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: The node pointer must be valid. For maximum safety this *
- * routine should be called right after Mem_Find(). *
- * *
- * HISTORY: *
- * 08/06/1993 JLB : Created. *
- * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, *
- * optimized for low memory only. *
- *=========================================================================*/
- void Mem_Reference(void *node)
- {
- MemChain_Type *nodeptr; // Pointer of current memory node.
- if (!node) return;
- // Get to the node header.
- nodeptr = (MemChain_Type *) node;
- nodeptr--;
- nodeptr->Time = (unsigned short)(TickCount.Time() >> 4);
- }
- /***************************************************************************
- * MEM_LOCK_BLOCK -- Locks a block so that it cannot be moved in cleanup. *
- * By marking a memory block in use, the memory system will never return*
- * it as the oldest memory block. It also makes it so that the block *
- * will never be moved during a Cleanup process. *
- * *
- * INPUT: node -- Pointer to memory block returned from Mem_Find. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: If one or more blocks are locked in a heap, Mem_Avail might *
- * not equal Mem_Largest_Avail after a call to Mem_Cleanup. *
- * *
- * HISTORY: *
- * 04/15/1994 SKB : Created. *
- *=========================================================================*/
- void Mem_Lock_Block(void *node)
- {
- MemChain_Type *nodeptr; // Pointer of current memory node.
- if (!node) return;
- // Get to the node header.
- nodeptr = (MemChain_Type *) node;
- nodeptr--;
- nodeptr->Time = MEM_BLOCK_LOCKED;
- }
- /***************************************************************************
- * MEM_IN_USE -- Makes it so a block will never be returned as oldest *
- * By marking a memory block in use, the memory system will never return*
- * it as the oldest memory block. It still can be moved in the Cleanup *
- * code. *
- * *
- * INPUT: node -- Pointer to memory block returned from Mem_Find. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: Mem_Find_Oldest() will return NULL if only IN_USE blocks are *
- * in memory. *
- * HISTORY: *
- * 04/15/1994 SKB : Created. *
- *=========================================================================*/
- void Mem_In_Use(void *node)
- {
- MemChain_Type *nodeptr; // Pointer of current memory node.
- if (!node) return;
- // Get to the node header.
- nodeptr = (MemChain_Type *) node - 1;
- nodeptr->Time = MEM_BLOCK_IN_USE;
- }
- /***************************************************************************
- * Mem_Find -- Returns with pointer to specified memory block. *
- * *
- * Use this routine to convert a memory ID value into an actual memory *
- * pointer. It sweeps through all of the 'cached' memory blocks and *
- * returns with the matching block pointer. *
- * *
- * INPUT: poolptr -- Pointer to the memory cache block. *
- * *
- * id -- The ID of the block desired. *
- * *
- * OUTPUT: Returns with the pointer to the memory block. If NULL is *
- * returned then the desired block is not in the memory cache. *
- * *
- * WARNINGS: This routine may return NULL if the memory block is not *
- * present in the cache. *
- * *
- * HISTORY: *
- * 08/06/1993 JLB : Created. *
- * 08/06/1993 JLB : Optimized for low memory caches. *
- * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, *
- * optimized for low memory only. *
- *=========================================================================*/
- void *Mem_Find(void *poolptr, unsigned long id)
- {
- MemPool_Type *pool; // pointer to structure.
- MemChain_Type *node; // Working node structure.
- if (!poolptr) return(NULL);
- pool = (MemPool_Type *) poolptr;
-
- /*
- ** Cannot free a node that is not on the UsedChain list.
- */
- if (!pool->UsedChain) {
- return(NULL);
- }
- /*
- ** Sweep through entire allocation chain to find
- ** the one with the matching ID.
- */
- node = pool->UsedChain;
- while (node) {
-
- if (node->ID == id) {
- return(node + 1);
- }
- node = node->Next;
- }
- return(NULL);
- }
- /***************************************************************************
- * MEM_GET_ID -- Returns ID of node. *
- * *
- * INPUT: void *node - pointer to node. *
- * *
- * OUTPUT: The ID of the node that was supplied by user during Mem_Alloc().*
- * *
- * WARNINGS: pointer to node must be one that Mem_Alloc or *
- * Mem_Find returned. **
- * *
- * HISTORY: *
- * 04/18/1994 SKB : Created. *
- *=========================================================================*/
- unsigned long Mem_Get_ID(void *node)
- {
- MemChain_Type *nodeptr; // Pointer of current memory node.
- if (!node) return (0L);
- // Get to the node header.
- nodeptr = (MemChain_Type *) node - 1;
- return (nodeptr->ID);
- }
- /***************************************************************************
- * Mem_Find_Oldest -- Returns with the memory block with the oldest time st*
- * *
- * Use this routine to find the memory block with the oldest time stamp *
- * value. Typically, this is used when freeing memory blocks in the *
- * cache in order to make room for a new memory block. *
- * *
- * INPUT: poolptr -- Pointer to the memory cache. *
- * *
- * OUTPUT: Returns with the pointer to the oldest memory block. If NULL *
- * is returned, then the memory cache is empty. *
- * *
- * WARNINGS: This routine could return NULL. *
- * *
- * HISTORY: *
- * 08/06/1993 JLB : Created. *
- * 08/06/1993 JLB : Optimized for low memory caches. *
- * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, *
- * optimized for low memory only. *
- * 04/15/1994 SKB : Handle time wrap, locked blocks, and no_refenece blocks*
- *=========================================================================*/
- void *Mem_Find_Oldest(void *poolptr)
- {
- MemChain_Type *node; // Working node pointer.
- MemChain_Type *oldnode; // Pointer to oldest block.
- unsigned int oldtime; // Time of oldest block.
- unsigned int basetime; // Time to mark our base time with.
- unsigned int time; // basetime + time of node.
- if (!poolptr) return(NULL);
- /*
- ** Sweep through entire allocation chain to find
- ** the oldest referenced memory block.
- */
- oldnode = NULL;
- oldtime = 0;
- node = ((MemPool_Type*) poolptr)->UsedChain;
- basetime = (unsigned int)(TickCount.Time() >> 4);
- while (node) {
- /*
- ** Don't allow MEM_BLOCK_IN_USE or MEM_BLOCK_LOCKED to be returned.
- */
- if (node->Time > MEM_BLOCK_LOCKED) {
- /*
- ** Adjust time for wrap around (after about 5 hrs).
- ** times less then the base time will wrap up high while
- ** and times greater then base time will then be lower since
- ** any time greater has been on the thing a long time.
- */
- time = node->Time - basetime ;
- if (time < oldtime || !oldnode) {
- oldtime = time;
- oldnode = node;
- }
- }
- node = node->Next;
- }
- /*
- ** Return with the value that matches the pointer that
- ** was allocated by the system previously.
- */
- if (oldnode) {
- oldnode++;
- }
- return(oldnode);
- }
- /***************************************************************************
- * Mem_Free_Oldest -- Find and free the oldest memory block. *
- * *
- * This routine is used to free the oldest memory block in the memory *
- * cache. This routine is typcially used in order to create more room *
- * in the cache for a new allocation. *
- * *
- * INPUT: poolptr -- Pointer to the memory cache. *
- * *
- * OUTPUT: Returns with the node that it freed. Although this node is *
- * is no longer valid, it may be used to mark that pointer as *
- * invalid in the main code. *
- * *
- * WARNINGS: If this routine returns NULL, then no memory was freed. *
- * *
- * HISTORY: *
- * 08/06/1993 JLB : Created. *
- * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, *
- * optimized for low memory only. *
- *=========================================================================*/
- void *Mem_Free_Oldest(void *poolptr)
- {
- MemChain_Type *node; // Copy of pointer to oldest node.
- if (!poolptr) return(NULL);
- node = (MemChain *) Mem_Find_Oldest(poolptr);
- if (Mem_Free(poolptr, node)) {
- return(node);
- }
- return(NULL);
- }
- /***************************************************************************
- * MEM_POOL_SIZE -- Returns total amount of memory in pool. *
- * *
- * INPUT: poolptr -- Pointer to the memory cache. *
- * *
- * OUTPUT: long total size of pool. i.e. largest possible allocation if *
- * no memory was allocated. *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 04/18/1994 SKB : Created. *
- *=========================================================================*/
- long Mem_Pool_Size(void *poolptr)
- {
- MemPool_Type *pool; // Memory pool control structure.
- long memtotal; // Total amount of memory free.
- if (!poolptr) return(NULL);
- pool = (MemPool_Type *) poolptr;
- memtotal = ((long)pool->TotalMem) << 4;
- memtotal -= sizeof(MemChain_Type);
- memtotal = MAX(memtotal, (long)0);
- return(memtotal);
- }
- /***************************************************************************
- * Mem_Avail -- Returns the amount of free memory available in the cache. *
- * *
- * This routine examines the memory cache and returns the amount of *
- * free memory available. This memory total MAY be fragmented but *
- * after Mem_Cleanup() is called, an allocation of the amount returned *
- * by this function is guaranteed. *
- * *
- * INPUT: poolptr -- Pointer to the memory cache. *
- * *
- * OUTPUT: Returns the largest allocation possible from the memory cache. *
- * *
- * WARNINGS: The value returned may represent the FRAGMENTED total *
- * amount of memory free in the cache. *
- * *
- * HISTORY: *
- * 08/06/1993 JLB : Created. *
- * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, *
- * optimized for low memory only. *
- *=========================================================================*/
- long Mem_Avail(void *poolptr)
- {
- MemPool_Type *pool; // Memory pool control structure.
- long memtotal; // Total amount of memory free.
- if (!poolptr) return(NULL);
- pool = (MemPool_Type *) poolptr;
- memtotal = ((long)pool->FreeMem) << 4;
- memtotal -= sizeof(MemChain_Type);
- //memtotal -= sizeof(MemChain_Type) + 15;
- memtotal = MAX(memtotal, (long)0);
- return(memtotal);
- }
- /***************************************************************************
- * MEM_LARGEST_AVAIL -- Largest free block available. *
- * This routine examines the free node list to find the largest block *
- * available. User can Mem_Alloc() this return size successfully. *
- * *
- * INPUT: poolptr -- Pointer to the memory cache. *
- * *
- * OUTPUT: Returns largest allocation currently possible from the cache. *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 04/15/1994 SKB : Created. *
- *=========================================================================*/
- long Mem_Largest_Avail(void *poolptr)
- {
- MemChain_Type *node; // Pointer to current memory node.
- unsigned int size;
- long truesize;
- /*
- ** Make sure that it is a buffer.
- */
- if (!poolptr) return(NULL);
- /*
- ** Go through the entire free chain looking for the largest block.
- */
- node = ((MemPool_Type *)poolptr)->FreeChain;
- size = 0;
- while (node) {
- /*
- ** Fetch free memory chunk block and see if it is big enough.
- */
- if (node->Size >= size) {
- size = node->Size;
- }
- node = node->Next;
- }
- truesize = (long)size << 4;
- truesize -= sizeof(MemChain_Type);
- truesize = MAX(truesize, 0L);
- return (truesize);
- }
- /***************************************************************************
- * Mem_Cleanup -- Performs a garbage collection on the memory cache. *
- * *
- * This routine is used to coalesce all adjacent free blocks of *
- * memory in the specified cache. As a result, all previous pointers *
- * provided by Mem_Find() are invalidated. This routine consumes a *
- * fair amount of time and should be called as infrequently as *
- * possible. *
- * *
- * INPUT: poolptr -- Pointer to the memory cache. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: This routine takes a significant amount of time! *
- * If there are locked block in memory, the pool may still *
- * be fragmented. *
- * *
- * HISTORY: *
- * 08/06/1993 JLB : Created. *
- * 08/06/1993 JLB : Updated for low memory caches. *
- * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, *
- * optimized for low memory only. *
- *=========================================================================*/
- void Mem_Cleanup(void *poolptr)
- {
- MemPool_Type *pool; // Memory pool control structure.
- MemChain_Type *free, // Pointer to first free area.
- *cur; // Pointer to first used block that is after free.
- unsigned long size;
- unsigned long freesize;// Size of free heap at the end of the block.
- if (!poolptr) return;
- /*
- ** Fetch working copy of pool control structure.
- */
- pool = (MemPool_Type *) poolptr;
- /*
- ** Basic parameter and condition legality checks. If the memory pool
- ** has no free space, no free blocks, or no allocated blocks, then
- ** memory cleanup is unnecessary -- just exit.
- */
- if (!pool->FreeMem || !pool->FreeChain || !pool->UsedChain) return;
- freesize = pool->FreeMem;
- free = pool->FreeChain;
- pool->FreeChain = NULL;
- cur = pool->UsedChain;
- while (TRUE) {
- /*
- ** Setup pointers so that free points to the first free block and cur
- ** points to the next used block after the free block.
- */
- while (cur < free && cur) {
- cur = cur->Next;
- }
- // All used blocks are at the front of the free. We are done.
- if (!cur) {
- break;
- }
- /*
- ** Do not allow a locked block to be moved.
- */
- if (cur->Time == MEM_BLOCK_LOCKED) {
- /*
- ** Figure the size of the new free block that we are creating.
- ** Subtract off the total block size.
- ** Add the node to the free list.
- */
- size = (char *) cur - (char *) free;
- size >>= 4;
- freesize -= size;
- MemNode_Insert(pool, TRUE, free, (unsigned int) size, -1, FALSE);
- /*
- ** Time to find a new free position to start working from.
- ** Cur will be in the position just following.
- */
- free = (MemChain_Type *) Add_Long_To_Pointer(cur, (unsigned long)cur->Size << 4);
- cur = cur->Next;
- while (free == cur) {
- free = (MemChain_Type *) Add_Long_To_Pointer(cur, (unsigned long)cur->Size << 4);
- cur = cur->Next;
- }
- // All used blocks are at the front of the free. We are done.
- if (!cur) {
- break;
- }
- } else {
- // Copy the block up.
- size = (unsigned long)cur->Size << 4;
- Mem_Copy(cur, free, size);
- cur = free;
- // Change pointers of surrounding blocks.
- if (cur->Next) {
- cur->Next->Prev = cur;
- }
- if (cur->Prev) {
- cur->Prev->Next = cur;
- } else {
- pool->UsedChain = cur;
- }
- // Change to next new free area.
- free = (MemChain_Type *) Add_Long_To_Pointer(cur, size);
- }
- }
- /*
- ** Now build the single free chunk.
- */
- MemNode_Insert(pool, TRUE, free, freesize, -1, FALSE);
- }
- /***************************************************************************
- * MemNode_Unlink -- Unlinks a node from the cache. *
- * *
- * A private routine the actually unlinks a memory block from the *
- * memory cache. It doesn't perform a complete update of the memory *
- * cache. *
- * *
- * INPUT: pool -- Pointer to the memory cache header (copy in real *
- * memory). *
- * *
- * freechain-- Is the block part of the free memory chain? *
- * *
- * node -- Pointer to the node that will be unlinked. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: This routine doesn't update memory totals. It is a support *
- * function. *
- * *
- * HISTORY: *
- * 08/06/1993 JLB : Created. *
- * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, *
- * optimized for low memory only. *
- *=========================================================================*/
- PRIVATE void MemNode_Unlink(MemPool_Type *pool, int freechain, MemChain_Type *node)
- {
- MemChain_Type *other; // Copy of node data to unlink.
- MemChain_Type **chain; // A pointer to one of the chains pointer.
- /*
- ** Check for parameter validity.
- */
- if (!pool || !node) return;
-
- /*
- ** Setup working pointer for the particular chain desired.
- */
- if (freechain) {
- chain = &pool->FreeChain;
- } else {
- chain = &pool->UsedChain;
- }
-
- /*
- ** Make adjustments to the previous node. If the pointer
- ** to the previous node is NULL then this indicates the
- ** first node in the list and thus the chain pointer needs
- ** to be updated instead.
- */
- if (node->Prev) {
- other = node->Prev;
- other->Next = node->Next;
- } else {
- *chain = node->Next;
- }
- if (node->Next) {
- other = node->Next;
- other->Prev = node->Prev;
- }
- }
- /***************************************************************************
- * MemNode_Insert -- Inserts a node into a cache chain. *
- * *
- * This routine is used to add a node to a cache chain. Since nodes *
- * do not contain double links, they must be placed in sequence. *
- * *
- * INPUT: pool -- Pointer to memory pool (must be in real memory). *
- * *
- * freechain-- Is the node to be inserted into the free chain? *
- * *
- * node -- Pointer to the node to insert. *
- * *
- * size -- Size of the memory block (in paragraphs). *
- * *
- * id -- The ID number to associate with this block. *
- * *
- * merge -- Merge inserted block with adjacent blocks. *
- * *
- * OUTPUT: return *
- * *
- * WARNINGS: This is a support routine. *
- * *
- * HISTORY: *
- * 08/06/1993 JLB : Created. *
- *=========================================================================*/
- PRIVATE void MemNode_Insert(MemPool_Type *pool, int freechain, MemChain_Type *node, unsigned int size, unsigned long id, int merge)
- {
- MemChain_Type **chain; // Pointer to chain that will be linked.
- MemChain_Type *prev, // Successor node pointer.
- *next; // Predecessor node pointer.
- int doit=TRUE; // Link the node into the list.
- /*
- ** Determine if the parameters are valid.
- */
- if (!pool || !node || !size) return;
- /*
- ** Setup working pointer for the particular chain desired.
- */
- if (freechain) {
- chain = &pool->FreeChain;
- } else {
- chain = &pool->UsedChain;
- }
- /*
- ** Handle the "no node in list" condition (easiest).
- */
- if (!*chain) {
- node->Next = NULL;
- node->Prev = NULL;
- node->Size = size;
- node->Time = (unsigned short)(TickCount.Time() >> 4);
- node->ID = id;
- *chain = node;
- return;
- }
- /*
- ** Sweep through the memory chain looking for a likely spot
- ** to insert the new node. It will stop with "next" pointing
- ** to the node to come after the block to be inserted and "prev"
- ** will point to the node right before.
- */
- prev = NULL;
- next = *chain;
- while (next && (next < node)) {
- /*
- ** Move up the memory chain.
- */
- prev = next;
- next = next->Next;
- }
- /*
- ** Coallescing of adjacent blocks (if requested).
- */
- if (merge) {
-
- /*
- ** If the previous block is touching the block to insert
- ** then merely adjust the size of the previous block and
- ** that is all that is necessary.
- */
- if (prev) {
- if (((char *)prev + ((long)prev->Size << 4)) == ((char *) node)) {
- prev->Size += size;
- size = prev->Size;
- node = prev;
- prev = prev->Prev;
- doit = FALSE;
- }
- }
- /*
- ** If the following block is touching the block to insert
- ** then remove the following block and increase the size of
- ** the original insertion block by the size of the other
- ** block.
- */
- if (next) {
- if (((char *)node + ((long)size << 4)) == (char *)next) {
-
- if (!doit) {
- /*
- ** If the node was already merged with the previous block
- ** then merely increase the previous block's size
- ** and adjust it's next pointer appropriately.
- */
- node->Size += next->Size;
- node->Next = next->Next;
- next = next->Next;
- } else {
- /*
- ** Increase the size of the current block and adjust
- ** the "next" pointer so that it gets fixed up
- ** accordingly.
- */
- size += next->Size;
- next = next->Next;
- }
- }
- }
- }
- #if DEBUG_FILL
- if (doit) {
- memset(node + 1, 0xFF, (size - 1) << 4);
- } else {
- memset(node + 1, 0xFF, (node->Size - 1) << 4);
- }
- #endif
- /*
- ** Fixup the node pointers.
- */
- if (prev) {
- prev->Next = node;
- }else{
- *chain = node;
- }
- if (next) {
- next->Prev = node;
- }
- if (doit) {
- node->Prev = prev;
- node->Next = next;
- node->Size = size;
- node->Time = (unsigned short)(TickCount.Time() >> 4);
- node->ID = id;
- }
- }
|