XFA: merge patch from CL 817753002
[pdfium.git] / core / src / fxge / fx_freetype / fxft2.5.01 / src / base / ftdbgmem.c
1 /***************************************************************************/
2 /*                                                                         */
3 /*  ftdbgmem.c                                                             */
4 /*                                                                         */
5 /*    Memory debugger (body).                                              */
6 /*                                                                         */
7 /*  Copyright 2001-2006, 2009, 2013 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 "../../include/ft2build.h"
20 #include "../../include/freetype/config/ftconfig.h"
21 #include "../../include/freetype/internal/ftdebug.h"
22 #include "../../include/freetype/internal/ftmemory.h"
23 #include "../../include/freetype/ftsystem.h"
24 #include "../../include/freetype/fterrors.h"
25 #include "../../include/freetype/fttypes.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 "../../include/freetype/config/ftstdlib.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     FT_Long   leak_count = 0;
348     FT_ULong  leaks      = 0;
349
350
351     FT_DumpMemory( table->memory );
352
353     /* remove all blocks from the table, revealing leaked ones */
354     for ( i = 0; i < table->size; i++ )
355     {
356       FT_MemNode  *pnode = table->buckets + i, next, node = *pnode;
357
358
359       while ( node )
360       {
361         next       = node->link;
362         node->link = 0;
363
364         if ( node->size > 0 )
365         {
366           printf(
367             "leaked memory block at address %p, size %8ld in (%s:%ld)\n",
368             node->address, node->size,
369             FT_FILENAME( node->source->file_name ),
370             node->source->line_no );
371
372           leak_count++;
373           leaks += node->size;
374
375           ft_mem_table_free( table, node->address );
376         }
377
378         node->address = NULL;
379         node->size    = 0;
380
381         ft_mem_table_free( table, node );
382         node = next;
383       }
384       table->buckets[i] = 0;
385     }
386
387     ft_mem_table_free( table, table->buckets );
388     table->buckets = NULL;
389
390     table->size  = 0;
391     table->nodes = 0;
392
393     /* remove all sources */
394     for ( i = 0; i < FT_MEM_SOURCE_BUCKETS; i++ )
395     {
396       FT_MemSource  source, next;
397
398
399       for ( source = table->sources[i]; source != NULL; source = next )
400       {
401         next = source->link;
402         ft_mem_table_free( table, source );
403       }
404
405       table->sources[i] = NULL;
406     }
407
408     printf( "FreeType: total memory allocations = %ld\n",
409             table->alloc_total );
410     printf( "FreeType: maximum memory footprint = %ld\n",
411             table->alloc_max );
412
413     ft_mem_table_free( table, table );
414
415     if ( leak_count > 0 )
416       ft_mem_debug_panic(
417         "FreeType: %ld bytes of memory leaked in %ld blocks\n",
418         leaks, leak_count );
419
420     printf( "FreeType: no memory leaks detected\n" );
421   }
422
423
424   static FT_MemNode*
425   ft_mem_table_get_nodep( FT_MemTable  table,
426                           FT_Byte*     address )
427   {
428     FT_PtrDist   hash;
429     FT_MemNode  *pnode, node;
430
431
432     hash  = FT_MEM_VAL( address );
433     pnode = table->buckets + ( hash % table->size );
434
435     for (;;)
436     {
437       node = pnode[0];
438       if ( !node )
439         break;
440
441       if ( node->address == address )
442         break;
443
444       pnode = &node->link;
445     }
446     return pnode;
447   }
448
449
450   static FT_MemSource
451   ft_mem_table_get_source( FT_MemTable  table )
452   {
453     FT_UInt32     hash;
454     FT_MemSource  node, *pnode;
455
456
457     /* cast to FT_PtrDist first since void* can be larger */
458     /* than FT_UInt32 and GCC 4.1.1 emits a warning       */
459     hash  = (FT_UInt32)(FT_PtrDist)(void*)_ft_debug_file +
460               (FT_UInt32)( 5 * _ft_debug_lineno );
461     pnode = &table->sources[hash % FT_MEM_SOURCE_BUCKETS];
462
463     for ( ;; )
464     {
465       node = *pnode;
466       if ( node == NULL )
467         break;
468
469       if ( node->file_name == _ft_debug_file &&
470            node->line_no   == _ft_debug_lineno   )
471         goto Exit;
472
473       pnode = &node->link;
474     }
475
476     node = (FT_MemSource)ft_mem_table_alloc( table, sizeof ( *node ) );
477     if ( node == NULL )
478       ft_mem_debug_panic(
479         "not enough memory to perform memory debugging\n" );
480
481     node->file_name = _ft_debug_file;
482     node->line_no   = _ft_debug_lineno;
483
484     node->cur_blocks = 0;
485     node->max_blocks = 0;
486     node->all_blocks = 0;
487
488     node->cur_size   = 0;
489     node->max_size   = 0;
490     node->all_size   = 0;
491
492     node->cur_max    = 0;
493
494     node->link = NULL;
495     node->hash = hash;
496     *pnode     = node;
497
498   Exit:
499     return node;
500   }
501
502
503   static void
504   ft_mem_table_set( FT_MemTable  table,
505                     FT_Byte*     address,
506                     FT_ULong     size,
507                     FT_Long      delta )
508   {
509     FT_MemNode  *pnode, node;
510
511
512     if ( table )
513     {
514       FT_MemSource  source;
515
516
517       pnode = ft_mem_table_get_nodep( table, address );
518       node  = *pnode;
519       if ( node )
520       {
521         if ( node->size < 0 )
522         {
523           /* This block was already freed.  Our memory is now completely */
524           /* corrupted!                                                  */
525           /* This can only happen in keep-alive mode.                    */
526           ft_mem_debug_panic(
527             "memory heap corrupted (allocating freed block)" );
528         }
529         else
530         {
531           /* This block was already allocated.  This means that our memory */
532           /* is also corrupted!                                            */
533           ft_mem_debug_panic(
534             "memory heap corrupted (re-allocating allocated block at"
535             " %p, of size %ld)\n"
536             "org=%s:%d new=%s:%d\n",
537             node->address, node->size,
538             FT_FILENAME( node->source->file_name ), node->source->line_no,
539             FT_FILENAME( _ft_debug_file ), _ft_debug_lineno );
540         }
541       }
542
543       /* we need to create a new node in this table */
544       node = (FT_MemNode)ft_mem_table_alloc( table, sizeof ( *node ) );
545       if ( node == NULL )
546         ft_mem_debug_panic( "not enough memory to run memory tests" );
547
548       node->address = address;
549       node->size    = size;
550       node->source  = source = ft_mem_table_get_source( table );
551
552       if ( delta == 0 )
553       {
554         /* this is an allocation */
555         source->all_blocks++;
556         source->cur_blocks++;
557         if ( source->cur_blocks > source->max_blocks )
558           source->max_blocks = source->cur_blocks;
559       }
560
561       if ( size > (FT_ULong)source->cur_max )
562         source->cur_max = size;
563
564       if ( delta != 0 )
565       {
566         /* we are growing or shrinking a reallocated block */
567         source->cur_size     += delta;
568         table->alloc_current += delta;
569       }
570       else
571       {
572         /* we are allocating a new block */
573         source->cur_size     += size;
574         table->alloc_current += size;
575       }
576
577       source->all_size += size;
578
579       if ( source->cur_size > source->max_size )
580         source->max_size = source->cur_size;
581
582       node->free_file_name = NULL;
583       node->free_line_no   = 0;
584
585       node->link = pnode[0];
586
587       pnode[0] = node;
588       table->nodes++;
589
590       table->alloc_total += size;
591
592       if ( table->alloc_current > table->alloc_max )
593         table->alloc_max = table->alloc_current;
594
595       if ( table->nodes * 3 < table->size  ||
596            table->size  * 3 < table->nodes )
597         ft_mem_table_resize( table );
598     }
599   }
600
601
602   static void
603   ft_mem_table_remove( FT_MemTable  table,
604                        FT_Byte*     address,
605                        FT_Long      delta )
606   {
607     if ( table )
608     {
609       FT_MemNode  *pnode, node;
610
611
612       pnode = ft_mem_table_get_nodep( table, address );
613       node  = *pnode;
614       if ( node )
615       {
616         FT_MemSource  source;
617
618
619         if ( node->size < 0 )
620           ft_mem_debug_panic(
621             "freeing memory block at %p more than once at (%s:%ld)\n"
622             "block allocated at (%s:%ld) and released at (%s:%ld)",
623             address,
624             FT_FILENAME( _ft_debug_file ), _ft_debug_lineno,
625             FT_FILENAME( node->source->file_name ), node->source->line_no,
626             FT_FILENAME( node->free_file_name ), node->free_line_no );
627
628         /* scramble the node's content for additional safety */
629         FT_MEM_SET( address, 0xF3, node->size );
630
631         if ( delta == 0 )
632         {
633           source = node->source;
634
635           source->cur_blocks--;
636           source->cur_size -= node->size;
637
638           table->alloc_current -= node->size;
639         }
640
641         if ( table->keep_alive )
642         {
643           /* we simply invert the node's size to indicate that the node */
644           /* was freed.                                                 */
645           node->size           = -node->size;
646           node->free_file_name = _ft_debug_file;
647           node->free_line_no   = _ft_debug_lineno;
648         }
649         else
650         {
651           table->nodes--;
652
653           *pnode = node->link;
654
655           node->size   = 0;
656           node->source = NULL;
657
658           ft_mem_table_free( table, node );
659
660           if ( table->nodes * 3 < table->size  ||
661                table->size  * 3 < table->nodes )
662             ft_mem_table_resize( table );
663         }
664       }
665       else
666         ft_mem_debug_panic(
667           "trying to free unknown block at %p in (%s:%ld)\n",
668           address,
669           FT_FILENAME( _ft_debug_file ), _ft_debug_lineno );
670     }
671   }
672
673
674   extern FT_Pointer
675   ft_mem_debug_alloc( FT_Memory  memory,
676                       FT_Long    size )
677   {
678     FT_MemTable  table = (FT_MemTable)memory->user;
679     FT_Byte*     block;
680
681
682     if ( size <= 0 )
683       ft_mem_debug_panic( "negative block size allocation (%ld)", size );
684
685     /* return NULL if the maximum number of allocations was reached */
686     if ( table->bound_count                           &&
687          table->alloc_count >= table->alloc_count_max )
688       return NULL;
689
690     /* return NULL if this allocation would overflow the maximum heap size */
691     if ( table->bound_total                                             &&
692          table->alloc_total_max - table->alloc_current > (FT_ULong)size )
693       return NULL;
694
695     block = (FT_Byte *)ft_mem_table_alloc( table, size );
696     if ( block )
697     {
698       ft_mem_table_set( table, block, (FT_ULong)size, 0 );
699
700       table->alloc_count++;
701     }
702
703     _ft_debug_file   = "<unknown>";
704     _ft_debug_lineno = 0;
705
706     return (FT_Pointer)block;
707   }
708
709
710   extern void
711   ft_mem_debug_free( FT_Memory   memory,
712                      FT_Pointer  block )
713   {
714     FT_MemTable  table = (FT_MemTable)memory->user;
715
716
717     if ( block == NULL )
718       ft_mem_debug_panic( "trying to free NULL in (%s:%ld)",
719                           FT_FILENAME( _ft_debug_file ),
720                           _ft_debug_lineno );
721
722     ft_mem_table_remove( table, (FT_Byte*)block, 0 );
723
724     if ( !table->keep_alive )
725       ft_mem_table_free( table, block );
726
727     table->alloc_count--;
728
729     _ft_debug_file   = "<unknown>";
730     _ft_debug_lineno = 0;
731   }
732
733
734   extern FT_Pointer
735   ft_mem_debug_realloc( FT_Memory   memory,
736                         FT_Long     cur_size,
737                         FT_Long     new_size,
738                         FT_Pointer  block )
739   {
740     FT_MemTable  table = (FT_MemTable)memory->user;
741     FT_MemNode   node, *pnode;
742     FT_Pointer   new_block;
743     FT_Long      delta;
744
745     const char*  file_name = FT_FILENAME( _ft_debug_file );
746     FT_Long      line_no   = _ft_debug_lineno;
747
748
749     /* unlikely, but possible */
750     if ( new_size == cur_size )
751       return block;
752
753     /* the following is valid according to ANSI C */
754 #if 0
755     if ( block == NULL || cur_size == 0 )
756       ft_mem_debug_panic( "trying to reallocate NULL in (%s:%ld)",
757                           file_name, line_no );
758 #endif
759
760     /* while the following is allowed in ANSI C also, we abort since */
761     /* such case should be handled by FreeType.                      */
762     if ( new_size <= 0 )
763       ft_mem_debug_panic(
764         "trying to reallocate %p to size 0 (current is %ld) in (%s:%ld)",
765         block, cur_size, file_name, line_no );
766
767     /* check `cur_size' value */
768     pnode = ft_mem_table_get_nodep( table, (FT_Byte*)block );
769     node  = *pnode;
770     if ( !node )
771       ft_mem_debug_panic(
772         "trying to reallocate unknown block at %p in (%s:%ld)",
773         block, file_name, line_no );
774
775     if ( node->size <= 0 )
776       ft_mem_debug_panic(
777         "trying to reallocate freed block at %p in (%s:%ld)",
778         block, file_name, line_no );
779
780     if ( node->size != cur_size )
781       ft_mem_debug_panic( "invalid ft_realloc request for %p. cur_size is "
782                           "%ld instead of %ld in (%s:%ld)",
783                           block, cur_size, node->size, file_name, line_no );
784
785     /* return NULL if the maximum number of allocations was reached */
786     if ( table->bound_count                           &&
787          table->alloc_count >= table->alloc_count_max )
788       return NULL;
789
790     delta = (FT_Long)( new_size - cur_size );
791
792     /* return NULL if this allocation would overflow the maximum heap size */
793     if ( delta > 0                                                       &&
794          table->bound_total                                              &&
795          table->alloc_current + (FT_ULong)delta > table->alloc_total_max )
796       return NULL;
797
798     new_block = (FT_Byte *)ft_mem_table_alloc( table, new_size );
799     if ( new_block == NULL )
800       return NULL;
801
802     ft_mem_table_set( table, (FT_Byte*)new_block, new_size, delta );
803
804     ft_memcpy( new_block, block, cur_size < new_size ? cur_size : new_size );
805
806     ft_mem_table_remove( table, (FT_Byte*)block, delta );
807
808     _ft_debug_file   = "<unknown>";
809     _ft_debug_lineno = 0;
810
811     if ( !table->keep_alive )
812       ft_mem_table_free( table, block );
813
814     return new_block;
815   }
816
817
818   extern FT_Int
819   ft_mem_debug_init( FT_Memory  memory )
820   {
821     FT_MemTable  table;
822     FT_Int       result = 0;
823
824
825     if (1)/* XYQ: always enable ( getenv( "FT2_DEBUG_MEMORY" ) ) */
826     {
827       table = ft_mem_table_new( memory );
828       if ( table )
829       {
830         const char*  p;
831
832
833         memory->user    = table;
834         memory->alloc   = ft_mem_debug_alloc;
835         memory->realloc = ft_mem_debug_realloc;
836         memory->free    = ft_mem_debug_free;
837
838         p = getenv( "FT2_ALLOC_TOTAL_MAX" );
839         if ( p != NULL )
840         {
841           FT_Long   total_max = ft_atol( p );
842
843
844           if ( total_max > 0 )
845           {
846             table->bound_total     = 1;
847             table->alloc_total_max = (FT_ULong)total_max;
848           }
849         }
850
851         p = getenv( "FT2_ALLOC_COUNT_MAX" );
852         if ( p != NULL )
853         {
854           FT_Long  total_count = ft_atol( p );
855
856
857           if ( total_count > 0 )
858           {
859             table->bound_count     = 1;
860             table->alloc_count_max = (FT_ULong)total_count;
861           }
862         }
863
864         p = getenv( "FT2_KEEP_ALIVE" );
865         if ( p != NULL )
866         {
867           FT_Long  keep_alive = ft_atol( p );
868
869
870           if ( keep_alive > 0 )
871             table->keep_alive = 1;
872         }
873
874         result = 1;
875       }
876     }
877     return result;
878   }
879
880
881   extern void
882   ft_mem_debug_done( FT_Memory  memory )
883   {
884     FT_MemTable  table = (FT_MemTable)memory->user;
885
886
887     if ( table )
888     {
889       memory->free    = table->free;
890       memory->realloc = table->realloc;
891       memory->alloc   = table->alloc;
892
893       ft_mem_table_destroy( table );
894       memory->user = NULL;
895     }
896   }
897
898
899
900   static int
901   ft_mem_source_compare( const void*  p1,
902                          const void*  p2 )
903   {
904     FT_MemSource  s1 = *(FT_MemSource*)p1;
905     FT_MemSource  s2 = *(FT_MemSource*)p2;
906
907
908     if ( s2->max_size > s1->max_size )
909       return 1;
910     else if ( s2->max_size < s1->max_size )
911       return -1;
912     else
913       return 0;
914   }
915
916
917   extern void
918   FT_DumpMemory( FT_Memory  memory )
919   {
920     FT_MemTable  table = (FT_MemTable)memory->user;
921
922
923     if ( table )
924     {
925       FT_MemSource*  bucket = table->sources;
926       FT_MemSource*  limit  = bucket + FT_MEM_SOURCE_BUCKETS;
927       FT_MemSource*  sources;
928       FT_UInt        nn, count;
929       const char*    fmt;
930
931
932       count = 0;
933       for ( ; bucket < limit; bucket++ )
934       {
935         FT_MemSource  source = *bucket;
936
937
938         for ( ; source; source = source->link )
939           count++;
940       }
941
942       sources = (FT_MemSource*)ft_mem_table_alloc(
943                                  table, sizeof ( *sources ) * count );
944
945       count = 0;
946       for ( bucket = table->sources; bucket < limit; bucket++ )
947       {
948         FT_MemSource  source = *bucket;
949
950
951         for ( ; source; source = source->link )
952           sources[count++] = source;
953       }
954
955       ft_qsort( sources, count, sizeof ( *sources ), ft_mem_source_compare );
956
957       printf( "FreeType Memory Dump: "
958               "current=%ld max=%ld total=%ld count=%ld\n",
959               table->alloc_current, table->alloc_max,
960               table->alloc_total, table->alloc_count );
961       printf( " block  block    sizes    sizes    sizes   source\n" );
962       printf( " count   high      sum  highsum      max   location\n" );
963       printf( "-------------------------------------------------\n" );
964
965       fmt = "%6ld %6ld %8ld %8ld %8ld %s:%d\n";
966
967       for ( nn = 0; nn < count; nn++ )
968       {
969         FT_MemSource  source = sources[nn];
970
971
972         printf( fmt,
973                 source->cur_blocks, source->max_blocks,
974                 source->cur_size, source->max_size, source->cur_max,
975                 FT_FILENAME( source->file_name ),
976                 source->line_no );
977       }
978       printf( "------------------------------------------------\n" );
979
980       ft_mem_table_free( table, sources );
981     }
982   }
983
984 #else  /* !FT_DEBUG_MEMORY */
985
986   /* ANSI C doesn't like empty source files */
987   typedef int  _debug_mem_dummy;
988
989 #endif /* !FT_DEBUG_MEMORY */
990
991
992 /* END */