1 /******************************************************************* 2 * 3 * hugemem.c 4 * 5 * Memory management component (body) 6 * for dealing with "huge" objects with 16-bit Windows. 7 * 8 * Written by Antoine Leca based on ideas from Dave Hoo. 9 * Copyright 1999 by Dave Hoo, Antoine Leca, 10 * David Turner, Robert Wilhelm, and Werner Lemberg. 11 * 12 * This file is part of the FreeType project, and may only be used 13 * modified and distributed under the terms of the FreeType project 14 * license, LICENSE.TXT. By continuing to use, modify, or distribute 15 * this file you indicate that you have read the license and 16 * understand and accept it fully. 17 * 18 ******************************************************************/ 19 20 #include <limits.h> 21 #include <windows.h> 22 23 #include "ttdebug.h" 24 #include "ttmemory.h" 25 #include "ttengine.h" 26 27 #ifndef TT_HUGE_PTR 28 #error "This component needs TT_HUGE_PTR to be #defined." 29 #endif 30 31 #ifdef TT_CONFIG_OPTION_THREAD_SAFE 32 #error "This component needs static allocation and is not re-entrant." 33 #endif 34 35 /* If the memory reclaimed is abobve this limit, alloc directly from */ 36 /* global heap. Else, alloc using malloc (using suballocation). */ 37 #ifndef MEMORY_MIN_GLOBAL 38 #define MEMORY_MIN_GLOBAL 4096 39 #endif 40 41 /* required by the tracing mode */ 42 #undef TT_COMPONENT 43 #define TT_COMPONENT trace_memory 44 45 46 #ifdef DEBUG_MEMORY 47 48 #include <stdio.h> 49 50 #define MAX_TRACKED_BLOCKS 1024 51 52 struct TMemRec_ 53 { 54 void* base; 55 Long size; 56 }; 57 58 typedef struct TMemRec_ TMemRec; 59 60 static TMemRec pointers[MAX_TRACKED_BLOCKS + 1]; 61 62 static Int num_alloc; 63 static Int num_free; 64 static Int num_realloc; /* counts only `real' reallocations 65 (i.e., an existing buffer will be resized 66 to a value larger than zero */ 67 68 static Int fail_alloc; 69 static Int fail_realloc; 70 static Int fail_free; 71 72 #else 73 74 /* We need a tracing stack of the calls to big chunks of memory, */ 75 /* in order to call the matching version of free(). */ 76 77 #define MAX_TRACKED_BIGCHUNKS 64 78 79 struct TMemRec_ 80 { 81 void* base; 82 }; 83 84 typedef struct TMemRec_ TMemRec; 85 86 static TMemRec pointers[MAX_TRACKED_BIGCHUNKS + 1]; 87 88 #endif /* DEBUG_MEMORY */ 89 90 91 #ifndef TT_CONFIG_REENTRANT 92 Long TTMemory_Allocated; 93 Long TTMemory_MaxAllocated; 94 #endif 95 96 97 /******************************************************************* 98 * 99 * Function : TT_Alloc 100 * 101 * Description : Allocates memory from the heap buffer. 102 * 103 * Input : Size size of the memory to be allocated 104 * P pointer to a buffer pointer 105 * 106 * Output : Error code. 107 * 108 * NOTE : The newly allocated block should _always_ be zeroed 109 * on return. Many parts of the engine rely on this to 110 * work properly. 111 * 112 ******************************************************************/ 113 114 EXPORT_FUNC TT_Alloc(ULong Size,void ** P)115 TT_Error TT_Alloc( ULong Size, void** P ) 116 { 117 Int i; 118 119 120 if ( !P ) 121 return TT_Err_Invalid_Argument; 122 /* Also see below for another case of "invalid argument". */ 123 124 if ( Size > 0 ) 125 { 126 if ( Size >= MEMORY_MIN_GLOBAL ) 127 { 128 HANDLE hMem; 129 130 hMem = GlobalAlloc( GMEM_ZEROINIT, Size ); 131 if ( !hMem ) 132 return TT_Err_Out_Of_Memory; 133 134 *P = (void*)GlobalLock( hMem ); 135 } 136 else 137 *P = (void*)malloc( Size ); 138 139 if ( !*P ) 140 return TT_Err_Out_Of_Memory; 141 142 #ifndef TT_CONFIG_REENTRANT 143 TTMemory_MaxAllocated += Size; 144 TTMemory_Allocated += Size; 145 #endif 146 147 #ifdef DEBUG_MEMORY 148 149 num_alloc++; 150 151 i = 0; 152 while ( i < MAX_TRACKED_BLOCKS && pointers[i].base != NULL ) 153 i++; 154 155 if ( i >= MAX_TRACKED_BLOCKS ) 156 fail_alloc++; 157 else 158 { 159 pointers[i].base = *P; 160 pointers[i].size = Size; 161 } 162 163 #else 164 165 if ( Size >= MEMORY_MIN_GLOBAL ) 166 { 167 i = 0; 168 while ( i < MAX_TRACKED_BIGCHUNKS && pointers[i].base != NULL ) 169 i++; 170 171 if ( i >= MAX_TRACKED_BIGCHUNKS ) 172 /* We fail badly here. Increase MAX_TRACKED_BIGCHUNKS if needed. */ 173 return TT_Err_Invalid_Argument; 174 else 175 pointers[i].base = *P; 176 } 177 178 #endif /* DEBUG_MEMORY */ 179 180 /* The nice thing about GlobalAlloc is that it zeroes the memory. */ 181 182 if ( Size < MEMORY_MIN_GLOBAL ) 183 MEM_Set( *P, 0, Size ); 184 185 } 186 else 187 *P = NULL; 188 189 return TT_Err_Ok; 190 } 191 192 193 #ifdef TT_CONFIG_OPTION_EXTEND_ENGINE 194 195 196 /******************************************************************* 197 * 198 * Function : TT_Realloc 199 * 200 * Description : Reallocates memory from the heap buffer. 201 * 202 * Input : Size new size of the memory to be allocated; 203 * if zero, TT_Free() will be called 204 * P pointer to a buffer pointer; if *P == NULL, 205 * TT_Alloc() will be called 206 * 207 * Output : Error code. 208 * 209 * NOTES : It's not necessary to zero the memory in case the 210 * reallocated buffer is larger than before -- the 211 * application has to take care of this. 212 * 213 * If the memory request fails, TT_Free() will be 214 * called on *P, and TT_Err_Out_Of_Memory returned. 215 * 216 ******************************************************************/ 217 218 EXPORT_FUNC TT_Realloc(ULong Size,void ** P)219 TT_Error TT_Realloc( ULong Size, void** P ) 220 { 221 ULong oldSize; 222 void* Q; 223 Int i; 224 225 226 if ( !P ) 227 return TT_Err_Invalid_Argument; 228 229 if ( !*P ) 230 return TT_Alloc( Size, P ); 231 232 if ( Size == 0 ) 233 return TT_Free( P ); 234 235 #ifdef DEBUG_MEMORY 236 237 num_realloc++; 238 239 i = 0; 240 while ( i < MAX_TRACKED_BLOCKS && pointers[i].base != *P ) 241 i++; 242 243 if ( i >= MAX_TRACKED_BLOCKS ) 244 fail_realloc++; 245 else 246 oldSize = pointers[i].size; 247 248 #else 249 250 i = 0; 251 while ( i < MAX_TRACKED_BIGCHUNKS && pointers[i].base != *P ) 252 i++; 253 254 /* If we did not found the pointer, then this is a "small" chunk. */ 255 256 if ( i < MAX_TRACKED_BIGCHUNKS ) 257 { 258 /* Signal we found a big one. Real size does not matter. */ 259 oldSize = MEMORY_MIN_GLOBAL; 260 } 261 262 #endif /* DEBUG_MEMORY */ 263 264 if ( oldSize >= MEMORY_MIN_GLOBAL ) 265 { 266 /* Deal with a big chunk. */ 267 HANDLE hMem, hNewMem; 268 269 hMem = GlobalHandle ( (ULong)*P >> 16 ) & 0xFFFF; 270 if ( !hMem ) /* Bad call... */ 271 return TT_Err_Invalid_Argument; 272 273 GlobalUnlock( hMem ); 274 hNewMem = GlobalReAlloc( hMem, Size, 0 ); 275 if ( hNewMem ) 276 *P = (void*)GlobalLock( hNewMem ); 277 } 278 if ( Size >= MEMORY_MIN_GLOBAL ) 279 { 280 /* A small chunk crosses the limit... */ 281 282 if( TT_Alloc( Size, &Q ) != TT_Err_Ok ) 283 Q = NULL; /* Failed to create the new block. */ 284 else 285 MEM_Copy( Q, *P, oldSize ); 286 287 /* We need to register the new entry. */ 288 #ifndef DEBUG_MEMORY 289 290 i = 0; 291 while ( i < MAX_TRACKED_BIGCHUNKS && pointers[i].base != NULL ) 292 i++; 293 294 if ( i >= MAX_TRACKED_BIGCHUNKS ) 295 /* We fail badly here. Increase MAX_TRACKED_BIGCHUNKS if needed. */ 296 return TT_Err_Invalid_Argument; 297 #endif /* DEBUG_MEMORY */ 298 } 299 else 300 Q = (void*)realloc( *P, Size ); 301 302 if ( !Q ) 303 { 304 TT_Free( *P ); 305 return TT_Err_Out_Of_Memory; 306 } 307 308 #ifdef DEBUG_MEMORY 309 310 if ( i < MAX_TRACKED_BLOCKS ) 311 { 312 #ifndef TT_CONFIG_REENTRANT 313 TTMemory_Allocated += Size - pointers[i].size; 314 if ( Size > pointers[i].size ) 315 TTMemory_MaxAllocated += Size - pointers[i].size; 316 #endif 317 318 pointers[i].base = Q; 319 pointers[i].size = Size; 320 } 321 #else 322 if ( i < MAX_TRACKED_BIGCHUNKS ) 323 { 324 pointers[i].base = Q; 325 } 326 #endif /* DEBUG_MEMORY */ 327 328 *P = Q; 329 330 return TT_Err_Ok; 331 } 332 333 334 #endif /* TT_CONFIG_OPTION_EXTEND_ENGINE */ 335 336 337 /******************************************************************* 338 * 339 * Function : TT_Free 340 * 341 * Description : Releases a previously allocated block of memory. 342 * 343 * Input : P pointer to memory block 344 * 345 * Output : Always SUCCESS. 346 * 347 * Note : The pointer must _always_ be set to NULL by this function. 348 * 349 ******************************************************************/ 350 351 EXPORT_FUNC TT_Free(void ** P)352 TT_Error TT_Free( void** P ) 353 { 354 Int i; 355 Long Size = 0; 356 357 358 if ( !P || !*P ) 359 return TT_Err_Ok; 360 361 #ifdef DEBUG_MEMORY 362 363 num_free++; 364 365 i = 0; 366 while ( i < MAX_TRACKED_BLOCKS && pointers[i].base != *P ) 367 i++; 368 369 if ( i >= MAX_TRACKED_BLOCKS ) 370 fail_free++; 371 else 372 { 373 #ifndef TT_CONFIG_REENTRANT 374 TTMemory_Allocated -= pointers[i].size; 375 #endif 376 377 Size = pointers[i].size; 378 pointers[i].base = NULL; 379 pointers[i].size = 0; 380 } 381 382 #else 383 384 i = 0; 385 while ( i < MAX_TRACKED_BIGCHUNKS && pointers[i].base != *P ) 386 i++; 387 388 /* If we did not found the pointer, then this is a "small" chunk. */ 389 390 if ( i < MAX_TRACKED_BIGCHUNKS ) 391 { 392 pointers[i].base = NULL; 393 /* Signal we found a big one. Real size does not matter. */ 394 Size = MEMORY_MIN_GLOBAL; 395 } 396 397 #endif /* DEBUG_MEMORY */ 398 399 if ( Size >= MEMORY_MIN_GLOBAL ) 400 { 401 HANDLE hMem; 402 403 hMem = GlobalHandle ( (ULong)*P >> 16 ) & 0xFFFF; 404 if ( !hMem ) /* Bad call... */ 405 return TT_Err_Invalid_Argument; 406 407 GlobalUnlock( hMem ); 408 GlobalFree ( hMem ); 409 } 410 else 411 free( *P ); 412 413 *P = NULL; 414 415 return TT_Err_Ok; 416 } 417 418 419 /******************************************************************* 420 * 421 * Function : TTMemory_Init 422 * 423 * Description : Initializes the memory. 424 * 425 * Output : Always SUCCESS. 426 * 427 ******************************************************************/ 428 429 LOCAL_FUNC TTMemory_Init(void)430 TT_Error TTMemory_Init( void ) 431 { 432 #ifdef DEBUG_MEMORY 433 Int i; 434 435 436 for ( i = 0; i < MAX_TRACKED_BLOCKS; i++ ) 437 { 438 pointers[i].base = NULL; 439 pointers[i].size = 0; 440 } 441 442 num_alloc = 0; 443 num_realloc = 0; 444 num_free = 0; 445 446 fail_alloc = 0; 447 fail_realloc = 0; 448 fail_free = 0; 449 #else 450 Int i; 451 452 for ( i = 0; i < MAX_TRACKED_BIGCHUNKS; i++ ) 453 { 454 pointers[i].base = NULL; 455 } 456 #endif 457 458 459 #ifndef TT_CONFIG_REENTRANT 460 TTMemory_Allocated = 0; 461 TTMemory_MaxAllocated = 0; 462 #endif 463 464 return TT_Err_Ok; 465 } 466 467 468 /******************************************************************* 469 * 470 * Function : TTMemory_Done 471 * 472 * Description : Finalizes memory usage. 473 * 474 * Output : Always SUCCESS. 475 * 476 ******************************************************************/ 477 478 LOCAL_FUNC TTMemory_Done(void)479 TT_Error TTMemory_Done( void ) 480 { 481 #ifdef DEBUG_MEMORY 482 Int i, num_leaked, tot_leaked; 483 484 485 num_leaked = 0; 486 tot_leaked = 0; 487 488 for ( i = 0; i < MAX_TRACKED_BLOCKS; i++ ) 489 { 490 if ( pointers[i].base ) 491 { 492 num_leaked ++; 493 tot_leaked += pointers[i].size; 494 } 495 } 496 497 fprintf( stderr, 498 "%d memory allocations, of which %d failed\n", 499 num_alloc, 500 fail_alloc ); 501 502 fprintf( stderr, 503 "%d memory reallocations, of which %d failed\n", 504 num_realloc, 505 fail_realloc ); 506 507 fprintf( stderr, 508 "%d memory frees, of which %d failed\n", 509 num_free, 510 fail_free ); 511 512 if ( num_leaked > 0 ) 513 { 514 fprintf( stderr, 515 "There are %d leaked memory blocks, totalizing %d bytes\n", 516 num_leaked, tot_leaked ); 517 518 for ( i = 0; i < MAX_TRACKED_BLOCKS; i++ ) 519 { 520 if ( pointers[i].base ) 521 { 522 fprintf( stderr, 523 "index: %4d (base: $%08lx, size: %08ld)\n", 524 i, 525 (long)pointers[i].base, 526 pointers[i].size ); 527 } 528 } 529 } 530 else 531 fprintf( stderr, "No memory leaks !\n" ); 532 533 #endif /* DEBUG_MEMORY */ 534 535 return TT_Err_Ok; 536 } 537 538 539 /* END */ 540