doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Heap.cpp
Go to the documentation of this file.
1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
8 
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 #include "../idlib/precompiled.h"
30 #pragma hdrstop
31 
32 #ifndef USE_LIBC_MALLOC
33  #define USE_LIBC_MALLOC 0
34 #endif
35 
36 #ifndef CRASH_ON_STATIC_ALLOCATION
37 // #define CRASH_ON_STATIC_ALLOCATION
38 #endif
39 
40 //===============================================================
41 //
42 // idHeap
43 //
44 //===============================================================
45 
46 #define SMALL_HEADER_SIZE ( (int) ( sizeof( byte ) + sizeof( byte ) ) )
47 #define MEDIUM_HEADER_SIZE ( (int) ( sizeof( mediumHeapEntry_s ) + sizeof( byte ) ) )
48 #define LARGE_HEADER_SIZE ( (int) ( sizeof( dword * ) + sizeof( byte ) ) )
49 
50 #define ALIGN_SIZE( bytes ) ( ( (bytes) + ALIGN - 1 ) & ~(ALIGN - 1) )
51 #define SMALL_ALIGN( bytes ) ( ALIGN_SIZE( (bytes) + SMALL_HEADER_SIZE ) - SMALL_HEADER_SIZE )
52 #define MEDIUM_SMALLEST_SIZE ( ALIGN_SIZE( 256 ) + ALIGN_SIZE( MEDIUM_HEADER_SIZE ) )
53 
54 
55 class idHeap {
56 
57 public:
58  idHeap( void );
59  ~idHeap( void ); // frees all associated data
60  void Init( void ); // initialize
61  void * Allocate( const dword bytes ); // allocate memory
62  void Free( void *p ); // free memory
63  void * Allocate16( const dword bytes );// allocate 16 byte aligned memory
64  void Free16( void *p ); // free 16 byte aligned memory
65  dword Msize( void *p ); // return size of data block
66  void Dump( void );
67 
68  void AllocDefragBlock( void ); // hack for huge renderbumps
69 
70 private:
71 
72  enum {
73  ALIGN = 8 // memory alignment in bytes
74  };
75 
76  enum {
77  INVALID_ALLOC = 0xdd,
78  SMALL_ALLOC = 0xaa, // small allocation
79  MEDIUM_ALLOC = 0xbb, // medium allocaction
80  LARGE_ALLOC = 0xcc // large allocaction
81  };
82 
83  struct page_s { // allocation page
84  void * data; // data pointer to allocated memory
85  dword dataSize; // number of bytes of memory 'data' points to
86  page_s * next; // next free page in same page manager
87  page_s * prev; // used only when allocated
88  dword largestFree; // this data used by the medium-size heap manager
89  void * firstFree; // pointer to first free entry
90  };
91 
93  page_s * page; // pointer to page
94  dword size; // size of block
95  mediumHeapEntry_s * prev; // previous block
96  mediumHeapEntry_s * next; // next block
97  mediumHeapEntry_s * prevFree; // previous free block
98  mediumHeapEntry_s * nextFree; // next free block
99  dword freeBlock; // non-zero if free block
100  };
101 
102  // variables
103  void * smallFirstFree[256/ALIGN+1]; // small heap allocator lists (for allocs of 1-255 bytes)
104  page_s * smallCurPage; // current page for small allocations
105  dword smallCurPageOffset; // byte offset in current page
106  page_s * smallFirstUsedPage; // first used page of the small heap manager
107 
108  page_s * mediumFirstFreePage; // first partially free page
109  page_s * mediumLastFreePage; // last partially free page
110  page_s * mediumFirstUsedPage; // completely used page
111 
112  page_s * largeFirstUsedPage; // first page used by the large heap manager
113 
115 
116  dword pagesAllocated; // number of pages currently allocated
117  dword pageSize; // size of one alloc page in bytes
118 
119  dword pageRequests; // page requests
120  dword OSAllocs; // number of allocs made to the OS
121 
123 
124  void *defragBlock; // a single huge block that can be allocated
125  // at startup, then freed when needed
126 
127  // methods
128  page_s * AllocatePage( dword bytes ); // allocate page from the OS
129  void FreePage( idHeap::page_s *p ); // free an OS allocated page
130 
131  void * SmallAllocate( dword bytes ); // allocate memory (1-255 bytes) from small heap manager
132  void SmallFree( void *ptr ); // free memory allocated by small heap manager
133 
134  void * MediumAllocateFromPage( idHeap::page_s *p, dword sizeNeeded );
135  void * MediumAllocate( dword bytes ); // allocate memory (256-32768 bytes) from medium heap manager
136  void MediumFree( void *ptr ); // free memory allocated by medium heap manager
137 
138  void * LargeAllocate( dword bytes ); // allocate large block from OS directly
139  void LargeFree( void *ptr ); // free memory allocated by large heap manager
140 
141  void ReleaseSwappedPages( void );
142  void FreePageReal( idHeap::page_s *p );
143 };
144 
145 
146 /*
147 ================
148 idHeap::Init
149 ================
150 */
151 void idHeap::Init () {
152  OSAllocs = 0;
153  pageRequests = 0;
154  pageSize = 65536 - sizeof( idHeap::page_s );
155  pagesAllocated = 0; // reset page allocation counter
156 
157  largeFirstUsedPage = NULL; // init large heap manager
158  swapPage = NULL;
159 
160  memset( smallFirstFree, 0, sizeof(smallFirstFree) ); // init small heap manager
163  assert( smallCurPage );
165 
166  defragBlock = NULL;
167 
168  mediumFirstFreePage = NULL; // init medium heap manager
171 
173 }
174 
175 /*
176 ================
177 idHeap::idHeap
178 ================
179 */
180 idHeap::idHeap( void ) {
181  Init();
182 }
183 
184 /*
185 ================
186 idHeap::~idHeap
187 
188  returns all allocated memory back to OS
189 ================
190 */
192 
193  idHeap::page_s *p;
194 
195  if ( smallCurPage ) {
196  FreePage( smallCurPage ); // free small-heap current allocation page
197  }
198  p = smallFirstUsedPage; // free small-heap allocated pages
199  while( p ) {
200  idHeap::page_s *next = p->next;
201  FreePage( p );
202  p= next;
203  }
204 
205  p = largeFirstUsedPage; // free large-heap allocated pages
206  while( p ) {
207  idHeap::page_s *next = p->next;
208  FreePage( p );
209  p = next;
210  }
211 
212  p = mediumFirstFreePage; // free medium-heap allocated pages
213  while( p ) {
214  idHeap::page_s *next = p->next;
215  FreePage( p );
216  p = next;
217  }
218 
219  p = mediumFirstUsedPage; // free medium-heap allocated completely used pages
220  while( p ) {
221  idHeap::page_s *next = p->next;
222  FreePage( p );
223  p = next;
224  }
225 
227 
228  if ( defragBlock ) {
229  free( defragBlock );
230  }
231 
232  assert( pagesAllocated == 0 );
233 }
234 
235 /*
236 ================
237 idHeap::AllocDefragBlock
238 ================
239 */
241  int size = 0x40000000;
242 
243  if ( defragBlock ) {
244  return;
245  }
246  while( 1 ) {
247  defragBlock = malloc( size );
248  if ( defragBlock ) {
249  break;
250  }
251  size >>= 1;
252  }
253  idLib::common->Printf( "Allocated a %i mb defrag block\n", size / (1024*1024) );
254 }
255 
256 /*
257 ================
258 idHeap::Allocate
259 ================
260 */
261 void *idHeap::Allocate( const dword bytes ) {
262  if ( !bytes ) {
263  return NULL;
264  }
266 
267 #if USE_LIBC_MALLOC
268  return malloc( bytes );
269 #else
270  if ( !(bytes & ~255) ) {
271  return SmallAllocate( bytes );
272  }
273  if ( !(bytes & ~32767) ) {
274  return MediumAllocate( bytes );
275  }
276  return LargeAllocate( bytes );
277 #endif
278 }
279 
280 /*
281 ================
282 idHeap::Free
283 ================
284 */
285 void idHeap::Free( void *p ) {
286  if ( !p ) {
287  return;
288  }
290 
291 #if USE_LIBC_MALLOC
292  free( p );
293 #else
294  switch( ((byte *)(p))[-1] ) {
295  case SMALL_ALLOC: {
296  SmallFree( p );
297  break;
298  }
299  case MEDIUM_ALLOC: {
300  MediumFree( p );
301  break;
302  }
303  case LARGE_ALLOC: {
304  LargeFree( p );
305  break;
306  }
307  default: {
308  idLib::common->FatalError( "idHeap::Free: invalid memory block (%s)", idLib::sys->GetCallStackCurStr( 4 ) );
309  break;
310  }
311  }
312 #endif
313 }
314 
315 /*
316 ================
317 idHeap::Allocate16
318 ================
319 */
320 void *idHeap::Allocate16( const dword bytes ) {
321  byte *ptr, *alignedPtr;
322 
323  ptr = (byte *) malloc( bytes + 16 + 4 );
324  if ( !ptr ) {
325  if ( defragBlock ) {
326  idLib::common->Printf( "Freeing defragBlock on alloc of %i.\n", bytes );
327  free( defragBlock );
328  defragBlock = NULL;
329  ptr = (byte *) malloc( bytes + 16 + 4 );
331  }
332  if ( !ptr ) {
333  common->FatalError( "malloc failure for %i", bytes );
334  }
335  }
336  alignedPtr = (byte *) ( ( (int) ptr ) + 15 & ~15 );
337  if ( alignedPtr - ptr < 4 ) {
338  alignedPtr += 16;
339  }
340  *((int *)(alignedPtr - 4)) = (int) ptr;
341  return (void *) alignedPtr;
342 }
343 
344 /*
345 ================
346 idHeap::Free16
347 ================
348 */
349 void idHeap::Free16( void *p ) {
350  free( (void *) *((int *) (( (byte *) p ) - 4)) );
351 }
352 
353 /*
354 ================
355 idHeap::Msize
356 
357  returns size of allocated memory block
358  p = pointer to memory block
359  Notes: size may not be the same as the size in the original
360  allocation request (due to block alignment reasons).
361 ================
362 */
364 
365  if ( !p ) {
366  return 0;
367  }
368 
369 #if USE_LIBC_MALLOC
370  #ifdef _WIN32
371  return _msize( p );
372  #else
373  return 0;
374  #endif
375 #else
376  switch( ((byte *)(p))[-1] ) {
377  case SMALL_ALLOC: {
378  return SMALL_ALIGN( ((byte *)(p))[-SMALL_HEADER_SIZE] * ALIGN );
379  }
380  case MEDIUM_ALLOC: {
381  return ((mediumHeapEntry_s *)(((byte *)(p)) - ALIGN_SIZE( MEDIUM_HEADER_SIZE )))->size - ALIGN_SIZE( MEDIUM_HEADER_SIZE );
382  }
383  case LARGE_ALLOC: {
384  return ((idHeap::page_s*)(*((dword *)(((byte *)p) - ALIGN_SIZE( LARGE_HEADER_SIZE )))))->dataSize - ALIGN_SIZE( LARGE_HEADER_SIZE );
385  }
386  default: {
387  idLib::common->FatalError( "idHeap::Msize: invalid memory block (%s)", idLib::sys->GetCallStackCurStr( 4 ) );
388  return 0;
389  }
390  }
391 #endif
392 }
393 
394 /*
395 ================
396 idHeap::Dump
397 
398  dump contents of the heap
399 ================
400 */
401 void idHeap::Dump( void ) {
402  idHeap::page_s *pg;
403 
404  for ( pg = smallFirstUsedPage; pg; pg = pg->next ) {
405  idLib::common->Printf( "%p bytes %-8d (in use by small heap)\n", pg->data, pg->dataSize);
406  }
407 
408  if ( smallCurPage ) {
409  pg = smallCurPage;
410  idLib::common->Printf( "%p bytes %-8d (small heap active page)\n", pg->data, pg->dataSize );
411  }
412 
413  for ( pg = mediumFirstUsedPage; pg; pg = pg->next ) {
414  idLib::common->Printf( "%p bytes %-8d (completely used by medium heap)\n", pg->data, pg->dataSize );
415  }
416 
417  for ( pg = mediumFirstFreePage; pg; pg = pg->next ) {
418  idLib::common->Printf( "%p bytes %-8d (partially used by medium heap)\n", pg->data, pg->dataSize );
419  }
420 
421  for ( pg = largeFirstUsedPage; pg; pg = pg->next ) {
422  idLib::common->Printf( "%p bytes %-8d (fully used by large heap)\n", pg->data, pg->dataSize );
423  }
424 
425  idLib::common->Printf( "pages allocated : %d\n", pagesAllocated );
426 }
427 
428 /*
429 ================
430 idHeap::FreePageReal
431 
432  frees page to be used by the OS
433  p = page to free
434 ================
435 */
437  assert( p );
438  ::free( p );
439 }
440 
441 /*
442 ================
443 idHeap::ReleaseSwappedPages
444 
445  releases the swap page to OS
446 ================
447 */
449  if ( swapPage ) {
451  }
452  swapPage = NULL;
453 }
454 
455 /*
456 ================
457 idHeap::AllocatePage
458 
459  allocates memory from the OS
460  bytes = page size in bytes
461  returns pointer to page
462 ================
463 */
465  idHeap::page_s* p;
466 
467  pageRequests++;
468 
469  if ( swapPage && swapPage->dataSize == bytes ) { // if we've got a swap page somewhere
470  p = swapPage;
471  swapPage = NULL;
472  }
473  else {
474  dword size;
475 
476  size = bytes + sizeof(idHeap::page_s);
477 
478  p = (idHeap::page_s *) ::malloc( size + ALIGN - 1 );
479  if ( !p ) {
480  if ( defragBlock ) {
481  idLib::common->Printf( "Freeing defragBlock on alloc of %i.\n", size + ALIGN - 1 );
482  free( defragBlock );
483  defragBlock = NULL;
484  p = (idHeap::page_s *) ::malloc( size + ALIGN - 1 );
486  }
487  if ( !p ) {
488  common->FatalError( "malloc failure for %i", bytes );
489  }
490  }
491 
492  p->data = (void *) ALIGN_SIZE( (int)((byte *)(p)) + sizeof( idHeap::page_s ) );
493  p->dataSize = size - sizeof(idHeap::page_s);
494  p->firstFree = NULL;
495  p->largestFree = 0;
496  OSAllocs++;
497  }
498 
499  p->prev = NULL;
500  p->next = NULL;
501 
502  pagesAllocated++;
503 
504  return p;
505 }
506 
507 /*
508 ================
509 idHeap::FreePage
510 
511  frees a page back to the operating system
512  p = pointer to page
513 ================
514 */
516  assert( p );
517 
518  if ( p->dataSize == pageSize && !swapPage ) { // add to swap list?
519  swapPage = p;
520  }
521  else {
522  FreePageReal( p );
523  }
524 
525  pagesAllocated--;
526 }
527 
528 //===============================================================
529 //
530 // small heap code
531 //
532 //===============================================================
533 
534 /*
535 ================
536 idHeap::SmallAllocate
537 
538  allocate memory (1-255 bytes) from the small heap manager
539  bytes = number of bytes to allocate
540  returns pointer to allocated memory
541 ================
542 */
543 void *idHeap::SmallAllocate( dword bytes ) {
544  // we need the at least sizeof( dword ) bytes for the free list
545  if ( bytes < sizeof( dword ) ) {
546  bytes = sizeof( dword );
547  }
548 
549  // increase the number of bytes if necessary to make sure the next small allocation is aligned
550  bytes = SMALL_ALIGN( bytes );
551 
552  byte *smallBlock = (byte *)(smallFirstFree[bytes / ALIGN]);
553  if ( smallBlock ) {
554  dword *link = (dword *)(smallBlock + SMALL_HEADER_SIZE);
555  smallBlock[1] = SMALL_ALLOC; // allocation identifier
556  smallFirstFree[bytes / ALIGN] = (void *)(*link);
557  return (void *)(link);
558  }
559 
560  dword bytesLeft = (long)(pageSize) - smallCurPageOffset;
561  // if we need to allocate a new page
562  if ( bytes >= bytesLeft ) {
563 
567  if ( !smallCurPage ) {
568  return NULL;
569  }
570  // make sure the first allocation is aligned
572  }
573 
574  smallBlock = ((byte *)smallCurPage->data) + smallCurPageOffset;
575  smallBlock[0] = (byte)(bytes / ALIGN); // write # of bytes/ALIGN
576  smallBlock[1] = SMALL_ALLOC; // allocation identifier
577  smallCurPageOffset += bytes + SMALL_HEADER_SIZE; // increase the offset on the current page
578  return ( smallBlock + SMALL_HEADER_SIZE ); // skip the first two bytes
579 }
580 
581 /*
582 ================
583 idHeap::SmallFree
584 
585  frees a block of memory allocated by SmallAllocate() call
586  data = pointer to block of memory
587 ================
588 */
589 void idHeap::SmallFree( void *ptr ) {
590  ((byte *)(ptr))[-1] = INVALID_ALLOC;
591 
592  byte *d = ( (byte *)ptr ) - SMALL_HEADER_SIZE;
593  dword *dt = (dword *)ptr;
594  // index into the table with free small memory blocks
595  dword ix = *d;
596 
597  // check if the index is correct
598  if ( ix > (256 / ALIGN) ) {
599  idLib::common->FatalError( "SmallFree: invalid memory block" );
600  }
601 
602  *dt = (dword)smallFirstFree[ix]; // write next index
603  smallFirstFree[ix] = (void *)d; // link
604 }
605 
606 //===============================================================
607 //
608 // medium heap code
609 //
610 // Medium-heap allocated pages not returned to OS until heap destructor
611 // called (re-used instead on subsequent medium-size malloc requests).
612 //
613 //===============================================================
614 
615 /*
616 ================
617 idHeap::MediumAllocateFromPage
618 
619  performs allocation using the medium heap manager from a given page
620  p = page
621  sizeNeeded = # of bytes needed
622  returns pointer to allocated memory
623 ================
624 */
626 
627  mediumHeapEntry_s *best,*nw = NULL;
628  byte *ret;
629 
630  best = (mediumHeapEntry_s *)(p->firstFree); // first block is largest
631 
632  assert( best );
633  assert( best->size == p->largestFree );
634  assert( best->size >= sizeNeeded );
635 
636  // if we can allocate another block from this page after allocating sizeNeeded bytes
637  if ( best->size >= (dword)( sizeNeeded + MEDIUM_SMALLEST_SIZE ) ) {
638  nw = (mediumHeapEntry_s *)((byte *)best + best->size - sizeNeeded);
639  nw->page = p;
640  nw->prev = best;
641  nw->next = best->next;
642  nw->prevFree = NULL;
643  nw->nextFree = NULL;
644  nw->size = sizeNeeded;
645  nw->freeBlock = 0; // used block
646  if ( best->next ) {
647  best->next->prev = nw;
648  }
649  best->next = nw;
650  best->size -= sizeNeeded;
651 
652  p->largestFree = best->size;
653  }
654  else {
655  if ( best->prevFree ) {
656  best->prevFree->nextFree = best->nextFree;
657  }
658  else {
659  p->firstFree = (void *)best->nextFree;
660  }
661  if ( best->nextFree ) {
662  best->nextFree->prevFree = best->prevFree;
663  }
664 
665  best->prevFree = NULL;
666  best->nextFree = NULL;
667  best->freeBlock = 0; // used block
668  nw = best;
669 
670  p->largestFree = 0;
671  }
672 
673  ret = (byte *)(nw) + ALIGN_SIZE( MEDIUM_HEADER_SIZE );
674  ret[-1] = MEDIUM_ALLOC; // allocation identifier
675 
676  return (void *)(ret);
677 }
678 
679 /*
680 ================
681 idHeap::MediumAllocate
682 
683  allocate memory (256-32768 bytes) from medium heap manager
684  bytes = number of bytes to allocate
685  returns pointer to allocated memory
686 ================
687 */
689  idHeap::page_s *p;
690  void *data;
691 
692  dword sizeNeeded = ALIGN_SIZE( bytes ) + ALIGN_SIZE( MEDIUM_HEADER_SIZE );
693 
694  // find first page with enough space
695  for ( p = mediumFirstFreePage; p; p = p->next ) {
696  if ( p->largestFree >= sizeNeeded ) {
697  break;
698  }
699  }
700 
701  if ( !p ) { // need to allocate new page?
702  p = AllocatePage( pageSize );
703  if ( !p ) {
704  return NULL; // malloc failure!
705  }
706  p->prev = NULL;
708  if (p->next) {
709  p->next->prev = p;
710  }
711  else {
713  }
714 
716 
717  p->largestFree = pageSize;
718  p->firstFree = (void *)p->data;
719 
721  e = (mediumHeapEntry_s *)(p->firstFree);
722  e->page = p;
723  // make sure ((byte *)e + e->size) is aligned
724  e->size = pageSize & ~(ALIGN - 1);
725  e->prev = NULL;
726  e->next = NULL;
727  e->prevFree = NULL;
728  e->nextFree = NULL;
729  e->freeBlock = 1;
730  }
731 
732  data = MediumAllocateFromPage( p, sizeNeeded ); // allocate data from page
733 
734  // if the page can no longer serve memory, move it away from free list
735  // (so that it won't slow down the later alloc queries)
736  // this modification speeds up the pageWalk from O(N) to O(sqrt(N))
737  // a call to free may swap this page back to the free list
738 
739  if ( p->largestFree < MEDIUM_SMALLEST_SIZE ) {
740  if ( p == mediumLastFreePage ) {
742  }
743 
744  if ( p == mediumFirstFreePage ) {
746  }
747 
748  if ( p->prev ) {
749  p->prev->next = p->next;
750  }
751  if ( p->next ) {
752  p->next->prev = p->prev;
753  }
754 
755  // link to "completely used" list
756  p->prev = NULL;
758  if ( p->next ) {
759  p->next->prev = p;
760  }
762  return data;
763  }
764 
765  // re-order linked list (so that next malloc query starts from current
766  // matching block) -- this speeds up both the page walks and block walks
767 
768  if ( p != mediumFirstFreePage ) {
771  assert( p->prev);
772 
776  p->prev->next = NULL;
777  p->prev = NULL;
779  }
780 
781  return data;
782 }
783 
784 /*
785 ================
786 idHeap::MediumFree
787 
788  frees a block allocated by the medium heap manager
789  ptr = pointer to data block
790 ================
791 */
792 void idHeap::MediumFree( void *ptr ) {
793  ((byte *)(ptr))[-1] = INVALID_ALLOC;
794 
796  idHeap::page_s *p = e->page;
797  bool isInFreeList;
798 
799  isInFreeList = p->largestFree >= MEDIUM_SMALLEST_SIZE;
800 
801  assert( e->size );
802  assert( e->freeBlock == 0 );
803 
804  mediumHeapEntry_s *prev = e->prev;
805 
806  // if the previous block is free we can merge
807  if ( prev && prev->freeBlock ) {
808  prev->size += e->size;
809  prev->next = e->next;
810  if ( e->next ) {
811  e->next->prev = prev;
812  }
813  e = prev;
814  }
815  else {
816  e->prevFree = NULL; // link to beginning of free list
818  if ( e->nextFree ) {
819  assert( !(e->nextFree->prevFree) );
820  e->nextFree->prevFree = e;
821  }
822 
823  p->firstFree = e;
824  p->largestFree = e->size;
825  e->freeBlock = 1; // mark block as free
826  }
827 
828  mediumHeapEntry_s *next = e->next;
829 
830  // if the next block is free we can merge
831  if ( next && next->freeBlock ) {
832  e->size += next->size;
833  e->next = next->next;
834 
835  if ( next->next ) {
836  next->next->prev = e;
837  }
838 
839  if ( next->prevFree ) {
840  next->prevFree->nextFree = next->nextFree;
841  }
842  else {
843  assert( next == p->firstFree );
844  p->firstFree = next->nextFree;
845  }
846 
847  if ( next->nextFree ) {
848  next->nextFree->prevFree = next->prevFree;
849  }
850  }
851 
852  if ( p->firstFree ) {
854  }
855  else {
856  p->largestFree = 0;
857  }
858 
859  // did e become the largest block of the page ?
860 
861  if ( e->size > p->largestFree ) {
862  assert( e != p->firstFree );
863  p->largestFree = e->size;
864 
865  if ( e->prevFree ) {
866  e->prevFree->nextFree = e->nextFree;
867  }
868  if ( e->nextFree ) {
869  e->nextFree->prevFree = e->prevFree;
870  }
871 
873  e->prevFree = NULL;
874  if ( e->nextFree ) {
875  e->nextFree->prevFree = e;
876  }
877  p->firstFree = e;
878  }
879 
880  // if page wasn't in free list (because it was near-full), move it back there
881  if ( !isInFreeList ) {
882 
883  // remove from "completely used" list
884  if ( p->prev ) {
885  p->prev->next = p->next;
886  }
887  if ( p->next ) {
888  p->next->prev = p->prev;
889  }
890  if ( p == mediumFirstUsedPage ) {
892  }
893 
894  p->next = NULL;
896 
897  if ( mediumLastFreePage ) {
899  }
901  if ( !mediumFirstFreePage ) {
903  }
904  }
905 }
906 
907 //===============================================================
908 //
909 // large heap code
910 //
911 //===============================================================
912 
913 /*
914 ================
915 idHeap::LargeAllocate
916 
917  allocates a block of memory from the operating system
918  bytes = number of bytes to allocate
919  returns pointer to allocated memory
920 ================
921 */
922 void *idHeap::LargeAllocate( dword bytes ) {
924 
925  assert( p );
926 
927  if ( !p ) {
928  return NULL;
929  }
930 
931  byte * d = (byte*)(p->data) + ALIGN_SIZE( LARGE_HEADER_SIZE );
932  dword * dw = (dword*)(d - ALIGN_SIZE( LARGE_HEADER_SIZE ));
933  dw[0] = (dword)p; // write pointer back to page table
934  d[-1] = LARGE_ALLOC; // allocation identifier
935 
936  // link to 'large used page list'
937  p->prev = NULL;
939  if ( p->next ) {
940  p->next->prev = p;
941  }
943 
944  return (void *)(d);
945 }
946 
947 /*
948 ================
949 idHeap::LargeFree
950 
951  frees a block of memory allocated by the 'large memory allocator'
952  p = pointer to allocated memory
953 ================
954 */
955 void idHeap::LargeFree( void *ptr) {
956  idHeap::page_s* pg;
957 
958  ((byte *)(ptr))[-1] = INVALID_ALLOC;
959 
960  // get page pointer
961  pg = (idHeap::page_s *)(*((dword *)(((byte *)ptr) - ALIGN_SIZE( LARGE_HEADER_SIZE ))));
962 
963  // unlink from doubly linked list
964  if ( pg->prev ) {
965  pg->prev->next = pg->next;
966  }
967  if ( pg->next ) {
968  pg->next->prev = pg->prev;
969  }
970  if ( pg == largeFirstUsedPage ) {
971  largeFirstUsedPage = pg->next;
972  }
973  pg->next = pg->prev = NULL;
974 
975  FreePage(pg);
976 }
977 
978 //===============================================================
979 //
980 // memory allocation all in one place
981 //
982 //===============================================================
983 
984 #undef new
985 
986 static idHeap * mem_heap = NULL;
987 static memoryStats_t mem_total_allocs = { 0, 0x0fffffff, -1, 0 };
988 static memoryStats_t mem_frame_allocs;
989 static memoryStats_t mem_frame_frees;
990 
991 /*
992 ==================
993 Mem_ClearFrameStats
994 ==================
995 */
996 void Mem_ClearFrameStats( void ) {
997  mem_frame_allocs.num = mem_frame_frees.num = 0;
998  mem_frame_allocs.minSize = mem_frame_frees.minSize = 0x0fffffff;
999  mem_frame_allocs.maxSize = mem_frame_frees.maxSize = -1;
1000  mem_frame_allocs.totalSize = mem_frame_frees.totalSize = 0;
1001 }
1002 
1003 /*
1004 ==================
1005 Mem_GetFrameStats
1006 ==================
1007 */
1009  allocs = mem_frame_allocs;
1010  frees = mem_frame_frees;
1011 }
1012 
1013 /*
1014 ==================
1015 Mem_GetStats
1016 ==================
1017 */
1018 void Mem_GetStats( memoryStats_t &stats ) {
1019  stats = mem_total_allocs;
1020 }
1021 
1022 /*
1023 ==================
1024 Mem_UpdateStats
1025 ==================
1026 */
1027 void Mem_UpdateStats( memoryStats_t &stats, int size ) {
1028  stats.num++;
1029  if ( size < stats.minSize ) {
1030  stats.minSize = size;
1031  }
1032  if ( size > stats.maxSize ) {
1033  stats.maxSize = size;
1034  }
1035  stats.totalSize += size;
1036 }
1037 
1038 /*
1039 ==================
1040 Mem_UpdateAllocStats
1041 ==================
1042 */
1044  Mem_UpdateStats( mem_frame_allocs, size );
1045  Mem_UpdateStats( mem_total_allocs, size );
1046 }
1047 
1048 /*
1049 ==================
1050 Mem_UpdateFreeStats
1051 ==================
1052 */
1054  Mem_UpdateStats( mem_frame_frees, size );
1055  mem_total_allocs.num--;
1056  mem_total_allocs.totalSize -= size;
1057 }
1058 
1059 
1060 #ifndef ID_DEBUG_MEMORY
1061 
1062 /*
1063 ==================
1064 Mem_Alloc
1065 ==================
1066 */
1067 void *Mem_Alloc( const int size ) {
1068  if ( !size ) {
1069  return NULL;
1070  }
1071  if ( !mem_heap ) {
1072 #ifdef CRASH_ON_STATIC_ALLOCATION
1073  *((int*)0x0) = 1;
1074 #endif
1075  return malloc( size );
1076  }
1077  void *mem = mem_heap->Allocate( size );
1078  Mem_UpdateAllocStats( mem_heap->Msize( mem ) );
1079  return mem;
1080 }
1081 
1082 /*
1083 ==================
1084 Mem_Free
1085 ==================
1086 */
1087 void Mem_Free( void *ptr ) {
1088  if ( !ptr ) {
1089  return;
1090  }
1091  if ( !mem_heap ) {
1092 #ifdef CRASH_ON_STATIC_ALLOCATION
1093  *((int*)0x0) = 1;
1094 #endif
1095  free( ptr );
1096  return;
1097  }
1098  Mem_UpdateFreeStats( mem_heap->Msize( ptr ) );
1099  mem_heap->Free( ptr );
1100 }
1101 
1102 /*
1103 ==================
1104 Mem_Alloc16
1105 ==================
1106 */
1107 void *Mem_Alloc16( const int size ) {
1108  if ( !size ) {
1109  return NULL;
1110  }
1111  if ( !mem_heap ) {
1112 #ifdef CRASH_ON_STATIC_ALLOCATION
1113  *((int*)0x0) = 1;
1114 #endif
1115  return malloc( size );
1116  }
1117  void *mem = mem_heap->Allocate16( size );
1118  // make sure the memory is 16 byte aligned
1119  assert( ( ((int)mem) & 15) == 0 );
1120  return mem;
1121 }
1122 
1123 /*
1124 ==================
1125 Mem_Free16
1126 ==================
1127 */
1128 void Mem_Free16( void *ptr ) {
1129  if ( !ptr ) {
1130  return;
1131  }
1132  if ( !mem_heap ) {
1133 #ifdef CRASH_ON_STATIC_ALLOCATION
1134  *((int*)0x0) = 1;
1135 #endif
1136  free( ptr );
1137  return;
1138  }
1139  // make sure the memory is 16 byte aligned
1140  assert( ( ((int)ptr) & 15) == 0 );
1141  mem_heap->Free16( ptr );
1142 }
1143 
1144 /*
1145 ==================
1146 Mem_ClearedAlloc
1147 ==================
1148 */
1149 void *Mem_ClearedAlloc( const int size ) {
1150  void *mem = Mem_Alloc( size );
1151  SIMDProcessor->Memset( mem, 0, size );
1152  return mem;
1153 }
1154 
1155 /*
1156 ==================
1157 Mem_ClearedAlloc
1158 ==================
1159 */
1160 void Mem_AllocDefragBlock( void ) {
1161  mem_heap->AllocDefragBlock();
1162 }
1163 
1164 /*
1165 ==================
1166 Mem_CopyString
1167 ==================
1168 */
1169 char *Mem_CopyString( const char *in ) {
1170  char *out;
1171 
1172  out = (char *)Mem_Alloc( strlen(in) + 1 );
1173  strcpy( out, in );
1174  return out;
1175 }
1176 
1177 /*
1178 ==================
1179 Mem_Dump_f
1180 ==================
1181 */
1182 void Mem_Dump_f( const idCmdArgs &args ) {
1183 }
1184 
1185 /*
1186 ==================
1187 Mem_DumpCompressed_f
1188 ==================
1189 */
1190 void Mem_DumpCompressed_f( const idCmdArgs &args ) {
1191 }
1192 
1193 /*
1194 ==================
1195 Mem_Init
1196 ==================
1197 */
1198 void Mem_Init( void ) {
1199  mem_heap = new idHeap;
1201 }
1202 
1203 /*
1204 ==================
1205 Mem_Shutdown
1206 ==================
1207 */
1208 void Mem_Shutdown( void ) {
1209  idHeap *m = mem_heap;
1210  mem_heap = NULL;
1211  delete m;
1212 }
1213 
1214 /*
1215 ==================
1216 Mem_EnableLeakTest
1217 ==================
1218 */
1219 void Mem_EnableLeakTest( const char *name ) {
1220 }
1221 
1222 
1223 #else /* !ID_DEBUG_MEMORY */
1224 
1225 #undef Mem_Alloc
1226 #undef Mem_ClearedAlloc
1227 #undef Com_ClearedReAlloc
1228 #undef Mem_Free
1229 #undef Mem_CopyString
1230 #undef Mem_Alloc16
1231 #undef Mem_Free16
1232 
1233 #define MAX_CALLSTACK_DEPTH 6
1234 
1235 // size of this struct must be a multiple of 16 bytes
1236 typedef struct debugMemory_s {
1237  const char * fileName;
1238  int lineNumber;
1239  int frameNumber;
1240  int size;
1241  address_t callStack[MAX_CALLSTACK_DEPTH];
1242  struct debugMemory_s * prev;
1243  struct debugMemory_s * next;
1244 } debugMemory_t;
1245 
1246 static debugMemory_t * mem_debugMemory = NULL;
1247 static char mem_leakName[256] = "";
1248 
1249 /*
1250 ==================
1251 Mem_CleanupFileName
1252 ==================
1253 */
1254 const char *Mem_CleanupFileName( const char *fileName ) {
1255  int i1, i2;
1256  idStr newFileName;
1257  static char newFileNames[4][MAX_STRING_CHARS];
1258  static int index;
1259 
1260  newFileName = fileName;
1261  newFileName.BackSlashesToSlashes();
1262  i1 = newFileName.Find( "neo", false );
1263  if ( i1 >= 0 ) {
1264  i1 = newFileName.Find( "/", false, i1 );
1265  newFileName = newFileName.Right( newFileName.Length() - ( i1 + 1 ) );
1266  }
1267  while( 1 ) {
1268  i1 = newFileName.Find( "/../" );
1269  if ( i1 <= 0 ) {
1270  break;
1271  }
1272  i2 = i1 - 1;
1273  while( i2 > 1 && newFileName[i2-1] != '/' ) {
1274  i2--;
1275  }
1276  newFileName = newFileName.Left( i2 - 1 ) + newFileName.Right( newFileName.Length() - ( i1 + 4 ) );
1277  }
1278  index = ( index + 1 ) & 3;
1279  strncpy( newFileNames[index], newFileName.c_str(), sizeof( newFileNames[index] ) );
1280  return newFileNames[index];
1281 }
1282 
1283 /*
1284 ==================
1285 Mem_Dump
1286 ==================
1287 */
1288 void Mem_Dump( const char *fileName ) {
1289  int i, numBlocks, totalSize;
1290  char dump[32], *ptr;
1291  debugMemory_t *b;
1292  idStr module, funcName;
1293  FILE *f;
1294 
1295  f = fopen( fileName, "wb" );
1296  if ( !f ) {
1297  return;
1298  }
1299 
1300  totalSize = 0;
1301  for ( numBlocks = 0, b = mem_debugMemory; b; b = b->next, numBlocks++ ) {
1302  ptr = ((char *) b) + sizeof(debugMemory_t);
1303  totalSize += b->size;
1304  for ( i = 0; i < (sizeof(dump)-1) && i < b->size; i++) {
1305  if ( ptr[i] >= 32 && ptr[i] < 127 ) {
1306  dump[i] = ptr[i];
1307  } else {
1308  dump[i] = '_';
1309  }
1310  }
1311  dump[i] = '\0';
1312  if ( ( b->size >> 10 ) != 0 ) {
1313  fprintf( f, "size: %6d KB: %s, line: %d [%s], call stack: %s\r\n", ( b->size >> 10 ), Mem_CleanupFileName(b->fileName), b->lineNumber, dump, idLib::sys->GetCallStackStr( b->callStack, MAX_CALLSTACK_DEPTH ) );
1314  }
1315  else {
1316  fprintf( f, "size: %7d B: %s, line: %d [%s], call stack: %s\r\n", b->size, Mem_CleanupFileName(b->fileName), b->lineNumber, dump, idLib::sys->GetCallStackStr( b->callStack, MAX_CALLSTACK_DEPTH ) );
1317  }
1318  }
1319 
1321 
1322  fprintf( f, "%8d total memory blocks allocated\r\n", numBlocks );
1323  fprintf( f, "%8d KB memory allocated\r\n", ( totalSize >> 10 ) );
1324 
1325  fclose( f );
1326 }
1327 
1328 /*
1329 ==================
1330 Mem_Dump_f
1331 ==================
1332 */
1333 void Mem_Dump_f( const idCmdArgs &args ) {
1334  const char *fileName;
1335 
1336  if ( args.Argc() >= 2 ) {
1337  fileName = args.Argv( 1 );
1338  }
1339  else {
1340  fileName = "memorydump.txt";
1341  }
1342  Mem_Dump( fileName );
1343 }
1344 
1345 /*
1346 ==================
1347 Mem_DumpCompressed
1348 ==================
1349 */
1350 typedef struct allocInfo_s {
1351  const char * fileName;
1352  int lineNumber;
1353  int size;
1354  int numAllocs;
1355  address_t callStack[MAX_CALLSTACK_DEPTH];
1356  struct allocInfo_s * next;
1357 } allocInfo_t;
1358 
1359 typedef enum {
1360  MEMSORT_SIZE,
1361  MEMSORT_LOCATION,
1362  MEMSORT_NUMALLOCS,
1363  MEMSORT_CALLSTACK
1364 } memorySortType_t;
1365 
1366 void Mem_DumpCompressed( const char *fileName, memorySortType_t memSort, int sortCallStack, int numFrames ) {
1367  int numBlocks, totalSize, r, j;
1368  debugMemory_t *b;
1369  allocInfo_t *a, *nexta, *allocInfo = NULL, *sortedAllocInfo = NULL, *prevSorted, *nextSorted;
1370  idStr module, funcName;
1371  FILE *f;
1372 
1373  // build list with memory allocations
1374  totalSize = 0;
1375  numBlocks = 0;
1376  for ( b = mem_debugMemory; b; b = b->next ) {
1377 
1378  if ( numFrames && b->frameNumber < idLib::frameNumber - numFrames ) {
1379  continue;
1380  }
1381 
1382  numBlocks++;
1383  totalSize += b->size;
1384 
1385  // search for an allocation from the same source location
1386  for ( a = allocInfo; a; a = a->next ) {
1387  if ( a->lineNumber != b->lineNumber ) {
1388  continue;
1389  }
1390  for ( j = 0; j < MAX_CALLSTACK_DEPTH; j++ ) {
1391  if ( a->callStack[j] != b->callStack[j] ) {
1392  break;
1393  }
1394  }
1395  if ( j < MAX_CALLSTACK_DEPTH ) {
1396  continue;
1397  }
1398  if ( idStr::Cmp( a->fileName, b->fileName ) != 0 ) {
1399  continue;
1400  }
1401  a->numAllocs++;
1402  a->size += b->size;
1403  break;
1404  }
1405 
1406  // if this is an allocation from a new source location
1407  if ( !a ) {
1408  a = (allocInfo_t *) ::malloc( sizeof( allocInfo_t ) );
1409  a->fileName = b->fileName;
1410  a->lineNumber = b->lineNumber;
1411  a->size = b->size;
1412  a->numAllocs = 1;
1413  for ( j = 0; j < MAX_CALLSTACK_DEPTH; j++ ) {
1414  a->callStack[j] = b->callStack[j];
1415  }
1416  a->next = allocInfo;
1417  allocInfo = a;
1418  }
1419  }
1420 
1421  // sort list
1422  for ( a = allocInfo; a; a = nexta ) {
1423  nexta = a->next;
1424 
1425  prevSorted = NULL;
1426  switch( memSort ) {
1427  // sort on size
1428  case MEMSORT_SIZE: {
1429  for ( nextSorted = sortedAllocInfo; nextSorted; nextSorted = nextSorted->next ) {
1430  if ( a->size > nextSorted->size ) {
1431  break;
1432  }
1433  prevSorted = nextSorted;
1434  }
1435  break;
1436  }
1437  // sort on file name and line number
1438  case MEMSORT_LOCATION: {
1439  for ( nextSorted = sortedAllocInfo; nextSorted; nextSorted = nextSorted->next ) {
1440  r = idStr::Cmp( Mem_CleanupFileName( a->fileName ), Mem_CleanupFileName( nextSorted->fileName ) );
1441  if ( r < 0 || ( r == 0 && a->lineNumber < nextSorted->lineNumber ) ) {
1442  break;
1443  }
1444  prevSorted = nextSorted;
1445  }
1446  break;
1447  }
1448  // sort on the number of allocations
1449  case MEMSORT_NUMALLOCS: {
1450  for ( nextSorted = sortedAllocInfo; nextSorted; nextSorted = nextSorted->next ) {
1451  if ( a->numAllocs > nextSorted->numAllocs ) {
1452  break;
1453  }
1454  prevSorted = nextSorted;
1455  }
1456  break;
1457  }
1458  // sort on call stack
1459  case MEMSORT_CALLSTACK: {
1460  for ( nextSorted = sortedAllocInfo; nextSorted; nextSorted = nextSorted->next ) {
1461  if ( a->callStack[sortCallStack] < nextSorted->callStack[sortCallStack] ) {
1462  break;
1463  }
1464  prevSorted = nextSorted;
1465  }
1466  break;
1467  }
1468  }
1469  if ( !prevSorted ) {
1470  a->next = sortedAllocInfo;
1471  sortedAllocInfo = a;
1472  }
1473  else {
1474  prevSorted->next = a;
1475  a->next = nextSorted;
1476  }
1477  }
1478 
1479  f = fopen( fileName, "wb" );
1480  if ( !f ) {
1481  return;
1482  }
1483 
1484  // write list to file
1485  for ( a = sortedAllocInfo; a; a = nexta ) {
1486  nexta = a->next;
1487  fprintf( f, "size: %6d KB, allocs: %5d: %s, line: %d, call stack: %s\r\n",
1488  (a->size >> 10), a->numAllocs, Mem_CleanupFileName(a->fileName),
1489  a->lineNumber, idLib::sys->GetCallStackStr( a->callStack, MAX_CALLSTACK_DEPTH ) );
1490  ::free( a );
1491  }
1492 
1494 
1495  fprintf( f, "%8d total memory blocks allocated\r\n", numBlocks );
1496  fprintf( f, "%8d KB memory allocated\r\n", ( totalSize >> 10 ) );
1497 
1498  fclose( f );
1499 }
1500 
1501 /*
1502 ==================
1503 Mem_DumpCompressed_f
1504 ==================
1505 */
1506 void Mem_DumpCompressed_f( const idCmdArgs &args ) {
1507  int argNum;
1508  const char *arg, *fileName;
1509  memorySortType_t memSort = MEMSORT_LOCATION;
1510  int sortCallStack = 0, numFrames = 0;
1511 
1512  // get cmd-line options
1513  argNum = 1;
1514  arg = args.Argv( argNum );
1515  while( arg[0] == '-' ) {
1516  arg = args.Argv( ++argNum );
1517  if ( idStr::Icmp( arg, "s" ) == 0 ) {
1518  memSort = MEMSORT_SIZE;
1519  } else if ( idStr::Icmp( arg, "l" ) == 0 ) {
1520  memSort = MEMSORT_LOCATION;
1521  } else if ( idStr::Icmp( arg, "a" ) == 0 ) {
1522  memSort = MEMSORT_NUMALLOCS;
1523  } else if ( idStr::Icmp( arg, "cs1" ) == 0 ) {
1524  memSort = MEMSORT_CALLSTACK;
1525  sortCallStack = 2;
1526  } else if ( idStr::Icmp( arg, "cs2" ) == 0 ) {
1527  memSort = MEMSORT_CALLSTACK;
1528  sortCallStack = 1;
1529  } else if ( idStr::Icmp( arg, "cs3" ) == 0 ) {
1530  memSort = MEMSORT_CALLSTACK;
1531  sortCallStack = 0;
1532  } else if ( arg[0] == 'f' ) {
1533  numFrames = atoi( arg + 1 );
1534  } else {
1535  idLib::common->Printf( "memoryDumpCompressed [options] [filename]\n"
1536  "options:\n"
1537  " -s sort on size\n"
1538  " -l sort on location\n"
1539  " -a sort on the number of allocations\n"
1540  " -cs1 sort on first function on call stack\n"
1541  " -cs2 sort on second function on call stack\n"
1542  " -cs3 sort on third function on call stack\n"
1543  " -f<X> only report allocations the last X frames\n"
1544  "By default the memory allocations are sorted on location.\n"
1545  "By default a 'memorydump.txt' is written if no file name is specified.\n" );
1546  return;
1547  }
1548  arg = args.Argv( ++argNum );
1549  }
1550  if ( argNum >= args.Argc() ) {
1551  fileName = "memorydump.txt";
1552  } else {
1553  fileName = arg;
1554  }
1555  Mem_DumpCompressed( fileName, memSort, sortCallStack, numFrames );
1556 }
1557 
1558 /*
1559 ==================
1560 Mem_AllocDebugMemory
1561 ==================
1562 */
1563 void *Mem_AllocDebugMemory( const int size, const char *fileName, const int lineNumber, const bool align16 ) {
1564  void *p;
1565  debugMemory_t *m;
1566 
1567  if ( !size ) {
1568  return NULL;
1569  }
1570 
1571  if ( !mem_heap ) {
1572 #ifdef CRASH_ON_STATIC_ALLOCATION
1573  *((int*)0x0) = 1;
1574 #endif
1575  // NOTE: set a breakpoint here to find memory allocations before mem_heap is initialized
1576  return malloc( size );
1577  }
1578 
1579  if ( align16 ) {
1580  p = mem_heap->Allocate16( size + sizeof( debugMemory_t ) );
1581  }
1582  else {
1583  p = mem_heap->Allocate( size + sizeof( debugMemory_t ) );
1584  }
1585 
1586  Mem_UpdateAllocStats( size );
1587 
1588  m = (debugMemory_t *) p;
1589  m->fileName = fileName;
1590  m->lineNumber = lineNumber;
1591  m->frameNumber = idLib::frameNumber;
1592  m->size = size;
1593  m->next = mem_debugMemory;
1594  m->prev = NULL;
1595  if ( mem_debugMemory ) {
1596  mem_debugMemory->prev = m;
1597  }
1598  mem_debugMemory = m;
1599  idLib::sys->GetCallStack( m->callStack, MAX_CALLSTACK_DEPTH );
1600 
1601  return ( ( (byte *) p ) + sizeof( debugMemory_t ) );
1602 }
1603 
1604 /*
1605 ==================
1606 Mem_FreeDebugMemory
1607 ==================
1608 */
1609 void Mem_FreeDebugMemory( void *p, const char *fileName, const int lineNumber, const bool align16 ) {
1610  debugMemory_t *m;
1611 
1612  if ( !p ) {
1613  return;
1614  }
1615 
1616  if ( !mem_heap ) {
1617 #ifdef CRASH_ON_STATIC_ALLOCATION
1618  *((int*)0x0) = 1;
1619 #endif
1620  // NOTE: set a breakpoint here to find memory being freed before mem_heap is initialized
1621  free( p );
1622  return;
1623  }
1624 
1625  m = (debugMemory_t *) ( ( (byte *) p ) - sizeof( debugMemory_t ) );
1626 
1627  if ( m->size < 0 ) {
1628  idLib::common->FatalError( "memory freed twice, first from %s, now from %s", idLib::sys->GetCallStackStr( m->callStack, MAX_CALLSTACK_DEPTH ), idLib::sys->GetCallStackCurStr( MAX_CALLSTACK_DEPTH ) );
1629  }
1630 
1631  Mem_UpdateFreeStats( m->size );
1632 
1633  if ( m->next ) {
1634  m->next->prev = m->prev;
1635  }
1636  if ( m->prev ) {
1637  m->prev->next = m->next;
1638  }
1639  else {
1640  mem_debugMemory = m->next;
1641  }
1642 
1643  m->fileName = fileName;
1644  m->lineNumber = lineNumber;
1645  m->frameNumber = idLib::frameNumber;
1646  m->size = -m->size;
1647  idLib::sys->GetCallStack( m->callStack, MAX_CALLSTACK_DEPTH );
1648 
1649  if ( align16 ) {
1650  mem_heap->Free16( m );
1651  }
1652  else {
1653  mem_heap->Free( m );
1654  }
1655 }
1656 
1657 /*
1658 ==================
1659 Mem_Alloc
1660 ==================
1661 */
1662 void *Mem_Alloc( const int size, const char *fileName, const int lineNumber ) {
1663  if ( !size ) {
1664  return NULL;
1665  }
1666  return Mem_AllocDebugMemory( size, fileName, lineNumber, false );
1667 }
1668 
1669 /*
1670 ==================
1671 Mem_Free
1672 ==================
1673 */
1674 void Mem_Free( void *ptr, const char *fileName, const int lineNumber ) {
1675  if ( !ptr ) {
1676  return;
1677  }
1678  Mem_FreeDebugMemory( ptr, fileName, lineNumber, false );
1679 }
1680 
1681 /*
1682 ==================
1683 Mem_Alloc16
1684 ==================
1685 */
1686 void *Mem_Alloc16( const int size, const char *fileName, const int lineNumber ) {
1687  if ( !size ) {
1688  return NULL;
1689  }
1690  void *mem = Mem_AllocDebugMemory( size, fileName, lineNumber, true );
1691  // make sure the memory is 16 byte aligned
1692  assert( ( ((int)mem) & 15) == 0 );
1693  return mem;
1694 }
1695 
1696 /*
1697 ==================
1698 Mem_Free16
1699 ==================
1700 */
1701 void Mem_Free16( void *ptr, const char *fileName, const int lineNumber ) {
1702  if ( !ptr ) {
1703  return;
1704  }
1705  // make sure the memory is 16 byte aligned
1706  assert( ( ((int)ptr) & 15) == 0 );
1707  Mem_FreeDebugMemory( ptr, fileName, lineNumber, true );
1708 }
1709 
1710 /*
1711 ==================
1712 Mem_ClearedAlloc
1713 ==================
1714 */
1715 void *Mem_ClearedAlloc( const int size, const char *fileName, const int lineNumber ) {
1716  void *mem = Mem_Alloc( size, fileName, lineNumber );
1717  SIMDProcessor->Memset( mem, 0, size );
1718  return mem;
1719 }
1720 
1721 /*
1722 ==================
1723 Mem_CopyString
1724 ==================
1725 */
1726 char *Mem_CopyString( const char *in, const char *fileName, const int lineNumber ) {
1727  char *out;
1728 
1729  out = (char *)Mem_Alloc( strlen(in) + 1, fileName, lineNumber );
1730  strcpy( out, in );
1731  return out;
1732 }
1733 
1734 /*
1735 ==================
1736 Mem_Init
1737 ==================
1738 */
1739 void Mem_Init( void ) {
1740  mem_heap = new idHeap;
1741 }
1742 
1743 /*
1744 ==================
1745 Mem_Shutdown
1746 ==================
1747 */
1748 void Mem_Shutdown( void ) {
1749 
1750  if ( mem_leakName[0] != '\0' ) {
1751  Mem_DumpCompressed( va( "%s_leak_size.txt", mem_leakName ), MEMSORT_SIZE, 0, 0 );
1752  Mem_DumpCompressed( va( "%s_leak_location.txt", mem_leakName ), MEMSORT_LOCATION, 0, 0 );
1753  Mem_DumpCompressed( va( "%s_leak_cs1.txt", mem_leakName ), MEMSORT_CALLSTACK, 2, 0 );
1754  }
1755 
1756  idHeap *m = mem_heap;
1757  mem_heap = NULL;
1758  delete m;
1759 }
1760 
1761 /*
1762 ==================
1763 Mem_EnableLeakTest
1764 ==================
1765 */
1766 void Mem_EnableLeakTest( const char *name ) {
1767  idStr::Copynz( mem_leakName, name, sizeof( mem_leakName ) );
1768 }
1769 
1770 #endif /* !ID_DEBUG_MEMORY */
void Mem_EnableLeakTest(const char *name)
Definition: Heap.cpp:1219
void Init(void)
Definition: Heap.cpp:151
unsigned int dword
Definition: Lib.h:77
page_s * largeFirstUsedPage
Definition: Heap.cpp:112
virtual void GetCallStack(address_t *callStack, const int callStackSize)=0
assert(prefInfo.fullscreenBtn)
void Mem_ClearFrameStats(void)
Definition: Heap.cpp:996
int Cmp(const char *text) const
Definition: Str.h:652
page_s * smallFirstUsedPage
Definition: Heap.cpp:106
mediumHeapEntry_s * prev
Definition: Heap.cpp:95
#define MEDIUM_SMALLEST_SIZE
Definition: Heap.cpp:52
void * Allocate16(const dword bytes)
Definition: Heap.cpp:320
dword freeBlock
Definition: Heap.cpp:99
void * firstFree
Definition: Heap.cpp:89
void Mem_GetStats(memoryStats_t &stats)
Definition: Heap.cpp:1018
page_s * prev
Definition: Heap.cpp:87
dword pageRequests
Definition: Heap.cpp:119
#define SMALL_ALIGN(bytes)
Definition: Heap.cpp:51
Definition: Heap.cpp:92
int Length(void) const
Definition: Str.h:702
dword Msize(void *p)
Definition: Heap.cpp:363
virtual const char * GetCallStackStr(const address_t *callStack, const int callStackSize)=0
void Mem_Init(void)
Definition: Heap.cpp:1198
const char * Left(int len, idStr &result) const
Definition: Str.h:892
page_s * mediumFirstFreePage
Definition: Heap.cpp:108
#define SMALL_HEADER_SIZE
Definition: Heap.cpp:46
static class idSys * sys
Definition: Lib.h:52
virtual void VPCALL Memset(void *dst, const int val, const int count)=0
page_s * swapPage
Definition: Heap.cpp:114
int i
Definition: process.py:33
page_s * AllocatePage(dword bytes)
Definition: Heap.cpp:464
mediumHeapEntry_s * prevFree
Definition: Heap.cpp:97
void Mem_UpdateAllocStats(int size)
Definition: Heap.cpp:1043
int Icmp(const char *text) const
Definition: Str.h:667
idStr & BackSlashesToSlashes(void)
Definition: Str.cpp:727
page_s * mediumFirstUsedPage
Definition: Heap.cpp:110
void Mem_Dump_f(const idCmdArgs &args)
Definition: Heap.cpp:1182
int minSize
Definition: Heap.h:47
GLuint index
Definition: glext.h:3476
void * data
Definition: Heap.cpp:84
#define MAX_STRING_CHARS
Definition: Lib.h:95
#define LARGE_HEADER_SIZE
Definition: Heap.cpp:48
dword pageSize
Definition: Heap.cpp:117
idCommon * common
Definition: Common.cpp:206
#define NULL
Definition: Lib.h:88
page_s * smallCurPage
Definition: Heap.cpp:104
void Free16(void *p)
Definition: Heap.cpp:349
GLsizei GLsizei GLenum GLenum const GLvoid * data
Definition: glext.h:2853
virtual void virtual void FatalError(const char *fmt,...) id_attribute((format(printf
void * Mem_Alloc16(const int size)
Definition: Heap.cpp:1107
static void Copynz(char *dest, const char *src, int destsize)
Definition: Str.cpp:1376
void Dump(void)
Definition: Heap.cpp:401
void Mem_GetFrameStats(memoryStats_t &allocs, memoryStats_t &frees)
Definition: Heap.cpp:1008
static int frameNumber
Definition: Lib.h:56
void MediumFree(void *ptr)
Definition: Heap.cpp:792
int Argc(void) const
Definition: CmdArgs.h:48
int maxSize
Definition: Heap.h:48
void Mem_Free(void *ptr)
Definition: Heap.cpp:1087
#define MEDIUM_HEADER_SIZE
Definition: Heap.cpp:47
const char * Right(int len, idStr &result) const
Definition: Str.h:896
int Find(const char c, int start=0, int end=-1) const
Definition: Str.h:874
dword smallCurPageOffset
Definition: Heap.cpp:105
int totalSize
Definition: Heap.h:49
dword size
Definition: Heap.cpp:94
GLubyte GLubyte GLubyte a
Definition: glext.h:4662
virtual void Printf(const char *fmt,...) id_attribute((format(printf
int num
Definition: Heap.h:46
void Mem_UpdateFreeStats(int size)
Definition: Heap.cpp:1053
virtual const char * GetCallStackCurStr(int depth)=0
int c_heapAllocRunningCount
Definition: Heap.cpp:122
dword OSAllocs
Definition: Heap.cpp:120
GLubyte GLubyte b
Definition: glext.h:4662
void Mem_UpdateStats(memoryStats_t &stats, int size)
Definition: Heap.cpp:1027
page_s * next
Definition: Heap.cpp:86
void Mem_DumpCompressed_f(const idCmdArgs &args)
Definition: Heap.cpp:1190
void AllocDefragBlock(void)
Definition: Heap.cpp:240
GLdouble GLdouble GLdouble r
Definition: glext.h:2951
void * LargeAllocate(dword bytes)
Definition: Heap.cpp:922
page_s * page
Definition: Heap.cpp:93
dword largestFree
Definition: Heap.cpp:88
tuple f
Definition: idal.py:89
GLint GLint i2
Definition: qgl.h:261
GLuint in
Definition: glext.h:5388
unsigned char byte
Definition: Lib.h:75
const GLcharARB * name
Definition: glext.h:3629
GLsizeiptr size
Definition: glext.h:3112
void * SmallAllocate(dword bytes)
Definition: Heap.cpp:543
dword pagesAllocated
Definition: Heap.cpp:116
void Mem_Free16(void *ptr)
Definition: Heap.cpp:1128
void SmallFree(void *ptr)
Definition: Heap.cpp:589
Definition: Str.h:116
void FreePageReal(idHeap::page_s *p)
Definition: Heap.cpp:436
virtual void ShutdownSymbols(void)=0
void * Mem_ClearedAlloc(const int size)
Definition: Heap.cpp:1149
const char * c_str(void) const
Definition: Str.h:487
GLint i1
Definition: qgl.h:261
void * defragBlock
Definition: Heap.cpp:124
mediumHeapEntry_s * nextFree
Definition: Heap.cpp:98
const char * Argv(int arg) const
Definition: CmdArgs.h:50
void * MediumAllocate(dword bytes)
Definition: Heap.cpp:688
void * Mem_Alloc(const int size)
Definition: Heap.cpp:1067
GLint j
Definition: qgl.h:264
void * Allocate(const dword bytes)
Definition: Heap.cpp:261
unsigned long address_t
Definition: sys_public.h:234
void Mem_Shutdown(void)
Definition: Heap.cpp:1208
if(!ValidDisplayID(prefInfo.prefDisplayID)) prefInfo.prefDisplayID
char * Mem_CopyString(const char *in)
Definition: Heap.cpp:1169
char * va(const char *fmt,...)
Definition: Str.cpp:1568
page_s * mediumLastFreePage
Definition: Heap.cpp:109
idHeap(void)
Definition: Heap.cpp:180
Definition: Heap.cpp:55
GLfloat GLfloat p
Definition: glext.h:4674
void ReleaseSwappedPages(void)
Definition: Heap.cpp:448
void Free(void *p)
Definition: Heap.cpp:285
dword dataSize
Definition: Heap.cpp:85
void * MediumAllocateFromPage(idHeap::page_s *p, dword sizeNeeded)
Definition: Heap.cpp:625
void LargeFree(void *ptr)
Definition: Heap.cpp:955
void Mem_AllocDefragBlock(void)
Definition: Heap.cpp:1160
void FreePage(idHeap::page_s *p)
Definition: Heap.cpp:515
#define ALIGN_SIZE(bytes)
Definition: Heap.cpp:50
mediumHeapEntry_s * next
Definition: Heap.cpp:96
~idHeap(void)
Definition: Heap.cpp:191
static class idCommon * common
Definition: Lib.h:53
idSIMDProcessor * SIMDProcessor
Definition: Simd.cpp:43
void * smallFirstFree[256/ALIGN+1]
Definition: Heap.cpp:103