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