[CMAKE]
[reactos.git] / lib / 3rdparty / freetype / src / base / ftdbgmem.c
1 /***************************************************************************/
2 /* */
3 /* ftdbgmem.c */
4 /* */
5 /* Memory debugger (body). */
6 /* */
7 /* Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2009 by */
8 /* David Turner, Robert Wilhelm, and Werner Lemberg. */
9 /* */
10 /* This file is part of the FreeType project, and may only be used, */
11 /* modified, and distributed under the terms of the FreeType project */
12 /* license, LICENSE.TXT. By continuing to use, modify, or distribute */
13 /* this file you indicate that you have read the license and */
14 /* understand and accept it fully. */
15 /* */
16 /***************************************************************************/
17
18
19 #include <ft2build.h>
20 #include FT_CONFIG_CONFIG_H
21 #include FT_INTERNAL_DEBUG_H
22 #include FT_INTERNAL_MEMORY_H
23 #include FT_SYSTEM_H
24 #include FT_ERRORS_H
25 #include FT_TYPES_H
26
27
28 #ifdef FT_DEBUG_MEMORY
29
30 #define KEEPALIVE /* `Keep alive' means that freed blocks aren't released
31 * to the heap. This is useful to detect double-frees
32 * or weird heap corruption, but it uses large amounts of
33 * memory, however.
34 */
35
36 #include FT_CONFIG_STANDARD_LIBRARY_H
37
38 FT_BASE_DEF( const char* ) _ft_debug_file = 0;
39 FT_BASE_DEF( long ) _ft_debug_lineno = 0;
40
41 extern void
42 FT_DumpMemory( FT_Memory memory );
43
44
45 typedef struct FT_MemSourceRec_* FT_MemSource;
46 typedef struct FT_MemNodeRec_* FT_MemNode;
47 typedef struct FT_MemTableRec_* FT_MemTable;
48
49
50 #define FT_MEM_VAL( addr ) ((FT_PtrDist)(FT_Pointer)( addr ))
51
52 /*
53 * This structure holds statistics for a single allocation/release
54 * site. This is useful to know where memory operations happen the
55 * most.
56 */
57 typedef struct FT_MemSourceRec_
58 {
59 const char* file_name;
60 long line_no;
61
62 FT_Long cur_blocks; /* current number of allocated blocks */
63 FT_Long max_blocks; /* max. number of allocated blocks */
64 FT_Long all_blocks; /* total number of blocks allocated */
65
66 FT_Long cur_size; /* current cumulative allocated size */
67 FT_Long max_size; /* maximum cumulative allocated size */
68 FT_Long all_size; /* total cumulative allocated size */
69
70 FT_Long cur_max; /* current maximum allocated size */
71
72 FT_UInt32 hash;
73 FT_MemSource link;
74
75 } FT_MemSourceRec;
76
77
78 /*
79 * We don't need a resizable array for the memory sources, because
80 * their number is pretty limited within FreeType.
81 */
82 #define FT_MEM_SOURCE_BUCKETS 128
83
84 /*
85 * This structure holds information related to a single allocated
86 * memory block. If KEEPALIVE is defined, blocks that are freed by
87 * FreeType are never released to the system. Instead, their `size'
88 * field is set to -size. This is mainly useful to detect double frees,
89 * at the price of large memory footprint during execution.
90 */
91 typedef struct FT_MemNodeRec_
92 {
93 FT_Byte* address;
94 FT_Long size; /* < 0 if the block was freed */
95
96 FT_MemSource source;
97
98 #ifdef KEEPALIVE
99 const char* free_file_name;
100 FT_Long free_line_no;
101 #endif
102
103 FT_MemNode link;
104
105 } FT_MemNodeRec;
106
107
108 /*
109 * The global structure, containing compound statistics and all hash
110 * tables.
111 */
112 typedef struct FT_MemTableRec_
113 {
114 FT_ULong size;
115 FT_ULong nodes;
116 FT_MemNode* buckets;
117
118 FT_ULong alloc_total;
119 FT_ULong alloc_current;
120 FT_ULong alloc_max;
121 FT_ULong alloc_count;
122
123 FT_Bool bound_total;
124 FT_ULong alloc_total_max;
125
126 FT_Bool bound_count;
127 FT_ULong alloc_count_max;
128
129 FT_MemSource sources[FT_MEM_SOURCE_BUCKETS];
130
131 FT_Bool keep_alive;
132
133 FT_Memory memory;
134 FT_Pointer memory_user;
135 FT_Alloc_Func alloc;
136 FT_Free_Func free;
137 FT_Realloc_Func realloc;
138
139 } FT_MemTableRec;
140
141
142 #define FT_MEM_SIZE_MIN 7
143 #define FT_MEM_SIZE_MAX 13845163
144
145 #define FT_FILENAME( x ) ((x) ? (x) : "unknown file")
146
147
148 /*
149 * Prime numbers are ugly to handle. It would be better to implement
150 * L-Hashing, which is 10% faster and doesn't require divisions.
151 */
152 static const FT_UInt ft_mem_primes[] =
153 {
154 7,
155 11,
156 19,
157 37,
158 73,
159 109,
160 163,
161 251,
162 367,
163 557,
164 823,
165 1237,
166 1861,
167 2777,
168 4177,
169 6247,
170 9371,
171 14057,
172 21089,
173 31627,
174 47431,
175 71143,
176 106721,
177 160073,
178 240101,
179 360163,
180 540217,
181 810343,
182 1215497,
183 1823231,
184 2734867,
185 4102283,
186 6153409,
187 9230113,
188 13845163,
189 };
190
191
192 static FT_ULong
193 ft_mem_closest_prime( FT_ULong num )
194 {
195 FT_UInt i;
196
197
198 for ( i = 0;
199 i < sizeof ( ft_mem_primes ) / sizeof ( ft_mem_primes[0] ); i++ )
200 if ( ft_mem_primes[i] > num )
201 return ft_mem_primes[i];
202
203 return FT_MEM_SIZE_MAX;
204 }
205
206
207 extern void
208 ft_mem_debug_panic( const char* fmt,
209 ... )
210 {
211 va_list ap;
212
213
214 printf( "FreeType.Debug: " );
215
216 va_start( ap, fmt );
217 vprintf( fmt, ap );
218 va_end( ap );
219
220 printf( "\n" );
221 exit( EXIT_FAILURE );
222 }
223
224
225 static FT_Pointer
226 ft_mem_table_alloc( FT_MemTable table,
227 FT_Long size )
228 {
229 FT_Memory memory = table->memory;
230 FT_Pointer block;
231
232
233 memory->user = table->memory_user;
234 block = table->alloc( memory, size );
235 memory->user = table;
236
237 return block;
238 }
239
240
241 static void
242 ft_mem_table_free( FT_MemTable table,
243 FT_Pointer block )
244 {
245 FT_Memory memory = table->memory;
246
247
248 memory->user = table->memory_user;
249 table->free( memory, block );
250 memory->user = table;
251 }
252
253
254 static void
255 ft_mem_table_resize( FT_MemTable table )
256 {
257 FT_ULong new_size;
258
259
260 new_size = ft_mem_closest_prime( table->nodes );
261 if ( new_size != table->size )
262 {
263 FT_MemNode* new_buckets;
264 FT_ULong i;
265
266
267 new_buckets = (FT_MemNode *)
268 ft_mem_table_alloc( table,
269 new_size * sizeof ( FT_MemNode ) );
270 if ( new_buckets == NULL )
271 return;
272
273 FT_ARRAY_ZERO( new_buckets, new_size );
274
275 for ( i = 0; i < table->size; i++ )
276 {
277 FT_MemNode node, next, *pnode;
278 FT_PtrDist hash;
279
280
281 node = table->buckets[i];
282 while ( node )
283 {
284 next = node->link;
285 hash = FT_MEM_VAL( node->address ) % new_size;
286 pnode = new_buckets + hash;
287
288 node->link = pnode[0];
289 pnode[0] = node;
290
291 node = next;
292 }
293 }
294
295 if ( table->buckets )
296 ft_mem_table_free( table, table->buckets );
297
298 table->buckets = new_buckets;
299 table->size = new_size;
300 }
301 }
302
303
304 static FT_MemTable
305 ft_mem_table_new( FT_Memory memory )
306 {
307 FT_MemTable table;
308
309
310 table = (FT_MemTable)memory->alloc( memory, sizeof ( *table ) );
311 if ( table == NULL )
312 goto Exit;
313
314 FT_ZERO( table );
315
316 table->size = FT_MEM_SIZE_MIN;
317 table->nodes = 0;
318
319 table->memory = memory;
320
321 table->memory_user = memory->user;
322
323 table->alloc = memory->alloc;
324 table->realloc = memory->realloc;
325 table->free = memory->free;
326
327 table->buckets = (FT_MemNode *)
328 memory->alloc( memory,
329 table->size * sizeof ( FT_MemNode ) );
330 if ( table->buckets )
331 FT_ARRAY_ZERO( table->buckets, table->size );
332 else
333 {
334 memory->free( memory, table );
335 table = NULL;
336 }
337
338 Exit:
339 return table;
340 }
341
342
343 static void
344 ft_mem_table_destroy( FT_MemTable table )
345 {
346 FT_ULong i;
347
348
349 FT_DumpMemory( table->memory );
350
351 if ( table )
352 {
353 FT_Long leak_count = 0;
354 FT_ULong leaks = 0;
355
356
357 /* remove all blocks from the table, revealing leaked ones */
358 for ( i = 0; i < table->size; i++ )
359 {
360 FT_MemNode *pnode = table->buckets + i, next, node = *pnode;
361
362
363 while ( node )
364 {
365 next = node->link;
366 node->link = 0;
367
368 if ( node->size > 0 )
369 {
370 printf(
371 "leaked memory block at address %p, size %8ld in (%s:%ld)\n",
372 node->address, node->size,
373 FT_FILENAME( node->source->file_name ),
374 node->source->line_no );
375
376 leak_count++;
377 leaks += node->size;
378
379 ft_mem_table_free( table, node->address );
380 }
381
382 node->address = NULL;
383 node->size = 0;
384
385 ft_mem_table_free( table, node );
386 node = next;
387 }
388 table->buckets[i] = 0;
389 }
390
391 ft_mem_table_free( table, table->buckets );
392 table->buckets = NULL;
393
394 table->size = 0;
395 table->nodes = 0;
396
397 /* remove all sources */
398 for ( i = 0; i < FT_MEM_SOURCE_BUCKETS; i++ )
399 {
400 FT_MemSource source, next;
401
402
403 for ( source = table->sources[i]; source != NULL; source = next )
404 {
405 next = source->link;
406 ft_mem_table_free( table, source );
407 }
408
409 table->sources[i] = NULL;
410 }
411
412 printf(
413 "FreeType: total memory allocations = %ld\n", table->alloc_total );
414 printf(
415 "FreeType: maximum memory footprint = %ld\n", table->alloc_max );
416
417 ft_mem_table_free( table, table );
418
419 if ( leak_count > 0 )
420 ft_mem_debug_panic(
421 "FreeType: %ld bytes of memory leaked in %ld blocks\n",
422 leaks, leak_count );
423
424 printf( "FreeType: no memory leaks detected\n" );
425 }
426 }
427
428
429 static FT_MemNode*
430 ft_mem_table_get_nodep( FT_MemTable table,
431 FT_Byte* address )
432 {
433 FT_PtrDist hash;
434 FT_MemNode *pnode, node;
435
436
437 hash = FT_MEM_VAL( address );
438 pnode = table->buckets + ( hash % table->size );
439
440 for (;;)
441 {
442 node = pnode[0];
443 if ( !node )
444 break;
445
446 if ( node->address == address )
447 break;
448
449 pnode = &node->link;
450 }
451 return pnode;
452 }
453
454
455 static FT_MemSource
456 ft_mem_table_get_source( FT_MemTable table )
457 {
458 FT_UInt32 hash;
459 FT_MemSource node, *pnode;
460
461
462 /* cast to FT_PtrDist first since void* can be larger */
463 /* than FT_UInt32 and GCC 4.1.1 emits a warning */
464 hash = (FT_UInt32)(FT_PtrDist)(void*)_ft_debug_file +
465 (FT_UInt32)( 5 * _ft_debug_lineno );
466 pnode = &table->sources[hash % FT_MEM_SOURCE_BUCKETS];
467
468 for ( ;; )
469 {
470 node = *pnode;
471 if ( node == NULL )
472 break;
473
474 if ( node->file_name == _ft_debug_file &&
475 node->line_no == _ft_debug_lineno )
476 goto Exit;
477
478 pnode = &node->link;
479 }
480
481 node = (FT_MemSource)ft_mem_table_alloc( table, sizeof ( *node ) );
482 if ( node == NULL )
483 ft_mem_debug_panic(
484 "not enough memory to perform memory debugging\n" );
485
486 node->file_name = _ft_debug_file;
487 node->line_no = _ft_debug_lineno;
488
489 node->cur_blocks = 0;
490 node->max_blocks = 0;
491 node->all_blocks = 0;
492
493 node->cur_size = 0;
494 node->max_size = 0;
495 node->all_size = 0;
496
497 node->cur_max = 0;
498
499 node->link = NULL;
500 node->hash = hash;
501 *pnode = node;
502
503 Exit:
504 return node;
505 }
506
507
508 static void
509 ft_mem_table_set( FT_MemTable table,
510 FT_Byte* address,
511 FT_ULong size,
512 FT_Long delta )
513 {
514 FT_MemNode *pnode, node;
515
516
517 if ( table )
518 {
519 FT_MemSource source;
520
521
522 pnode = ft_mem_table_get_nodep( table, address );
523 node = *pnode;
524 if ( node )
525 {
526 if ( node->size < 0 )
527 {
528 /* This block was already freed. Our memory is now completely */
529 /* corrupted! */
530 /* This can only happen in keep-alive mode. */
531 ft_mem_debug_panic(
532 "memory heap corrupted (allocating freed block)" );
533 }
534 else
535 {
536 /* This block was already allocated. This means that our memory */
537 /* is also corrupted! */
538 ft_mem_debug_panic(
539 "memory heap corrupted (re-allocating allocated block at"
540 " %p, of size %ld)\n"
541 "org=%s:%d new=%s:%d\n",
542 node->address, node->size,
543 FT_FILENAME( node->source->file_name ), node->source->line_no,
544 FT_FILENAME( _ft_debug_file ), _ft_debug_lineno );
545 }
546 }
547
548 /* we need to create a new node in this table */
549 node = (FT_MemNode)ft_mem_table_alloc( table, sizeof ( *node ) );
550 if ( node == NULL )
551 ft_mem_debug_panic( "not enough memory to run memory tests" );
552
553 node->address = address;
554 node->size = size;
555 node->source = source = ft_mem_table_get_source( table );
556
557 if ( delta == 0 )
558 {
559 /* this is an allocation */
560 source->all_blocks++;
561 source->cur_blocks++;
562 if ( source->cur_blocks > source->max_blocks )
563 source->max_blocks = source->cur_blocks;
564 }
565
566 if ( size > (FT_ULong)source->cur_max )
567 source->cur_max = size;
568
569 if ( delta != 0 )
570 {
571 /* we are growing or shrinking a reallocated block */
572 source->cur_size += delta;
573 table->alloc_current += delta;
574 }
575 else
576 {
577 /* we are allocating a new block */
578 source->cur_size += size;
579 table->alloc_current += size;
580 }
581
582 source->all_size += size;
583
584 if ( source->cur_size > source->max_size )
585 source->max_size = source->cur_size;
586
587 node->free_file_name = NULL;
588 node->free_line_no = 0;
589
590 node->link = pnode[0];
591
592 pnode[0] = node;
593 table->nodes++;
594
595 table->alloc_total += size;
596
597 if ( table->alloc_current > table->alloc_max )
598 table->alloc_max = table->alloc_current;
599
600 if ( table->nodes * 3 < table->size ||
601 table->size * 3 < table->nodes )
602 ft_mem_table_resize( table );
603 }
604 }
605
606
607 static void
608 ft_mem_table_remove( FT_MemTable table,
609 FT_Byte* address,
610 FT_Long delta )
611 {
612 if ( table )
613 {
614 FT_MemNode *pnode, node;
615
616
617 pnode = ft_mem_table_get_nodep( table, address );
618 node = *pnode;
619 if ( node )
620 {
621 FT_MemSource source;
622
623
624 if ( node->size < 0 )
625 ft_mem_debug_panic(
626 "freeing memory block at %p more than once at (%s:%ld)\n"
627 "block allocated at (%s:%ld) and released at (%s:%ld)",
628 address,
629 FT_FILENAME( _ft_debug_file ), _ft_debug_lineno,
630 FT_FILENAME( node->source->file_name ), node->source->line_no,
631 FT_FILENAME( node->free_file_name ), node->free_line_no );
632
633 /* scramble the node's content for additional safety */
634 FT_MEM_SET( address, 0xF3, node->size );
635
636 if ( delta == 0 )
637 {
638 source = node->source;
639
640 source->cur_blocks--;
641 source->cur_size -= node->size;
642
643 table->alloc_current -= node->size;
644 }
645
646 if ( table->keep_alive )
647 {
648 /* we simply invert the node's size to indicate that the node */
649 /* was freed. */
650 node->size = -node->size;
651 node->free_file_name = _ft_debug_file;
652 node->free_line_no = _ft_debug_lineno;
653 }
654 else
655 {
656 table->nodes--;
657
658 *pnode = node->link;
659
660 node->size = 0;
661 node->source = NULL;
662
663 ft_mem_table_free( table, node );
664
665 if ( table->nodes * 3 < table->size ||
666 table->size * 3 < table->nodes )
667 ft_mem_table_resize( table );
668 }
669 }
670 else
671 ft_mem_debug_panic(
672 "trying to free unknown block at %p in (%s:%ld)\n",
673 address,
674 FT_FILENAME( _ft_debug_file ), _ft_debug_lineno );
675 }
676 }
677
678
679 extern FT_Pointer
680 ft_mem_debug_alloc( FT_Memory memory,
681 FT_Long size )
682 {
683 FT_MemTable table = (FT_MemTable)memory->user;
684 FT_Byte* block;
685
686
687 if ( size <= 0 )
688 ft_mem_debug_panic( "negative block size allocation (%ld)", size );
689
690 /* return NULL if the maximum number of allocations was reached */
691 if ( table->bound_count &&
692 table->alloc_count >= table->alloc_count_max )
693 return NULL;
694
695 /* return NULL if this allocation would overflow the maximum heap size */
696 if ( table->bound_total &&
697 table->alloc_total_max - table->alloc_current > (FT_ULong)size )
698 return NULL;
699
700 block = (FT_Byte *)ft_mem_table_alloc( table, size );
701 if ( block )
702 {
703 ft_mem_table_set( table, block, (FT_ULong)size, 0 );
704
705 table->alloc_count++;
706 }
707
708 _ft_debug_file = "<unknown>";
709 _ft_debug_lineno = 0;
710
711 return (FT_Pointer)block;
712 }
713
714
715 extern void
716 ft_mem_debug_free( FT_Memory memory,
717 FT_Pointer block )
718 {
719 FT_MemTable table = (FT_MemTable)memory->user;
720
721
722 if ( block == NULL )
723 ft_mem_debug_panic( "trying to free NULL in (%s:%ld)",
724 FT_FILENAME( _ft_debug_file ),
725 _ft_debug_lineno );
726
727 ft_mem_table_remove( table, (FT_Byte*)block, 0 );
728
729 if ( !table->keep_alive )
730 ft_mem_table_free( table, block );
731
732 table->alloc_count--;
733
734 _ft_debug_file = "<unknown>";
735 _ft_debug_lineno = 0;
736 }
737
738
739 extern FT_Pointer
740 ft_mem_debug_realloc( FT_Memory memory,
741 FT_Long cur_size,
742 FT_Long new_size,
743 FT_Pointer block )
744 {
745 FT_MemTable table = (FT_MemTable)memory->user;
746 FT_MemNode node, *pnode;
747 FT_Pointer new_block;
748 FT_Long delta;
749
750 const char* file_name = FT_FILENAME( _ft_debug_file );
751 FT_Long line_no = _ft_debug_lineno;
752
753
754 /* unlikely, but possible */
755 if ( new_size == cur_size )
756 return block;
757
758 /* the following is valid according to ANSI C */
759 #if 0
760 if ( block == NULL || cur_size == 0 )
761 ft_mem_debug_panic( "trying to reallocate NULL in (%s:%ld)",
762 file_name, line_no );
763 #endif
764
765 /* while the following is allowed in ANSI C also, we abort since */
766 /* such case should be handled by FreeType. */
767 if ( new_size <= 0 )
768 ft_mem_debug_panic(
769 "trying to reallocate %p to size 0 (current is %ld) in (%s:%ld)",
770 block, cur_size, file_name, line_no );
771
772 /* check `cur_size' value */
773 pnode = ft_mem_table_get_nodep( table, (FT_Byte*)block );
774 node = *pnode;
775 if ( !node )
776 ft_mem_debug_panic(
777 "trying to reallocate unknown block at %p in (%s:%ld)",
778 block, file_name, line_no );
779
780 if ( node->size <= 0 )
781 ft_mem_debug_panic(
782 "trying to reallocate freed block at %p in (%s:%ld)",
783 block, file_name, line_no );
784
785 if ( node->size != cur_size )
786 ft_mem_debug_panic( "invalid ft_realloc request for %p. cur_size is "
787 "%ld instead of %ld in (%s:%ld)",
788 block, cur_size, node->size, file_name, line_no );
789
790 /* return NULL if the maximum number of allocations was reached */
791 if ( table->bound_count &&
792 table->alloc_count >= table->alloc_count_max )
793 return NULL;
794
795 delta = (FT_Long)( new_size - cur_size );
796
797 /* return NULL if this allocation would overflow the maximum heap size */
798 if ( delta > 0 &&
799 table->bound_total &&
800 table->alloc_current + (FT_ULong)delta > table->alloc_total_max )
801 return NULL;
802
803 new_block = (FT_Byte *)ft_mem_table_alloc( table, new_size );
804 if ( new_block == NULL )
805 return NULL;
806
807 ft_mem_table_set( table, (FT_Byte*)new_block, new_size, delta );
808
809 ft_memcpy( new_block, block, cur_size < new_size ? cur_size : new_size );
810
811 ft_mem_table_remove( table, (FT_Byte*)block, delta );
812
813 _ft_debug_file = "<unknown>";
814 _ft_debug_lineno = 0;
815
816 if ( !table->keep_alive )
817 ft_mem_table_free( table, block );
818
819 return new_block;
820 }
821
822
823 extern FT_Int
824 ft_mem_debug_init( FT_Memory memory )
825 {
826 FT_MemTable table;
827 FT_Int result = 0;
828
829
830 if ( getenv( "FT2_DEBUG_MEMORY" ) )
831 {
832 table = ft_mem_table_new( memory );
833 if ( table )
834 {
835 const char* p;
836
837
838 memory->user = table;
839 memory->alloc = ft_mem_debug_alloc;
840 memory->realloc = ft_mem_debug_realloc;
841 memory->free = ft_mem_debug_free;
842
843 p = getenv( "FT2_ALLOC_TOTAL_MAX" );
844 if ( p != NULL )
845 {
846 FT_Long total_max = ft_atol( p );
847
848
849 if ( total_max > 0 )
850 {
851 table->bound_total = 1;
852 table->alloc_total_max = (FT_ULong)total_max;
853 }
854 }
855
856 p = getenv( "FT2_ALLOC_COUNT_MAX" );
857 if ( p != NULL )
858 {
859 FT_Long total_count = ft_atol( p );
860
861
862 if ( total_count > 0 )
863 {
864 table->bound_count = 1;
865 table->alloc_count_max = (FT_ULong)total_count;
866 }
867 }
868
869 p = getenv( "FT2_KEEP_ALIVE" );
870 if ( p != NULL )
871 {
872 FT_Long keep_alive = ft_atol( p );
873
874
875 if ( keep_alive > 0 )
876 table->keep_alive = 1;
877 }
878
879 result = 1;
880 }
881 }
882 return result;
883 }
884
885
886 extern void
887 ft_mem_debug_done( FT_Memory memory )
888 {
889 FT_MemTable table = (FT_MemTable)memory->user;
890
891
892 if ( table )
893 {
894 memory->free = table->free;
895 memory->realloc = table->realloc;
896 memory->alloc = table->alloc;
897
898 ft_mem_table_destroy( table );
899 memory->user = NULL;
900 }
901 }
902
903
904
905 static int
906 ft_mem_source_compare( const void* p1,
907 const void* p2 )
908 {
909 FT_MemSource s1 = *(FT_MemSource*)p1;
910 FT_MemSource s2 = *(FT_MemSource*)p2;
911
912
913 if ( s2->max_size > s1->max_size )
914 return 1;
915 else if ( s2->max_size < s1->max_size )
916 return -1;
917 else
918 return 0;
919 }
920
921
922 extern void
923 FT_DumpMemory( FT_Memory memory )
924 {
925 FT_MemTable table = (FT_MemTable)memory->user;
926
927
928 if ( table )
929 {
930 FT_MemSource* bucket = table->sources;
931 FT_MemSource* limit = bucket + FT_MEM_SOURCE_BUCKETS;
932 FT_MemSource* sources;
933 FT_UInt nn, count;
934 const char* fmt;
935
936
937 count = 0;
938 for ( ; bucket < limit; bucket++ )
939 {
940 FT_MemSource source = *bucket;
941
942
943 for ( ; source; source = source->link )
944 count++;
945 }
946
947 sources = (FT_MemSource*)ft_mem_table_alloc(
948 table, sizeof ( *sources ) * count );
949
950 count = 0;
951 for ( bucket = table->sources; bucket < limit; bucket++ )
952 {
953 FT_MemSource source = *bucket;
954
955
956 for ( ; source; source = source->link )
957 sources[count++] = source;
958 }
959
960 ft_qsort( sources, count, sizeof ( *sources ), ft_mem_source_compare );
961
962 printf( "FreeType Memory Dump: "
963 "current=%ld max=%ld total=%ld count=%ld\n",
964 table->alloc_current, table->alloc_max,
965 table->alloc_total, table->alloc_count );
966 printf( " block block sizes sizes sizes source\n" );
967 printf( " count high sum highsum max location\n" );
968 printf( "-------------------------------------------------\n" );
969
970 fmt = "%6ld %6ld %8ld %8ld %8ld %s:%d\n";
971
972 for ( nn = 0; nn < count; nn++ )
973 {
974 FT_MemSource source = sources[nn];
975
976
977 printf( fmt,
978 source->cur_blocks, source->max_blocks,
979 source->cur_size, source->max_size, source->cur_max,
980 FT_FILENAME( source->file_name ),
981 source->line_no );
982 }
983 printf( "------------------------------------------------\n" );
984
985 ft_mem_table_free( table, sources );
986 }
987 }
988
989 #else /* !FT_DEBUG_MEMORY */
990
991 /* ANSI C doesn't like empty source files */
992 typedef int _debug_mem_dummy;
993
994 #endif /* !FT_DEBUG_MEMORY */
995
996
997 /* END */