A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 667 lines 20 kB view raw
1/* Emacs style mode select -*- C++ -*- 2 *----------------------------------------------------------------------------- 3 * 4 * 5 * PrBoom a Doom port merged with LxDoom and LSDLDoom 6 * based on BOOM, a modified and improved DOOM engine 7 * Copyright (C) 1999 by 8 * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman 9 * Copyright (C) 1999-2000 by 10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze 11 * 12 * This program is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU General Public License 14 * as published by the Free Software Foundation; either version 2 15 * of the License, or (at your option) any later version. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU General Public License for more details. 21 * 22 * You should have received a copy of the GNU General Public License 23 * along with this program; if not, write to the Free Software 24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 25 * 02111-1307, USA. 26 * 27 * DESCRIPTION: 28 * Zone Memory Allocation. Neat. 29 * 30 * Neat enough to be rewritten by Lee Killough... 31 * 32 * Must not have been real neat :) 33 * 34 * Made faster and more general, and added wrappers for all of Doom's 35 * memory allocation functions, including malloc() and similar functions. 36 * Added line and file numbers, in case of error. Added performance 37 * statistics and tunables. 38 *----------------------------------------------------------------------------- 39 */ 40 41#include "z_zone.h" 42#include "z_bmalloc.h" 43#include "doomdef.h" 44#include "i_system.h" 45#include "rockmacros.h" 46#include "m_argv.h" 47 48// Tunables 49 50// Alignment of zone memory (benefit may be negated by HEADER_SIZE, CHUNK_SIZE) 51#define CACHE_ALIGN 32 52 53// Minimum chunk size at which blocks are allocated 54#define CHUNK_SIZE 32 55 56// Minimum size a block must be to become part of a split 57#define MIN_BLOCK_SPLIT (1024) 58 59// Minimum RAM machine is assumed to have 60/* cph - Select zone size. 6megs is usable, but with the SDL version 61 * storing sounds in the zone, 8 is more sensible */ 62#define MIN_RAM (8*1024*1024) 63 64// Amount to subtract when retrying failed attempts to allocate initial pool 65#define RETRY_AMOUNT (256*1024) 66 67// signature for block header 68#define ZONEID 0x931d4a11 69 70// Number of mallocs & frees kept in history buffer (must be a power of 2) 71#define ZONE_HISTORY 4 72 73// End Tunables 74 75typedef struct memblock { 76 77#ifdef ZONEIDCHECK 78 unsigned id; 79#endif 80 81 struct memblock *next,*prev; 82 size_t size; 83 void **user; 84 unsigned char tag,vm; 85 86#ifdef INSTRUMENTED 87 unsigned short extra; 88 const char *file; 89 int line; 90#endif 91 92} memblock_t; 93 94/* size of block header 95 * cph - base on sizeof(memblock_t), which can be larger than CHUNK_SIZE on 96 * 64bit architectures */ 97static const size_t HEADER_SIZE IDATA_ATTR= (sizeof(memblock_t)+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); 98 99static memblock_t *rover IBSS_ATTR; // roving pointer to memory blocks 100static memblock_t *zone IBSS_ATTR; // pointer to first block 101static memblock_t *zonebase IBSS_ATTR; // pointer to entire zone memory 102static size_t zonebase_size IBSS_ATTR; // zone memory allocated size 103 104#ifdef INSTRUMENTED 105 106// statistics for evaluating performance 107static size_t free_memory; 108static size_t active_memory; 109static size_t purgable_memory; 110static size_t inactive_memory; 111static size_t virtual_memory; 112 113static void Z_PrintStats(void) // Print allocation statistics 114{ 115 unsigned long total_memory = free_memory + active_memory + 116 purgable_memory + inactive_memory + 117 virtual_memory; 118// double s = 100.0 / total_memory; 119 int s = 100/total_memory; 120 121 doom_printf("%-5u\t%6.01f%%\tstatic\n" 122 "%-5u\t%6.01f%%\tpurgable\n" 123 "%-5u\t%6.01f%%\tfree\n" 124 "%-5u\t%6.01f%%\tfragmentary\n" 125 "%-5u\t%6.01f%%\tvirtual\n" 126 "%-5lu\t\ttotal\n", 127 active_memory, 128 active_memory*s, 129 purgable_memory, 130 purgable_memory*s, 131 free_memory, 132 free_memory*s, 133 inactive_memory, 134 inactive_memory*s, 135 virtual_memory, 136 virtual_memory*s, 137 total_memory 138 ); 139} 140 141#ifdef HEAPDUMP 142void W_PrintLump(FILE* fp, void* p); 143 144void Z_DumpMemory(void) 145{ 146 static int dump; 147 memblock_t* block = zone; 148 char buf[80]; 149 FILE* fp; 150 size_t total_cache = 0, total_free = 0, total_malloc = 0; 151 152 sprintf(buf, "memdump.%d", dump++); 153 fp = fopen(buf, "w"); 154 do { 155 switch (block->tag) { 156 case PU_FREE: 157 fprintf(fp, "free %d\n", block->size); 158 total_free += block->size; 159 break; 160 case PU_CACHE: 161 fprintf(fp, "cache %s:%d:%d\n", block->file, block->line, block->size); 162 total_cache += block->size; 163 break; 164 case PU_LEVEL: 165 fprintf(fp, "level %s:%d:%d\n", block->file, block->line, block->size); 166 total_malloc += block->size; 167 break; 168 default: 169 fprintf(fp, "malloc %s:%d:%d", block->file, block->line, block->size); 170 total_malloc += block->size; 171 if (!strcmp(block->file,"w_wad.c")) W_PrintLump(fp, (char*)block + HEADER_SIZE); 172 fputc('\n', fp); 173 break; 174 } 175 block=block->next; 176 } while (block != zone); 177 fprintf(fp, "malloc %d, cache %d, free %d, total %d\n", 178 total_malloc, total_cache, total_free, 179 total_malloc + total_cache + total_free); 180 fclose(fp); 181} 182#endif 183#endif 184 185#ifdef INSTRUMENTED 186 187// killough 4/26/98: Add history information 188 189enum {malloc_history, free_history, NUM_HISTORY_TYPES}; 190 191static const char *file_history[NUM_HISTORY_TYPES][ZONE_HISTORY]; 192static int line_history[NUM_HISTORY_TYPES][ZONE_HISTORY]; 193static int history_index[NUM_HISTORY_TYPES]; 194static const char *const desc[NUM_HISTORY_TYPES] = {"malloc()'s", "free()'s"}; 195 196void Z_DumpHistory(char *buf) 197{ 198 int i,j; 199 char s[1024]; 200 strcat(buf,"\n"); 201 for (i=0;i<NUM_HISTORY_TYPES;i++) 202 { 203 sprintf(s,"\nLast several %s:\n\n", desc[i]); 204 strcat(buf,s); 205 for (j=0; j<ZONE_HISTORY; j++) 206 { 207 int k = (history_index[i]-j-1) & (ZONE_HISTORY-1); 208 if (file_history[i][k]) 209 { 210 sprintf(s, "File: %s, Line: %d\n", file_history[i][k], 211 line_history[i][k]); 212 strcat(buf,s); 213 } 214 } 215 } 216} 217#else 218 219void Z_DumpHistory(char *buf) 220{ 221 (void)buf; 222} 223 224#endif 225 226void Z_Close(void) 227{ 228// (free)(zonebase); 229 zone = rover = zonebase = NULL; 230} 231 232void Z_Init(void) 233{ 234 size_t size; 235#ifdef INSTRUMENTED 236 if (!(HEADER_SIZE >= sizeof(memblock_t) && MIN_RAM > LEAVE_ASIDE)) 237 I_Error("Z_Init: Sanity check failed"); 238#endif 239 240// atexit(Z_Close); // exit handler 241 242 // Allocate the memory 243 244 zonebase=rb->plugin_get_audio_buffer(&size); 245 size-=2*(HEADER_SIZE + CACHE_ALIGN); // Leave space for header and CACHE_ALIGN 246 size = (size+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); // round to chunk size 247 size += HEADER_SIZE + CACHE_ALIGN; 248 249 zonebase_size=size; 250 251 printf("Z_Init: Allocated %luKb zone memory\n", (long unsigned)(size >> 10)); 252 253 // Align on cache boundary 254 255 zone = (memblock_t *) ((intptr_t)zonebase + CACHE_ALIGN - 256 ((intptr_t)zonebase & (CACHE_ALIGN-1))); 257 258 rover = zone; // Rover points to base of zone mem 259 zone->next = zone->prev = zone; // Single node 260 zone->size = size; // All memory in one block 261 zone->tag = PU_FREE; // A free block 262 zone->vm = 0; 263 264#ifdef ZONEIDCHECK 265 zone->id = 0; 266#endif 267 268#ifdef INSTRUMENTED 269 free_memory = size; 270 inactive_memory = zonebase_size - size; 271 active_memory = purgable_memory = virtual_memory = 0; 272#endif 273} 274 275/* Z_Malloc 276 * You can pass a NULL user if the tag is < PU_PURGELEVEL. 277 * 278 * cph - the algorithm here was a very simple first-fit round-robin 279 * one - just keep looping around, freeing everything we can until 280 * we get a large enough space 281 * 282 * This has been changed now; we still do the round-robin first-fit, 283 * but we only free the blocks we actually end up using; we don't 284 * free all the stuff we just pass on the way. 285 */ 286 287void *(Z_Malloc)(size_t size, int tag, void **user 288#ifdef INSTRUMENTED 289 , const char *file, int line 290#endif 291 ) 292{ 293 register memblock_t *block; 294 memblock_t *start, *first_of_free; 295 register size_t contig_free; 296 297#ifdef INSTRUMENTED 298 size_t size_orig = size; 299#ifdef CHECKHEAP 300 Z_CheckHeap(); 301#endif 302 303 file_history[malloc_history][history_index[malloc_history]] = file; 304 line_history[malloc_history][history_index[malloc_history]++] = line; 305 history_index[malloc_history] &= ZONE_HISTORY-1; 306#endif 307 308#ifdef ZONEIDCHECK 309 if (tag >= PU_PURGELEVEL && !user) 310 I_Error ("Z_Malloc: An owner is required for purgable blocks" 311#ifdef INSTRUMENTED 312 "Source: %s:%d", file, line 313#endif 314 ); 315#endif 316 317 if (!size) 318 return user ? *user = NULL : NULL; // malloc(0) returns NULL 319 320 size = (size+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); // round to chunk size 321 322 block = rover; 323 324 if (block->prev->tag == PU_FREE) 325 block = block->prev; 326 327 start = block; 328 first_of_free = NULL; contig_free = 0; 329 330 do { 331 /* If we just wrapped, we're not contiguous with the previous block */ 332 if (block == zone) contig_free = 0; 333 334 if (block->tag < PU_PURGELEVEL && block->tag != PU_FREE) { 335 /* Not free(able), so no free space here */ 336 contig_free = 0; 337 } else { 338 /* Add to contiguous chunk of free space */ 339 if (!contig_free) first_of_free = block; 340 contig_free += block->size; 341 342 /* First fit */ 343 if (contig_free >= size) 344 break; 345 } 346 } 347 while ((block = block->next) != start); // detect cycles as failure 348 349 if (contig_free >= size) { 350 /* We have a block of free(able) memory on the heap which will suffice */ 351 block = first_of_free; 352 353 /* If the previous block is adjacent and free, step back and include it */ 354 if (block != zone && block->prev->tag == PU_FREE) 355 block = block->prev; 356 357 /* Free current block if needed */ 358 if (block->tag != PU_FREE) Z_Free((char *) block + HEADER_SIZE); 359 360 /* Note: guaranteed that block->prev is either 361 * not free or not contiguous 362 * 363 * At every step, block->next must be not free, else it would 364 * have been merged with our block 365 * No range check needed because we know it works by the previous loop */ 366 while (block->size < size) 367 Z_Free((char *)(block->next) + HEADER_SIZE); 368 369 /* Now, carve up the block */ 370 { 371 size_t extra = block->size - size; 372 if (extra >= MIN_BLOCK_SPLIT + HEADER_SIZE) { 373 memblock_t *newb = (memblock_t *)((char *) block + 374 HEADER_SIZE + size); 375 376 (newb->next = block->next)->prev = newb; 377 (newb->prev = block)->next = newb; // Split up block 378 block->size = size; 379 newb->size = extra - HEADER_SIZE; 380 newb->tag = PU_FREE; 381 newb->vm = 0; 382 383#ifdef INSTRUMENTED 384 inactive_memory += HEADER_SIZE; 385 free_memory -= HEADER_SIZE; 386#endif 387 } 388 389 rover = block->next; // set roving pointer for next search 390 391#ifdef INSTRUMENTED 392 inactive_memory += block->extra = block->size - size_orig; 393 if (tag >= PU_PURGELEVEL) 394 purgable_memory += size_orig; 395 else 396 active_memory += size_orig; 397 free_memory -= block->size; 398#endif 399 } 400 } else { // We don't have enough contiguous free blocks 401 I_Error ("Z_Malloc: Failure trying to allocate %d bytes",(unsigned long) size); 402 rb->sleep(300); 403 } 404 405#ifdef INSTRUMENTED 406 block->file = file; 407 block->line = line; 408#endif 409 410#ifdef ZONEIDCHECK 411 block->id = ZONEID; // signature required in block header 412#endif 413 block->tag = tag; // tag 414 block->user = user; // user 415 block = (memblock_t *)((char *) block + HEADER_SIZE); 416 if (user) // if there is a user 417 *user = block; // set user to point to new block 418 419#ifdef INSTRUMENTED 420 Z_PrintStats(); // print memory allocation stats 421 // scramble memory -- weed out any bugs 422 memset(block, gametic & 0xff, size); 423#endif 424 return block; 425} 426 427void (Z_Free)(void *p 428#ifdef INSTRUMENTED 429 , const char *file, int line 430#endif 431 ) 432{ 433#ifdef INSTRUMENTED 434#ifdef CHECKHEAP 435 Z_CheckHeap(); 436#endif 437 file_history[free_history][history_index[free_history]] = file; 438 line_history[free_history][history_index[free_history]++] = line; 439 history_index[free_history] &= ZONE_HISTORY-1; 440#endif 441 442 if (p) 443 { 444 memblock_t *other, *block = (memblock_t *)((char *) p - HEADER_SIZE); 445 446#ifdef ZONEIDCHECK 447 if (block->id != ZONEID) 448 I_Error("Z_Free: freed a pointer without ZONEID" 449#ifdef INSTRUMENTED 450 "\nSource: %s:%d" 451 "\nSource of malloc: %s:%d" 452 , file, line, block->file, block->line 453#endif 454 ); 455 block->id = 0; // Nullify id so another free fails 456#endif 457 458#ifdef INSTRUMENTED 459 /* scramble memory -- weed out any bugs */ 460 memset(p, gametic & 0xff, block->size); 461#endif 462 463 if (block->user) // Nullify user if one exists 464 *block->user = NULL; 465 466 { 467 468#ifdef INSTRUMENTED 469 free_memory += block->size; 470 inactive_memory -= block->extra; 471 if (block->tag >= PU_PURGELEVEL) 472 purgable_memory -= block->size - block->extra; 473 else 474 active_memory -= block->size - block->extra; 475#endif 476 477 block->tag = PU_FREE; // Mark block freed 478 479 if (block != zone) 480 { 481 other = block->prev; // Possibly merge with previous block 482 if (other->tag == PU_FREE) 483 { 484 if (rover == block) // Move back rover if it points at block 485 rover = other; 486 (other->next = block->next)->prev = other; 487 other->size += block->size + HEADER_SIZE; 488 block = other; 489 490#ifdef INSTRUMENTED 491 inactive_memory -= HEADER_SIZE; 492 free_memory += HEADER_SIZE; 493#endif 494 } 495 } 496 497 other = block->next; // Possibly merge with next block 498 if (other->tag == PU_FREE && other != zone) 499 { 500 if (rover == other) // Move back rover if it points at next block 501 rover = block; 502 (block->next = other->next)->prev = block; 503 block->size += other->size + HEADER_SIZE; 504 505#ifdef INSTRUMENTED 506 inactive_memory -= HEADER_SIZE; 507 free_memory += HEADER_SIZE; 508#endif 509 } 510 } 511 512#ifdef INSTRUMENTED 513 Z_PrintStats(); // print memory allocation stats 514#endif 515 } 516} 517 518void (Z_FreeTags)(int lowtag, int hightag 519#ifdef INSTRUMENTED 520 , const char *file, int line 521#endif 522 ) 523{ 524 /* cph - move rover to start of zone; we like to encourage static 525 * data to stay in one place, at the start of the heap 526 */ 527 memblock_t *block = rover = zone; 528 529#ifdef HEAPDUMP 530 Z_DumpMemory(); 531#endif 532 533 if (lowtag <= PU_FREE) 534 lowtag = PU_FREE+1; 535 536 do // Scan through list, searching for tags in range 537 if (block->tag >= lowtag && block->tag <= hightag) 538 { 539 memblock_t *prev = block->prev, *cur = block; 540#ifdef INSTRUMENTED 541 (Z_Free)((char *) block + HEADER_SIZE, file, line); 542#else 543 (Z_Free)((char *) block + HEADER_SIZE); 544#endif 545 /* cph - be more careful here, we were skipping blocks! 546 * If the current block was not merged with the previous, 547 * cur is still a valid pointer, prev->next == cur, and cur is 548 * already free so skip to the next. 549 * If the current block was merged with the previous, 550 * the next block to analyse is prev->next. 551 * Note that the while() below does the actual step forward 552 */ 553 block = (prev->next == cur) ? cur : prev; 554 } 555 while ((block=block->next) != zone); 556} 557 558void (Z_ChangeTag)(void *ptr, int tag 559#ifdef INSTRUMENTED 560 , const char *file, int line 561#endif 562 ) 563{ 564 memblock_t *block = (memblock_t *)((char *) ptr - HEADER_SIZE); 565 566#ifdef INSTRUMENTED 567#ifdef CHECKHEAP 568 Z_CheckHeap(); 569#endif 570#endif 571 572#ifdef ZONEIDCHECK 573 if (block->id != ZONEID) 574 I_Error ("Z_ChangeTag: freed a pointer without ZONEID" 575#ifdef INSTRUMENTED 576 "\nSource: %s:%d" 577 "\nSource of malloc: %s:%d" 578 , file, line, block->file, block->line 579#endif 580 ); 581 582 if (tag >= PU_PURGELEVEL && !block->user) 583 I_Error ("Z_ChangeTag: an owner is required for purgable blocks\n" 584#ifdef INSTRUMENTED 585 "Source: %s:%d" 586 "\nSource of malloc: %s:%d" 587 , file, line, block->file, block->line 588#endif 589 ); 590 591#endif // ZONEIDCHECK 592 593 { 594#ifdef INSTRUMENTED 595 if (block->tag < PU_PURGELEVEL && tag >= PU_PURGELEVEL) 596 { 597 active_memory -= block->size - block->extra; 598 purgable_memory += block->size - block->extra; 599 } 600 else 601 if (block->tag >= PU_PURGELEVEL && tag < PU_PURGELEVEL) 602 { 603 active_memory += block->size - block->extra; 604 purgable_memory -= block->size - block->extra; 605 } 606#endif 607 } 608 block->tag = tag; 609} 610 611void *(Z_Realloc)(void *ptr, size_t n, int tag, void **user 612#ifdef INSTRUMENTED 613 , const char *file, int line 614#endif 615 ) 616{ 617 void *p = (Z_Malloc)(n, tag, user DA(file, line)); 618 if (ptr) 619 { 620 memblock_t *block = (memblock_t *)((char *) ptr - HEADER_SIZE); 621 memcpy(p, ptr, n <= block->size ? n : block->size); 622 (Z_Free)(ptr DA(file, line)); 623 if (user) // in case Z_Free nullified same user 624 *user=p; 625 } 626 return p; 627} 628 629void *(Z_Calloc)(size_t n1, size_t n2, int tag, void **user 630#ifdef INSTRUMENTED 631 , const char *file, int line 632#endif 633 ) 634{ 635 return 636 (n1*=n2) ? memset((Z_Malloc)(n1, tag, user DA(file, line)), 0, n1) : NULL; 637} 638 639char *(Z_Strdup)(const char *s, int tag, void **user 640#ifdef INSTRUMENTED 641 , const char *file, int line 642#endif 643 ) 644{ 645 return strcpy((Z_Malloc)(strlen(s)+1, tag, user DA(file, line)), s); 646} 647 648void (Z_CheckHeap)( 649#ifdef INSTRUMENTED 650 const char *file, int line 651#endif 652) 653{ 654 memblock_t *block = zone; // Start at base of zone mem 655 do // Consistency check (last node treated special) 656 if ((block->next != zone && 657 (memblock_t *)((char *) block+HEADER_SIZE+block->size) != block->next) 658 || block->next->prev != block || block->prev->next != block) 659 I_Error("Z_ChkHp: B size %d touch %d\n", block+HEADER_SIZE+block->size, block->next 660#ifdef INSTRUMENTED 661 "Source: %s:%d" 662 "\nSource of offending block: %s:%d" 663 , file, line, block->file, block->line 664#endif 665 ); 666 while ((block=block->next) != zone); 667}