- Remove svn:needs-lock, svn:eol-type, and svn:eol-tyle properties.
[reactos.git] / reactos / lib / 3rdparty / freetype / src / base / ftdbgmem.c
index 722eda4..52a5c20 100644 (file)
-/***************************************************************************/\r
-/*                                                                         */\r
-/*  ftdbgmem.c                                                             */\r
-/*                                                                         */\r
-/*    Memory debugger (body).                                              */\r
-/*                                                                         */\r
-/*  Copyright 2001, 2002, 2003, 2004, 2005, 2006 by                        */\r
-/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */\r
-/*                                                                         */\r
-/*  This file is part of the FreeType project, and may only be used,       */\r
-/*  modified, and distributed under the terms of the FreeType project      */\r
-/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */\r
-/*  this file you indicate that you have read the license and              */\r
-/*  understand and accept it fully.                                        */\r
-/*                                                                         */\r
-/***************************************************************************/\r
-\r
-\r
-#include <ft2build.h>\r
-#include FT_CONFIG_CONFIG_H\r
-#include FT_INTERNAL_DEBUG_H\r
-#include FT_INTERNAL_MEMORY_H\r
-#include FT_SYSTEM_H\r
-#include FT_ERRORS_H\r
-#include FT_TYPES_H\r
-\r
-\r
-#ifdef FT_DEBUG_MEMORY\r
-\r
-#define  KEEPALIVE /* `Keep alive' means that freed blocks aren't released\r
-                    * to the heap.  This is useful to detect double-frees\r
-                    * or weird heap corruption, but it uses large amounts of\r
-                    * memory, however.\r
-                    */\r
-\r
-#include <stdio.h>\r
-#include <stdlib.h>\r
-\r
-  FT_BASE_DEF( const char* )  _ft_debug_file   = 0;\r
-  FT_BASE_DEF( long )         _ft_debug_lineno = 0;\r
-\r
-  extern void\r
-  FT_DumpMemory( FT_Memory  memory );\r
-\r
-\r
-  typedef struct FT_MemSourceRec_*  FT_MemSource;\r
-  typedef struct FT_MemNodeRec_*    FT_MemNode;\r
-  typedef struct FT_MemTableRec_*   FT_MemTable;\r
-\r
-\r
-#define FT_MEM_VAL( addr )  ((FT_ULong)(FT_Pointer)( addr ))\r
-\r
-  /*\r
-   *  This structure holds statistics for a single allocation/release\r
-   *  site.  This is useful to know where memory operations happen the\r
-   *  most.\r
-   */\r
-  typedef struct  FT_MemSourceRec_\r
-  {\r
-    const char*   file_name;\r
-    long          line_no;\r
-\r
-    FT_Long       cur_blocks;   /* current number of allocated blocks */\r
-    FT_Long       max_blocks;   /* max. number of allocated blocks    */\r
-    FT_Long       all_blocks;   /* total number of blocks allocated   */\r
-\r
-    FT_Long       cur_size;     /* current cumulative allocated size */\r
-    FT_Long       max_size;     /* maximum cumulative allocated size */\r
-    FT_Long       all_size;     /* total cumulative allocated size   */\r
-\r
-    FT_Long       cur_max;      /* current maximum allocated size */\r
-\r
-    FT_UInt32     hash;\r
-    FT_MemSource  link;\r
-\r
-  } FT_MemSourceRec;\r
-\r
-\r
-  /*\r
-   *  We don't need a resizable array for the memory sources, because\r
-   *  their number is pretty limited within FreeType.\r
-   */\r
-#define FT_MEM_SOURCE_BUCKETS  128\r
-\r
-  /*\r
-   *  This structure holds information related to a single allocated\r
-   *  memory block.  If KEEPALIVE is defined, blocks that are freed by\r
-   *  FreeType are never released to the system.  Instead, their `size'\r
-   *  field is set to -size.  This is mainly useful to detect double frees,\r
-   *  at the price of large memory footprint during execution.\r
-   */\r
-  typedef struct  FT_MemNodeRec_\r
-  {\r
-    FT_Byte*      address;\r
-    FT_Long       size;     /* < 0 if the block was freed */\r
-\r
-    FT_MemSource  source;\r
-\r
-#ifdef KEEPALIVE\r
-    const char*   free_file_name;\r
-    FT_Long       free_line_no;\r
-#endif\r
-\r
-    FT_MemNode    link;\r
-\r
-  } FT_MemNodeRec;\r
-\r
-\r
-  /*\r
-   *  The global structure, containing compound statistics and all hash\r
-   *  tables.\r
-   */\r
-  typedef struct  FT_MemTableRec_\r
-  {\r
-    FT_ULong         size;\r
-    FT_ULong         nodes;\r
-    FT_MemNode*      buckets;\r
-\r
-    FT_ULong         alloc_total;\r
-    FT_ULong         alloc_current;\r
-    FT_ULong         alloc_max;\r
-    FT_ULong         alloc_count;\r
-\r
-    FT_Bool          bound_total;\r
-    FT_ULong         alloc_total_max;\r
-\r
-    FT_Bool          bound_count;\r
-    FT_ULong         alloc_count_max;\r
-\r
-    FT_MemSource     sources[FT_MEM_SOURCE_BUCKETS];\r
-\r
-    FT_Bool          keep_alive;\r
-\r
-    FT_Memory        memory;\r
-    FT_Pointer       memory_user;\r
-    FT_Alloc_Func    alloc;\r
-    FT_Free_Func     free;\r
-    FT_Realloc_Func  realloc;\r
-\r
-  } FT_MemTableRec;\r
-\r
-\r
-#define FT_MEM_SIZE_MIN  7\r
-#define FT_MEM_SIZE_MAX  13845163\r
-\r
-#define FT_FILENAME( x )  ((x) ? (x) : "unknown file")\r
-\r
-\r
-  /*\r
-   *  Prime numbers are ugly to handle.  It would be better to implement\r
-   *  L-Hashing, which is 10% faster and doesn't require divisions.\r
-   */\r
-  static const FT_UInt  ft_mem_primes[] =\r
-  {\r
-    7,\r
-    11,\r
-    19,\r
-    37,\r
-    73,\r
-    109,\r
-    163,\r
-    251,\r
-    367,\r
-    557,\r
-    823,\r
-    1237,\r
-    1861,\r
-    2777,\r
-    4177,\r
-    6247,\r
-    9371,\r
-    14057,\r
-    21089,\r
-    31627,\r
-    47431,\r
-    71143,\r
-    106721,\r
-    160073,\r
-    240101,\r
-    360163,\r
-    540217,\r
-    810343,\r
-    1215497,\r
-    1823231,\r
-    2734867,\r
-    4102283,\r
-    6153409,\r
-    9230113,\r
-    13845163,\r
-  };\r
-\r
-\r
-  static FT_ULong\r
-  ft_mem_closest_prime( FT_ULong  num )\r
-  {\r
-    FT_UInt  i;\r
-\r
-\r
-    for ( i = 0;\r
-          i < sizeof ( ft_mem_primes ) / sizeof ( ft_mem_primes[0] ); i++ )\r
-      if ( ft_mem_primes[i] > num )\r
-        return ft_mem_primes[i];\r
-\r
-    return FT_MEM_SIZE_MAX;\r
-  }\r
-\r
-\r
-  extern void\r
-  ft_mem_debug_panic( const char*  fmt,\r
-                      ... )\r
-  {\r
-    va_list  ap;\r
-\r
-\r
-    printf( "FreeType.Debug: " );\r
-\r
-    va_start( ap, fmt );\r
-    vprintf( fmt, ap );\r
-    va_end( ap );\r
-\r
-    printf( "\n" );\r
-    exit( EXIT_FAILURE );\r
-  }\r
-\r
-\r
-  static FT_Pointer\r
-  ft_mem_table_alloc( FT_MemTable  table,\r
-                      FT_Long      size )\r
-  {\r
-    FT_Memory   memory = table->memory;\r
-    FT_Pointer  block;\r
-\r
-\r
-    memory->user = table->memory_user;\r
-    block = table->alloc( memory, size );\r
-    memory->user = table;\r
-\r
-    return block;\r
-  }\r
-\r
-\r
-  static void\r
-  ft_mem_table_free( FT_MemTable  table,\r
-                     FT_Pointer   block )\r
-  {\r
-    FT_Memory  memory = table->memory;\r
-\r
-\r
-    memory->user = table->memory_user;\r
-    table->free( memory, block );\r
-    memory->user = table;\r
-  }\r
-\r
-\r
-  static void\r
-  ft_mem_table_resize( FT_MemTable  table )\r
-  {\r
-    FT_ULong  new_size;\r
-\r
-\r
-    new_size = ft_mem_closest_prime( table->nodes );\r
-    if ( new_size != table->size )\r
-    {\r
-      FT_MemNode*  new_buckets;\r
-      FT_ULong     i;\r
-\r
-\r
-      new_buckets = (FT_MemNode *)\r
-                      ft_mem_table_alloc( table,\r
-                                          new_size * sizeof ( FT_MemNode ) );\r
-      if ( new_buckets == NULL )\r
-        return;\r
-\r
-      FT_ARRAY_ZERO( new_buckets, new_size );\r
-\r
-      for ( i = 0; i < table->size; i++ )\r
-      {\r
-        FT_MemNode  node, next, *pnode;\r
-        FT_ULong    hash;\r
-\r
-\r
-        node = table->buckets[i];\r
-        while ( node )\r
-        {\r
-          next  = node->link;\r
-          hash  = FT_MEM_VAL( node->address ) % new_size;\r
-          pnode = new_buckets + hash;\r
-\r
-          node->link = pnode[0];\r
-          pnode[0]   = node;\r
-\r
-          node = next;\r
-        }\r
-      }\r
-\r
-      if ( table->buckets )\r
-        ft_mem_table_free( table, table->buckets );\r
-\r
-      table->buckets = new_buckets;\r
-      table->size    = new_size;\r
-    }\r
-  }\r
-\r
-\r
-  static FT_MemTable\r
-  ft_mem_table_new( FT_Memory  memory )\r
-  {\r
-    FT_MemTable  table;\r
-\r
-\r
-    table = (FT_MemTable)memory->alloc( memory, sizeof ( *table ) );\r
-    if ( table == NULL )\r
-      goto Exit;\r
-\r
-    FT_ZERO( table );\r
-\r
-    table->size  = FT_MEM_SIZE_MIN;\r
-    table->nodes = 0;\r
-\r
-    table->memory = memory;\r
-\r
-    table->memory_user = memory->user;\r
-\r
-    table->alloc   = memory->alloc;\r
-    table->realloc = memory->realloc;\r
-    table->free    = memory->free;\r
-\r
-    table->buckets = (FT_MemNode *)\r
-                       memory->alloc( memory,\r
-                                      table->size * sizeof ( FT_MemNode ) );\r
-    if ( table->buckets )\r
-      FT_ARRAY_ZERO( table->buckets, table->size );\r
-    else\r
-    {\r
-      memory->free( memory, table );\r
-      table = NULL;\r
-    }\r
-\r
-  Exit:\r
-    return table;\r
-  }\r
-\r
-\r
-  static void\r
-  ft_mem_table_destroy( FT_MemTable  table )\r
-  {\r
-    FT_ULong  i;\r
-\r
-\r
-    FT_DumpMemory( table->memory );\r
-\r
-    if ( table )\r
-    {\r
-      FT_Long   leak_count = 0;\r
-      FT_ULong  leaks      = 0;\r
-\r
-\r
-      /* remove all blocks from the table, revealing leaked ones */\r
-      for ( i = 0; i < table->size; i++ )\r
-      {\r
-        FT_MemNode  *pnode = table->buckets + i, next, node = *pnode;\r
-\r
-\r
-        while ( node )\r
-        {\r
-          next       = node->link;\r
-          node->link = 0;\r
-\r
-          if ( node->size > 0 )\r
-          {\r
-            printf(\r
-              "leaked memory block at address %p, size %8ld in (%s:%ld)\n",\r
-              node->address, node->size,\r
-              FT_FILENAME( node->source->file_name ),\r
-              node->source->line_no );\r
-\r
-            leak_count++;\r
-            leaks += node->size;\r
-\r
-            ft_mem_table_free( table, node->address );\r
-          }\r
-\r
-          node->address = NULL;\r
-          node->size    = 0;\r
-\r
-          ft_mem_table_free( table, node );\r
-          node = next;\r
-        }\r
-        table->buckets[i] = 0;\r
-      }\r
-\r
-      ft_mem_table_free( table, table->buckets );\r
-      table->buckets = NULL;\r
-\r
-      table->size  = 0;\r
-      table->nodes = 0;\r
-\r
-      /* remove all sources */\r
-      for ( i = 0; i < FT_MEM_SOURCE_BUCKETS; i++ )\r
-      {\r
-        FT_MemSource  source, next;\r
-\r
-\r
-        for ( source = table->sources[i]; source != NULL; source = next )\r
-        {\r
-          next = source->link;\r
-          ft_mem_table_free( table, source );\r
-        }\r
-\r
-        table->sources[i] = NULL;\r
-      }\r
-\r
-      printf(\r
-        "FreeType: total memory allocations = %ld\n", table->alloc_total );\r
-      printf(\r
-        "FreeType: maximum memory footprint = %ld\n", table->alloc_max );\r
-\r
-      ft_mem_table_free( table, table );\r
-\r
-      if ( leak_count > 0 )\r
-        ft_mem_debug_panic(\r
-          "FreeType: %ld bytes of memory leaked in %ld blocks\n",\r
-          leaks, leak_count );\r
-\r
-      printf( "FreeType: No memory leaks detected!\n" );\r
-    }\r
-  }\r
-\r
-\r
-  static FT_MemNode*\r
-  ft_mem_table_get_nodep( FT_MemTable  table,\r
-                          FT_Byte*     address )\r
-  {\r
-    FT_ULong     hash;\r
-    FT_MemNode  *pnode, node;\r
-\r
-\r
-    hash  = FT_MEM_VAL( address );\r
-    pnode = table->buckets + ( hash % table->size );\r
-\r
-    for (;;)\r
-    {\r
-      node = pnode[0];\r
-      if ( !node )\r
-        break;\r
-\r
-      if ( node->address == address )\r
-        break;\r
-\r
-      pnode = &node->link;\r
-    }\r
-    return pnode;\r
-  }\r
-\r
-\r
-  static FT_MemSource\r
-  ft_mem_table_get_source( FT_MemTable  table )\r
-  {\r
-    FT_UInt32     hash;\r
-    FT_MemSource  node, *pnode;\r
-\r
-\r
-    /* cast to FT_PtrDist first since void* can be larger */\r
-    /* than FT_UInt32 and GCC 4.1.1 emits a warning       */\r
-    hash  = (FT_UInt32)(FT_PtrDist)(void*)_ft_debug_file +\r
-              (FT_UInt32)( 5 * _ft_debug_lineno );\r
-    pnode = &table->sources[hash % FT_MEM_SOURCE_BUCKETS];\r
-\r
-    for ( ;; )\r
-    {\r
-      node = *pnode;\r
-      if ( node == NULL )\r
-        break;\r
-\r
-      if ( node->file_name == _ft_debug_file &&\r
-           node->line_no   == _ft_debug_lineno   )\r
-        goto Exit;\r
-\r
-      pnode = &node->link;\r
-    }\r
-\r
-    node = (FT_MemSource)ft_mem_table_alloc( table, sizeof ( *node ) );\r
-    if ( node == NULL )\r
-      ft_mem_debug_panic(\r
-        "not enough memory to perform memory debugging\n" );\r
-\r
-    node->file_name = _ft_debug_file;\r
-    node->line_no   = _ft_debug_lineno;\r
-\r
-    node->cur_blocks = 0;\r
-    node->max_blocks = 0;\r
-    node->all_blocks = 0;\r
-\r
-    node->cur_size   = 0;\r
-    node->max_size   = 0;\r
-    node->all_size   = 0;\r
-\r
-    node->cur_max    = 0;\r
-\r
-    node->link = NULL;\r
-    node->hash = hash;\r
-    *pnode     = node;\r
-\r
-  Exit:\r
-    return node;\r
-  }\r
-\r
-\r
-  static void\r
-  ft_mem_table_set( FT_MemTable  table,\r
-                    FT_Byte*     address,\r
-                    FT_ULong     size,\r
-                    FT_Long      delta )\r
-  {\r
-    FT_MemNode  *pnode, node;\r
-\r
-\r
-    if ( table )\r
-    {\r
-      FT_MemSource  source;\r
-\r
-\r
-      pnode = ft_mem_table_get_nodep( table, address );\r
-      node  = *pnode;\r
-      if ( node )\r
-      {\r
-        if ( node->size < 0 )\r
-        {\r
-          /* This block was already freed.  Our memory is now completely */\r
-          /* corrupted!                                                  */\r
-          /* This can only happen in keep-alive mode.                    */\r
-          ft_mem_debug_panic(\r
-            "memory heap corrupted (allocating freed block)" );\r
-        }\r
-        else\r
-        {\r
-          /* This block was already allocated.  This means that our memory */\r
-          /* is also corrupted!                                            */\r
-          ft_mem_debug_panic(\r
-            "memory heap corrupted (re-allocating allocated block at"\r
-            " %p, of size %ld)\n"\r
-            "org=%s:%d new=%s:%d\n",\r
-            node->address, node->size,\r
-            FT_FILENAME( node->source->file_name ), node->source->line_no,\r
-            FT_FILENAME( _ft_debug_file ), _ft_debug_lineno );\r
-        }\r
-      }\r
-\r
-      /* we need to create a new node in this table */\r
-      node = (FT_MemNode)ft_mem_table_alloc( table, sizeof ( *node ) );\r
-      if ( node == NULL )\r
-        ft_mem_debug_panic( "not enough memory to run memory tests" );\r
-\r
-      node->address = address;\r
-      node->size    = size;\r
-      node->source  = source = ft_mem_table_get_source( table );\r
-\r
-      if ( delta == 0 )\r
-      {\r
-        /* this is an allocation */\r
-        source->all_blocks++;\r
-        source->cur_blocks++;\r
-        if ( source->cur_blocks > source->max_blocks )\r
-          source->max_blocks = source->cur_blocks;\r
-      }\r
-\r
-      if ( size > (FT_ULong)source->cur_max )\r
-        source->cur_max = size;\r
-\r
-      if ( delta != 0 )\r
-      {\r
-        /* we are growing or shrinking a reallocated block */\r
-        source->cur_size     += delta;\r
-        table->alloc_current += delta;\r
-      }\r
-      else\r
-      {\r
-        /* we are allocating a new block */\r
-        source->cur_size     += size;\r
-        table->alloc_current += size;\r
-      }\r
-\r
-      source->all_size += size;\r
-\r
-      if ( source->cur_size > source->max_size )\r
-        source->max_size = source->cur_size;\r
-\r
-      node->free_file_name = NULL;\r
-      node->free_line_no   = 0;\r
-\r
-      node->link = pnode[0];\r
-\r
-      pnode[0] = node;\r
-      table->nodes++;\r
-\r
-      table->alloc_total += size;\r
-\r
-      if ( table->alloc_current > table->alloc_max )\r
-        table->alloc_max = table->alloc_current;\r
-\r
-      if ( table->nodes * 3 < table->size  ||\r
-           table->size  * 3 < table->nodes )\r
-        ft_mem_table_resize( table );\r
-    }\r
-  }\r
-\r
-\r
-  static void\r
-  ft_mem_table_remove( FT_MemTable  table,\r
-                       FT_Byte*     address,\r
-                       FT_Long      delta )\r
-  {\r
-    if ( table )\r
-    {\r
-      FT_MemNode  *pnode, node;\r
-\r
-\r
-      pnode = ft_mem_table_get_nodep( table, address );\r
-      node  = *pnode;\r
-      if ( node )\r
-      {\r
-        FT_MemSource  source;\r
-\r
-\r
-        if ( node->size < 0 )\r
-          ft_mem_debug_panic(\r
-            "freeing memory block at %p more than once at (%s:%ld)\n"\r
-            "block allocated at (%s:%ld) and released at (%s:%ld)",\r
-            address,\r
-            FT_FILENAME( _ft_debug_file ), _ft_debug_lineno,\r
-            FT_FILENAME( node->source->file_name ), node->source->line_no,\r
-            FT_FILENAME( node->free_file_name ), node->free_line_no );\r
-\r
-        /* scramble the node's content for additional safety */\r
-        FT_MEM_SET( address, 0xF3, node->size );\r
-\r
-        if ( delta == 0 )\r
-        {\r
-          source = node->source;\r
-\r
-          source->cur_blocks--;\r
-          source->cur_size -= node->size;\r
-\r
-          table->alloc_current -= node->size;\r
-        }\r
-\r
-        if ( table->keep_alive )\r
-        {\r
-          /* we simply invert the node's size to indicate that the node */\r
-          /* was freed.                                                 */\r
-          node->size           = -node->size;\r
-          node->free_file_name = _ft_debug_file;\r
-          node->free_line_no   = _ft_debug_lineno;\r
-        }\r
-        else\r
-        {\r
-          table->nodes--;\r
-\r
-          *pnode = node->link;\r
-\r
-          node->size   = 0;\r
-          node->source = NULL;\r
-\r
-          ft_mem_table_free( table, node );\r
-\r
-          if ( table->nodes * 3 < table->size  ||\r
-               table->size  * 3 < table->nodes )\r
-            ft_mem_table_resize( table );\r
-        }\r
-      }\r
-      else\r
-        ft_mem_debug_panic(\r
-          "trying to free unknown block at %p in (%s:%ld)\n",\r
-          address,\r
-          FT_FILENAME( _ft_debug_file ), _ft_debug_lineno );\r
-    }\r
-  }\r
-\r
-\r
-  extern FT_Pointer\r
-  ft_mem_debug_alloc( FT_Memory  memory,\r
-                      FT_Long    size )\r
-  {\r
-    FT_MemTable  table = (FT_MemTable)memory->user;\r
-    FT_Byte*     block;\r
-\r
-\r
-    if ( size <= 0 )\r
-      ft_mem_debug_panic( "negative block size allocation (%ld)", size );\r
-\r
-    /* return NULL if the maximum number of allocations was reached */\r
-    if ( table->bound_count                           &&\r
-         table->alloc_count >= table->alloc_count_max )\r
-      return NULL;\r
-\r
-    /* return NULL if this allocation would overflow the maximum heap size */\r
-    if ( table->bound_total                                             &&\r
-         table->alloc_total_max - table->alloc_current > (FT_ULong)size )\r
-      return NULL;\r
-\r
-    block = (FT_Byte *)ft_mem_table_alloc( table, size );\r
-    if ( block )\r
-    {\r
-      ft_mem_table_set( table, block, (FT_ULong)size, 0 );\r
-\r
-      table->alloc_count++;\r
-    }\r
-\r
-    _ft_debug_file   = "<unknown>";\r
-    _ft_debug_lineno = 0;\r
-\r
-    return (FT_Pointer)block;\r
-  }\r
-\r
-\r
-  extern void\r
-  ft_mem_debug_free( FT_Memory   memory,\r
-                     FT_Pointer  block )\r
-  {\r
-    FT_MemTable  table = (FT_MemTable)memory->user;\r
-\r
-\r
-    if ( block == NULL )\r
-      ft_mem_debug_panic( "trying to free NULL in (%s:%ld)",\r
-                          FT_FILENAME( _ft_debug_file ),\r
-                          _ft_debug_lineno );\r
-\r
-    ft_mem_table_remove( table, (FT_Byte*)block, 0 );\r
-\r
-    if ( !table->keep_alive )\r
-      ft_mem_table_free( table, block );\r
-\r
-    table->alloc_count--;\r
-\r
-    _ft_debug_file   = "<unknown>";\r
-    _ft_debug_lineno = 0;\r
-  }\r
-\r
-\r
-  extern FT_Pointer\r
-  ft_mem_debug_realloc( FT_Memory   memory,\r
-                        FT_Long     cur_size,\r
-                        FT_Long     new_size,\r
-                        FT_Pointer  block )\r
-  {\r
-    FT_MemTable  table = (FT_MemTable)memory->user;\r
-    FT_MemNode   node, *pnode;\r
-    FT_Pointer   new_block;\r
-    FT_Long      delta;\r
-\r
-    const char*  file_name = FT_FILENAME( _ft_debug_file );\r
-    FT_Long      line_no   = _ft_debug_lineno;\r
-\r
-\r
-    /* unlikely, but possible */\r
-    if ( new_size == cur_size )\r
-      return block;\r
-\r
-    /* the following is valid according to ANSI C */\r
-#if 0\r
-    if ( block == NULL || cur_size == 0 )\r
-      ft_mem_debug_panic( "trying to reallocate NULL in (%s:%ld)",\r
-                          file_name, line_no );\r
-#endif\r
-\r
-    /* while the following is allowed in ANSI C also, we abort since */\r
-    /* such case should be handled by FreeType.                      */\r
-    if ( new_size <= 0 )\r
-      ft_mem_debug_panic(\r
-        "trying to reallocate %p to size 0 (current is %ld) in (%s:%ld)",\r
-        block, cur_size, file_name, line_no );\r
-\r
-    /* check `cur_size' value */\r
-    pnode = ft_mem_table_get_nodep( table, (FT_Byte*)block );\r
-    node  = *pnode;\r
-    if ( !node )\r
-      ft_mem_debug_panic(\r
-        "trying to reallocate unknown block at %p in (%s:%ld)",\r
-        block, file_name, line_no );\r
-\r
-    if ( node->size <= 0 )\r
-      ft_mem_debug_panic(\r
-        "trying to reallocate freed block at %p in (%s:%ld)",\r
-        block, file_name, line_no );\r
-\r
-    if ( node->size != cur_size )\r
-      ft_mem_debug_panic( "invalid ft_realloc request for %p. cur_size is "\r
-                          "%ld instead of %ld in (%s:%ld)",\r
-                          block, cur_size, node->size, file_name, line_no );\r
-\r
-    /* return NULL if the maximum number of allocations was reached */\r
-    if ( table->bound_count                           &&\r
-         table->alloc_count >= table->alloc_count_max )\r
-      return NULL;\r
-\r
-    delta = (FT_Long)( new_size - cur_size );\r
-\r
-    /* return NULL if this allocation would overflow the maximum heap size */\r
-    if ( delta > 0                                                       &&\r
-         table->bound_total                                              &&\r
-         table->alloc_current + (FT_ULong)delta > table->alloc_total_max )\r
-      return NULL;\r
-\r
-    new_block = (FT_Byte *)ft_mem_table_alloc( table, new_size );\r
-    if ( new_block == NULL )\r
-      return NULL;\r
-\r
-    ft_mem_table_set( table, (FT_Byte*)new_block, new_size, delta );\r
-\r
-    ft_memcpy( new_block, block, cur_size < new_size ? cur_size : new_size );\r
-\r
-    ft_mem_table_remove( table, (FT_Byte*)block, delta );\r
-\r
-    _ft_debug_file   = "<unknown>";\r
-    _ft_debug_lineno = 0;\r
-\r
-    if ( !table->keep_alive )\r
-      ft_mem_table_free( table, block );\r
-\r
-    return new_block;\r
-  }\r
-\r
-\r
-  extern FT_Int\r
-  ft_mem_debug_init( FT_Memory  memory )\r
-  {\r
-    FT_MemTable  table;\r
-    FT_Int       result = 0;\r
-\r
-\r
-    if ( getenv( "FT2_DEBUG_MEMORY" ) )\r
-    {\r
-      table = ft_mem_table_new( memory );\r
-      if ( table )\r
-      {\r
-        const char*  p;\r
-\r
-\r
-        memory->user    = table;\r
-        memory->alloc   = ft_mem_debug_alloc;\r
-        memory->realloc = ft_mem_debug_realloc;\r
-        memory->free    = ft_mem_debug_free;\r
-\r
-        p = getenv( "FT2_ALLOC_TOTAL_MAX" );\r
-        if ( p != NULL )\r
-        {\r
-          FT_Long   total_max = ft_atol( p );\r
-\r
-\r
-          if ( total_max > 0 )\r
-          {\r
-            table->bound_total     = 1;\r
-            table->alloc_total_max = (FT_ULong)total_max;\r
-          }\r
-        }\r
-\r
-        p = getenv( "FT2_ALLOC_COUNT_MAX" );\r
-        if ( p != NULL )\r
-        {\r
-          FT_Long  total_count = ft_atol( p );\r
-\r
-\r
-          if ( total_count > 0 )\r
-          {\r
-            table->bound_count     = 1;\r
-            table->alloc_count_max = (FT_ULong)total_count;\r
-          }\r
-        }\r
-\r
-        p = getenv( "FT2_KEEP_ALIVE" );\r
-        if ( p != NULL )\r
-        {\r
-          FT_Long  keep_alive = ft_atol( p );\r
-\r
-\r
-          if ( keep_alive > 0 )\r
-            table->keep_alive = 1;\r
-        }\r
-\r
-        result = 1;\r
-      }\r
-    }\r
-    return result;\r
-  }\r
-\r
-\r
-  extern void\r
-  ft_mem_debug_done( FT_Memory  memory )\r
-  {\r
-    FT_MemTable  table = (FT_MemTable)memory->user;\r
-\r
-\r
-    if ( table )\r
-    {\r
-      memory->free    = table->free;\r
-      memory->realloc = table->realloc;\r
-      memory->alloc   = table->alloc;\r
-\r
-      ft_mem_table_destroy( table );\r
-      memory->user = NULL;\r
-    }\r
-  }\r
-\r
-\r
-\r
-  static int\r
-  ft_mem_source_compare( const void*  p1,\r
-                         const void*  p2 )\r
-  {\r
-    FT_MemSource  s1 = *(FT_MemSource*)p1;\r
-    FT_MemSource  s2 = *(FT_MemSource*)p2;\r
-\r
-\r
-    if ( s2->max_size > s1->max_size )\r
-      return 1;\r
-    else if ( s2->max_size < s1->max_size )\r
-      return -1;\r
-    else\r
-      return 0;\r
-  }\r
-\r
-\r
-  extern void\r
-  FT_DumpMemory( FT_Memory  memory )\r
-  {\r
-    FT_MemTable  table = (FT_MemTable)memory->user;\r
-\r
-\r
-    if ( table )\r
-    {\r
-      FT_MemSource*  bucket = table->sources;\r
-      FT_MemSource*  limit  = bucket + FT_MEM_SOURCE_BUCKETS;\r
-      FT_MemSource*  sources;\r
-      FT_UInt        nn, count;\r
-      const char*    fmt;\r
-\r
-\r
-      count = 0;\r
-      for ( ; bucket < limit; bucket++ )\r
-      {\r
-        FT_MemSource  source = *bucket;\r
-\r
-\r
-        for ( ; source; source = source->link )\r
-          count++;\r
-      }\r
-\r
-      sources = (FT_MemSource*)ft_mem_table_alloc(\r
-                                 table, sizeof ( *sources ) * count );\r
-\r
-      count = 0;\r
-      for ( bucket = table->sources; bucket < limit; bucket++ )\r
-      {\r
-        FT_MemSource  source = *bucket;\r
-\r
-\r
-        for ( ; source; source = source->link )\r
-          sources[count++] = source;\r
-      }\r
-\r
-      ft_qsort( sources, count, sizeof ( *sources ), ft_mem_source_compare );\r
-\r
-      printf( "FreeType Memory Dump: "\r
-              "current=%ld max=%ld total=%ld count=%ld\n",\r
-              table->alloc_current, table->alloc_max,\r
-              table->alloc_total, table->alloc_count );\r
-      printf( " block  block    sizes    sizes    sizes   source\n" );\r
-      printf( " count   high      sum  highsum      max   location\n" );\r
-      printf( "-------------------------------------------------\n" );\r
-\r
-      fmt = "%6ld %6ld %8ld %8ld %8ld %s:%d\n";\r
-\r
-      for ( nn = 0; nn < count; nn++ )\r
-      {\r
-        FT_MemSource  source = sources[nn];\r
-\r
-\r
-        printf( fmt,\r
-                source->cur_blocks, source->max_blocks,\r
-                source->cur_size, source->max_size, source->cur_max,\r
-                FT_FILENAME( source->file_name ),\r
-                source->line_no );\r
-      }\r
-      printf( "------------------------------------------------\n" );\r
-\r
-      ft_mem_table_free( table, sources );\r
-    }\r
-  }\r
-\r
-#else  /* !FT_DEBUG_MEMORY */\r
-\r
-  /* ANSI C doesn't like empty source files */\r
-  const FT_Byte  _debug_mem_dummy = 0;\r
-\r
-#endif /* !FT_DEBUG_MEMORY */\r
-\r
-\r
-/* END */\r
+/***************************************************************************/
+/*                                                                         */
+/*  ftdbgmem.c                                                             */
+/*                                                                         */
+/*    Memory debugger (body).                                              */
+/*                                                                         */
+/*  Copyright 2001, 2002, 2003, 2004, 2005, 2006 by                        */
+/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
+/*                                                                         */
+/*  This file is part of the FreeType project, and may only be used,       */
+/*  modified, and distributed under the terms of the FreeType project      */
+/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_CONFIG_CONFIG_H
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_MEMORY_H
+#include FT_SYSTEM_H
+#include FT_ERRORS_H
+#include FT_TYPES_H
+
+
+#ifdef FT_DEBUG_MEMORY
+
+#define  KEEPALIVE /* `Keep alive' means that freed blocks aren't released
+                    * to the heap.  This is useful to detect double-frees
+                    * or weird heap corruption, but it uses large amounts of
+                    * memory, however.
+                    */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+  FT_BASE_DEF( const char* )  _ft_debug_file   = 0;
+  FT_BASE_DEF( long )         _ft_debug_lineno = 0;
+
+  extern void
+  FT_DumpMemory( FT_Memory  memory );
+
+
+  typedef struct FT_MemSourceRec_*  FT_MemSource;
+  typedef struct FT_MemNodeRec_*    FT_MemNode;
+  typedef struct FT_MemTableRec_*   FT_MemTable;
+
+
+#define FT_MEM_VAL( addr )  ((FT_ULong)(FT_Pointer)( addr ))
+
+  /*
+   *  This structure holds statistics for a single allocation/release
+   *  site.  This is useful to know where memory operations happen the
+   *  most.
+   */
+  typedef struct  FT_MemSourceRec_
+  {
+    const char*   file_name;
+    long          line_no;
+
+    FT_Long       cur_blocks;   /* current number of allocated blocks */
+    FT_Long       max_blocks;   /* max. number of allocated blocks    */
+    FT_Long       all_blocks;   /* total number of blocks allocated   */
+
+    FT_Long       cur_size;     /* current cumulative allocated size */
+    FT_Long       max_size;     /* maximum cumulative allocated size */
+    FT_Long       all_size;     /* total cumulative allocated size   */
+
+    FT_Long       cur_max;      /* current maximum allocated size */
+
+    FT_UInt32     hash;
+    FT_MemSource  link;
+
+  } FT_MemSourceRec;
+
+
+  /*
+   *  We don't need a resizable array for the memory sources, because
+   *  their number is pretty limited within FreeType.
+   */
+#define FT_MEM_SOURCE_BUCKETS  128
+
+  /*
+   *  This structure holds information related to a single allocated
+   *  memory block.  If KEEPALIVE is defined, blocks that are freed by
+   *  FreeType are never released to the system.  Instead, their `size'
+   *  field is set to -size.  This is mainly useful to detect double frees,
+   *  at the price of large memory footprint during execution.
+   */
+  typedef struct  FT_MemNodeRec_
+  {
+    FT_Byte*      address;
+    FT_Long       size;     /* < 0 if the block was freed */
+
+    FT_MemSource  source;
+
+#ifdef KEEPALIVE
+    const char*   free_file_name;
+    FT_Long       free_line_no;
+#endif
+
+    FT_MemNode    link;
+
+  } FT_MemNodeRec;
+
+
+  /*
+   *  The global structure, containing compound statistics and all hash
+   *  tables.
+   */
+  typedef struct  FT_MemTableRec_
+  {
+    FT_ULong         size;
+    FT_ULong         nodes;
+    FT_MemNode*      buckets;
+
+    FT_ULong         alloc_total;
+    FT_ULong         alloc_current;
+    FT_ULong         alloc_max;
+    FT_ULong         alloc_count;
+
+    FT_Bool          bound_total;
+    FT_ULong         alloc_total_max;
+
+    FT_Bool          bound_count;
+    FT_ULong         alloc_count_max;
+
+    FT_MemSource     sources[FT_MEM_SOURCE_BUCKETS];
+
+    FT_Bool          keep_alive;
+
+    FT_Memory        memory;
+    FT_Pointer       memory_user;
+    FT_Alloc_Func    alloc;
+    FT_Free_Func     free;
+    FT_Realloc_Func  realloc;
+
+  } FT_MemTableRec;
+
+
+#define FT_MEM_SIZE_MIN  7
+#define FT_MEM_SIZE_MAX  13845163
+
+#define FT_FILENAME( x )  ((x) ? (x) : "unknown file")
+
+
+  /*
+   *  Prime numbers are ugly to handle.  It would be better to implement
+   *  L-Hashing, which is 10% faster and doesn't require divisions.
+   */
+  static const FT_UInt  ft_mem_primes[] =
+  {
+    7,
+    11,
+    19,
+    37,
+    73,
+    109,
+    163,
+    251,
+    367,
+    557,
+    823,
+    1237,
+    1861,
+    2777,
+    4177,
+    6247,
+    9371,
+    14057,
+    21089,
+    31627,
+    47431,
+    71143,
+    106721,
+    160073,
+    240101,
+    360163,
+    540217,
+    810343,
+    1215497,
+    1823231,
+    2734867,
+    4102283,
+    6153409,
+    9230113,
+    13845163,
+  };
+
+
+  static FT_ULong
+  ft_mem_closest_prime( FT_ULong  num )
+  {
+    FT_UInt  i;
+
+
+    for ( i = 0;
+          i < sizeof ( ft_mem_primes ) / sizeof ( ft_mem_primes[0] ); i++ )
+      if ( ft_mem_primes[i] > num )
+        return ft_mem_primes[i];
+
+    return FT_MEM_SIZE_MAX;
+  }
+
+
+  extern void
+  ft_mem_debug_panic( const char*  fmt,
+                      ... )
+  {
+    va_list  ap;
+
+
+    printf( "FreeType.Debug: " );
+
+    va_start( ap, fmt );
+    vprintf( fmt, ap );
+    va_end( ap );
+
+    printf( "\n" );
+    exit( EXIT_FAILURE );
+  }
+
+
+  static FT_Pointer
+  ft_mem_table_alloc( FT_MemTable  table,
+                      FT_Long      size )
+  {
+    FT_Memory   memory = table->memory;
+    FT_Pointer  block;
+
+
+    memory->user = table->memory_user;
+    block = table->alloc( memory, size );
+    memory->user = table;
+
+    return block;
+  }
+
+
+  static void
+  ft_mem_table_free( FT_MemTable  table,
+                     FT_Pointer   block )
+  {
+    FT_Memory  memory = table->memory;
+
+
+    memory->user = table->memory_user;
+    table->free( memory, block );
+    memory->user = table;
+  }
+
+
+  static void
+  ft_mem_table_resize( FT_MemTable  table )
+  {
+    FT_ULong  new_size;
+
+
+    new_size = ft_mem_closest_prime( table->nodes );
+    if ( new_size != table->size )
+    {
+      FT_MemNode*  new_buckets;
+      FT_ULong     i;
+
+
+      new_buckets = (FT_MemNode *)
+                      ft_mem_table_alloc( table,
+                                          new_size * sizeof ( FT_MemNode ) );
+      if ( new_buckets == NULL )
+        return;
+
+      FT_ARRAY_ZERO( new_buckets, new_size );
+
+      for ( i = 0; i < table->size; i++ )
+      {
+        FT_MemNode  node, next, *pnode;
+        FT_ULong    hash;
+
+
+        node = table->buckets[i];
+        while ( node )
+        {
+          next  = node->link;
+          hash  = FT_MEM_VAL( node->address ) % new_size;
+          pnode = new_buckets + hash;
+
+          node->link = pnode[0];
+          pnode[0]   = node;
+
+          node = next;
+        }
+      }
+
+      if ( table->buckets )
+        ft_mem_table_free( table, table->buckets );
+
+      table->buckets = new_buckets;
+      table->size    = new_size;
+    }
+  }
+
+
+  static FT_MemTable
+  ft_mem_table_new( FT_Memory  memory )
+  {
+    FT_MemTable  table;
+
+
+    table = (FT_MemTable)memory->alloc( memory, sizeof ( *table ) );
+    if ( table == NULL )
+      goto Exit;
+
+    FT_ZERO( table );
+
+    table->size  = FT_MEM_SIZE_MIN;
+    table->nodes = 0;
+
+    table->memory = memory;
+
+    table->memory_user = memory->user;
+
+    table->alloc   = memory->alloc;
+    table->realloc = memory->realloc;
+    table->free    = memory->free;
+
+    table->buckets = (FT_MemNode *)
+                       memory->alloc( memory,
+                                      table->size * sizeof ( FT_MemNode ) );
+    if ( table->buckets )
+      FT_ARRAY_ZERO( table->buckets, table->size );
+    else
+    {
+      memory->free( memory, table );
+      table = NULL;
+    }
+
+  Exit:
+    return table;
+  }
+
+
+  static void
+  ft_mem_table_destroy( FT_MemTable  table )
+  {
+    FT_ULong  i;
+
+
+    FT_DumpMemory( table->memory );
+
+    if ( table )
+    {
+      FT_Long   leak_count = 0;
+      FT_ULong  leaks      = 0;
+
+
+      /* remove all blocks from the table, revealing leaked ones */
+      for ( i = 0; i < table->size; i++ )
+      {
+        FT_MemNode  *pnode = table->buckets + i, next, node = *pnode;
+
+
+        while ( node )
+        {
+          next       = node->link;
+          node->link = 0;
+
+          if ( node->size > 0 )
+          {
+            printf(
+              "leaked memory block at address %p, size %8ld in (%s:%ld)\n",
+              node->address, node->size,
+              FT_FILENAME( node->source->file_name ),
+              node->source->line_no );
+
+            leak_count++;
+            leaks += node->size;
+
+            ft_mem_table_free( table, node->address );
+          }
+
+          node->address = NULL;
+          node->size    = 0;
+
+          ft_mem_table_free( table, node );
+          node = next;
+        }
+        table->buckets[i] = 0;
+      }
+
+      ft_mem_table_free( table, table->buckets );
+      table->buckets = NULL;
+
+      table->size  = 0;
+      table->nodes = 0;
+
+      /* remove all sources */
+      for ( i = 0; i < FT_MEM_SOURCE_BUCKETS; i++ )
+      {
+        FT_MemSource  source, next;
+
+
+        for ( source = table->sources[i]; source != NULL; source = next )
+        {
+          next = source->link;
+          ft_mem_table_free( table, source );
+        }
+
+        table->sources[i] = NULL;
+      }
+
+      printf(
+        "FreeType: total memory allocations = %ld\n", table->alloc_total );
+      printf(
+        "FreeType: maximum memory footprint = %ld\n", table->alloc_max );
+
+      ft_mem_table_free( table, table );
+
+      if ( leak_count > 0 )
+        ft_mem_debug_panic(
+          "FreeType: %ld bytes of memory leaked in %ld blocks\n",
+          leaks, leak_count );
+
+      printf( "FreeType: No memory leaks detected!\n" );
+    }
+  }
+
+
+  static FT_MemNode*
+  ft_mem_table_get_nodep( FT_MemTable  table,
+                          FT_Byte*     address )
+  {
+    FT_ULong     hash;
+    FT_MemNode  *pnode, node;
+
+
+    hash  = FT_MEM_VAL( address );
+    pnode = table->buckets + ( hash % table->size );
+
+    for (;;)
+    {
+      node = pnode[0];
+      if ( !node )
+        break;
+
+      if ( node->address == address )
+        break;
+
+      pnode = &node->link;
+    }
+    return pnode;
+  }
+
+
+  static FT_MemSource
+  ft_mem_table_get_source( FT_MemTable  table )
+  {
+    FT_UInt32     hash;
+    FT_MemSource  node, *pnode;
+
+
+    /* cast to FT_PtrDist first since void* can be larger */
+    /* than FT_UInt32 and GCC 4.1.1 emits a warning       */
+    hash  = (FT_UInt32)(FT_PtrDist)(void*)_ft_debug_file +
+              (FT_UInt32)( 5 * _ft_debug_lineno );
+    pnode = &table->sources[hash % FT_MEM_SOURCE_BUCKETS];
+
+    for ( ;; )
+    {
+      node = *pnode;
+      if ( node == NULL )
+        break;
+
+      if ( node->file_name == _ft_debug_file &&
+           node->line_no   == _ft_debug_lineno   )
+        goto Exit;
+
+      pnode = &node->link;
+    }
+
+    node = (FT_MemSource)ft_mem_table_alloc( table, sizeof ( *node ) );
+    if ( node == NULL )
+      ft_mem_debug_panic(
+        "not enough memory to perform memory debugging\n" );
+
+    node->file_name = _ft_debug_file;
+    node->line_no   = _ft_debug_lineno;
+
+    node->cur_blocks = 0;
+    node->max_blocks = 0;
+    node->all_blocks = 0;
+
+    node->cur_size   = 0;
+    node->max_size   = 0;
+    node->all_size   = 0;
+
+    node->cur_max    = 0;
+
+    node->link = NULL;
+    node->hash = hash;
+    *pnode     = node;
+
+  Exit:
+    return node;
+  }
+
+
+  static void
+  ft_mem_table_set( FT_MemTable  table,
+                    FT_Byte*     address,
+                    FT_ULong     size,
+                    FT_Long      delta )
+  {
+    FT_MemNode  *pnode, node;
+
+
+    if ( table )
+    {
+      FT_MemSource  source;
+
+
+      pnode = ft_mem_table_get_nodep( table, address );
+      node  = *pnode;
+      if ( node )
+      {
+        if ( node->size < 0 )
+        {
+          /* This block was already freed.  Our memory is now completely */
+          /* corrupted!                                                  */
+          /* This can only happen in keep-alive mode.                    */
+          ft_mem_debug_panic(
+            "memory heap corrupted (allocating freed block)" );
+        }
+        else
+        {
+          /* This block was already allocated.  This means that our memory */
+          /* is also corrupted!                                            */
+          ft_mem_debug_panic(
+            "memory heap corrupted (re-allocating allocated block at"
+            " %p, of size %ld)\n"
+            "org=%s:%d new=%s:%d\n",
+            node->address, node->size,
+            FT_FILENAME( node->source->file_name ), node->source->line_no,
+            FT_FILENAME( _ft_debug_file ), _ft_debug_lineno );
+        }
+      }
+
+      /* we need to create a new node in this table */
+      node = (FT_MemNode)ft_mem_table_alloc( table, sizeof ( *node ) );
+      if ( node == NULL )
+        ft_mem_debug_panic( "not enough memory to run memory tests" );
+
+      node->address = address;
+      node->size    = size;
+      node->source  = source = ft_mem_table_get_source( table );
+
+      if ( delta == 0 )
+      {
+        /* this is an allocation */
+        source->all_blocks++;
+        source->cur_blocks++;
+        if ( source->cur_blocks > source->max_blocks )
+          source->max_blocks = source->cur_blocks;
+      }
+
+      if ( size > (FT_ULong)source->cur_max )
+        source->cur_max = size;
+
+      if ( delta != 0 )
+      {
+        /* we are growing or shrinking a reallocated block */
+        source->cur_size     += delta;
+        table->alloc_current += delta;
+      }
+      else
+      {
+        /* we are allocating a new block */
+        source->cur_size     += size;
+        table->alloc_current += size;
+      }
+
+      source->all_size += size;
+
+      if ( source->cur_size > source->max_size )
+        source->max_size = source->cur_size;
+
+      node->free_file_name = NULL;
+      node->free_line_no   = 0;
+
+      node->link = pnode[0];
+
+      pnode[0] = node;
+      table->nodes++;
+
+      table->alloc_total += size;
+
+      if ( table->alloc_current > table->alloc_max )
+        table->alloc_max = table->alloc_current;
+
+      if ( table->nodes * 3 < table->size  ||
+           table->size  * 3 < table->nodes )
+        ft_mem_table_resize( table );
+    }
+  }
+
+
+  static void
+  ft_mem_table_remove( FT_MemTable  table,
+                       FT_Byte*     address,
+                       FT_Long      delta )
+  {
+    if ( table )
+    {
+      FT_MemNode  *pnode, node;
+
+
+      pnode = ft_mem_table_get_nodep( table, address );
+      node  = *pnode;
+      if ( node )
+      {
+        FT_MemSource  source;
+
+
+        if ( node->size < 0 )
+          ft_mem_debug_panic(
+            "freeing memory block at %p more than once at (%s:%ld)\n"
+            "block allocated at (%s:%ld) and released at (%s:%ld)",
+            address,
+            FT_FILENAME( _ft_debug_file ), _ft_debug_lineno,
+            FT_FILENAME( node->source->file_name ), node->source->line_no,
+            FT_FILENAME( node->free_file_name ), node->free_line_no );
+
+        /* scramble the node's content for additional safety */
+        FT_MEM_SET( address, 0xF3, node->size );
+
+        if ( delta == 0 )
+        {
+          source = node->source;
+
+          source->cur_blocks--;
+          source->cur_size -= node->size;
+
+          table->alloc_current -= node->size;
+        }
+
+        if ( table->keep_alive )
+        {
+          /* we simply invert the node's size to indicate that the node */
+          /* was freed.                                                 */
+          node->size           = -node->size;
+          node->free_file_name = _ft_debug_file;
+          node->free_line_no   = _ft_debug_lineno;
+        }
+        else
+        {
+          table->nodes--;
+
+          *pnode = node->link;
+
+          node->size   = 0;
+          node->source = NULL;
+
+          ft_mem_table_free( table, node );
+
+          if ( table->nodes * 3 < table->size  ||
+               table->size  * 3 < table->nodes )
+            ft_mem_table_resize( table );
+        }
+      }
+      else
+        ft_mem_debug_panic(
+          "trying to free unknown block at %p in (%s:%ld)\n",
+          address,
+          FT_FILENAME( _ft_debug_file ), _ft_debug_lineno );
+    }
+  }
+
+
+  extern FT_Pointer
+  ft_mem_debug_alloc( FT_Memory  memory,
+                      FT_Long    size )
+  {
+    FT_MemTable  table = (FT_MemTable)memory->user;
+    FT_Byte*     block;
+
+
+    if ( size <= 0 )
+      ft_mem_debug_panic( "negative block size allocation (%ld)", size );
+
+    /* return NULL if the maximum number of allocations was reached */
+    if ( table->bound_count                           &&
+         table->alloc_count >= table->alloc_count_max )
+      return NULL;
+
+    /* return NULL if this allocation would overflow the maximum heap size */
+    if ( table->bound_total                                             &&
+         table->alloc_total_max - table->alloc_current > (FT_ULong)size )
+      return NULL;
+
+    block = (FT_Byte *)ft_mem_table_alloc( table, size );
+    if ( block )
+    {
+      ft_mem_table_set( table, block, (FT_ULong)size, 0 );
+
+      table->alloc_count++;
+    }
+
+    _ft_debug_file   = "<unknown>";
+    _ft_debug_lineno = 0;
+
+    return (FT_Pointer)block;
+  }
+
+
+  extern void
+  ft_mem_debug_free( FT_Memory   memory,
+                     FT_Pointer  block )
+  {
+    FT_MemTable  table = (FT_MemTable)memory->user;
+
+
+    if ( block == NULL )
+      ft_mem_debug_panic( "trying to free NULL in (%s:%ld)",
+                          FT_FILENAME( _ft_debug_file ),
+                          _ft_debug_lineno );
+
+    ft_mem_table_remove( table, (FT_Byte*)block, 0 );
+
+    if ( !table->keep_alive )
+      ft_mem_table_free( table, block );
+
+    table->alloc_count--;
+
+    _ft_debug_file   = "<unknown>";
+    _ft_debug_lineno = 0;
+  }
+
+
+  extern FT_Pointer
+  ft_mem_debug_realloc( FT_Memory   memory,
+                        FT_Long     cur_size,
+                        FT_Long     new_size,
+                        FT_Pointer  block )
+  {
+    FT_MemTable  table = (FT_MemTable)memory->user;
+    FT_MemNode   node, *pnode;
+    FT_Pointer   new_block;
+    FT_Long      delta;
+
+    const char*  file_name = FT_FILENAME( _ft_debug_file );
+    FT_Long      line_no   = _ft_debug_lineno;
+
+
+    /* unlikely, but possible */
+    if ( new_size == cur_size )
+      return block;
+
+    /* the following is valid according to ANSI C */
+#if 0
+    if ( block == NULL || cur_size == 0 )
+      ft_mem_debug_panic( "trying to reallocate NULL in (%s:%ld)",
+                          file_name, line_no );
+#endif
+
+    /* while the following is allowed in ANSI C also, we abort since */
+    /* such case should be handled by FreeType.                      */
+    if ( new_size <= 0 )
+      ft_mem_debug_panic(
+        "trying to reallocate %p to size 0 (current is %ld) in (%s:%ld)",
+        block, cur_size, file_name, line_no );
+
+    /* check `cur_size' value */
+    pnode = ft_mem_table_get_nodep( table, (FT_Byte*)block );
+    node  = *pnode;
+    if ( !node )
+      ft_mem_debug_panic(
+        "trying to reallocate unknown block at %p in (%s:%ld)",
+        block, file_name, line_no );
+
+    if ( node->size <= 0 )
+      ft_mem_debug_panic(
+        "trying to reallocate freed block at %p in (%s:%ld)",
+        block, file_name, line_no );
+
+    if ( node->size != cur_size )
+      ft_mem_debug_panic( "invalid ft_realloc request for %p. cur_size is "
+                          "%ld instead of %ld in (%s:%ld)",
+                          block, cur_size, node->size, file_name, line_no );
+
+    /* return NULL if the maximum number of allocations was reached */
+    if ( table->bound_count                           &&
+         table->alloc_count >= table->alloc_count_max )
+      return NULL;
+
+    delta = (FT_Long)( new_size - cur_size );
+
+    /* return NULL if this allocation would overflow the maximum heap size */
+    if ( delta > 0                                                       &&
+         table->bound_total                                              &&
+         table->alloc_current + (FT_ULong)delta > table->alloc_total_max )
+      return NULL;
+
+    new_block = (FT_Byte *)ft_mem_table_alloc( table, new_size );
+    if ( new_block == NULL )
+      return NULL;
+
+    ft_mem_table_set( table, (FT_Byte*)new_block, new_size, delta );
+
+    ft_memcpy( new_block, block, cur_size < new_size ? cur_size : new_size );
+
+    ft_mem_table_remove( table, (FT_Byte*)block, delta );
+
+    _ft_debug_file   = "<unknown>";
+    _ft_debug_lineno = 0;
+
+    if ( !table->keep_alive )
+      ft_mem_table_free( table, block );
+
+    return new_block;
+  }
+
+
+  extern FT_Int
+  ft_mem_debug_init( FT_Memory  memory )
+  {
+    FT_MemTable  table;
+    FT_Int       result = 0;
+
+
+    if ( getenv( "FT2_DEBUG_MEMORY" ) )
+    {
+      table = ft_mem_table_new( memory );
+      if ( table )
+      {
+        const char*  p;
+
+
+        memory->user    = table;
+        memory->alloc   = ft_mem_debug_alloc;
+        memory->realloc = ft_mem_debug_realloc;
+        memory->free    = ft_mem_debug_free;
+
+        p = getenv( "FT2_ALLOC_TOTAL_MAX" );
+        if ( p != NULL )
+        {
+          FT_Long   total_max = ft_atol( p );
+
+
+          if ( total_max > 0 )
+          {
+            table->bound_total     = 1;
+            table->alloc_total_max = (FT_ULong)total_max;
+          }
+        }
+
+        p = getenv( "FT2_ALLOC_COUNT_MAX" );
+        if ( p != NULL )
+        {
+          FT_Long  total_count = ft_atol( p );
+
+
+          if ( total_count > 0 )
+          {
+            table->bound_count     = 1;
+            table->alloc_count_max = (FT_ULong)total_count;
+          }
+        }
+
+        p = getenv( "FT2_KEEP_ALIVE" );
+        if ( p != NULL )
+        {
+          FT_Long  keep_alive = ft_atol( p );
+
+
+          if ( keep_alive > 0 )
+            table->keep_alive = 1;
+        }
+
+        result = 1;
+      }
+    }
+    return result;
+  }
+
+
+  extern void
+  ft_mem_debug_done( FT_Memory  memory )
+  {
+    FT_MemTable  table = (FT_MemTable)memory->user;
+
+
+    if ( table )
+    {
+      memory->free    = table->free;
+      memory->realloc = table->realloc;
+      memory->alloc   = table->alloc;
+
+      ft_mem_table_destroy( table );
+      memory->user = NULL;
+    }
+  }
+
+
+
+  static int
+  ft_mem_source_compare( const void*  p1,
+                         const void*  p2 )
+  {
+    FT_MemSource  s1 = *(FT_MemSource*)p1;
+    FT_MemSource  s2 = *(FT_MemSource*)p2;
+
+
+    if ( s2->max_size > s1->max_size )
+      return 1;
+    else if ( s2->max_size < s1->max_size )
+      return -1;
+    else
+      return 0;
+  }
+
+
+  extern void
+  FT_DumpMemory( FT_Memory  memory )
+  {
+    FT_MemTable  table = (FT_MemTable)memory->user;
+
+
+    if ( table )
+    {
+      FT_MemSource*  bucket = table->sources;
+      FT_MemSource*  limit  = bucket + FT_MEM_SOURCE_BUCKETS;
+      FT_MemSource*  sources;
+      FT_UInt        nn, count;
+      const char*    fmt;
+
+
+      count = 0;
+      for ( ; bucket < limit; bucket++ )
+      {
+        FT_MemSource  source = *bucket;
+
+
+        for ( ; source; source = source->link )
+          count++;
+      }
+
+      sources = (FT_MemSource*)ft_mem_table_alloc(
+                                 table, sizeof ( *sources ) * count );
+
+      count = 0;
+      for ( bucket = table->sources; bucket < limit; bucket++ )
+      {
+        FT_MemSource  source = *bucket;
+
+
+        for ( ; source; source = source->link )
+          sources[count++] = source;
+      }
+
+      ft_qsort( sources, count, sizeof ( *sources ), ft_mem_source_compare );
+
+      printf( "FreeType Memory Dump: "
+              "current=%ld max=%ld total=%ld count=%ld\n",
+              table->alloc_current, table->alloc_max,
+              table->alloc_total, table->alloc_count );
+      printf( " block  block    sizes    sizes    sizes   source\n" );
+      printf( " count   high      sum  highsum      max   location\n" );
+      printf( "-------------------------------------------------\n" );
+
+      fmt = "%6ld %6ld %8ld %8ld %8ld %s:%d\n";
+
+      for ( nn = 0; nn < count; nn++ )
+      {
+        FT_MemSource  source = sources[nn];
+
+
+        printf( fmt,
+                source->cur_blocks, source->max_blocks,
+                source->cur_size, source->max_size, source->cur_max,
+                FT_FILENAME( source->file_name ),
+                source->line_no );
+      }
+      printf( "------------------------------------------------\n" );
+
+      ft_mem_table_free( table, sources );
+    }
+  }
+
+#else  /* !FT_DEBUG_MEMORY */
+
+  /* ANSI C doesn't like empty source files */
+  const FT_Byte  _debug_mem_dummy = 0;
+
+#endif /* !FT_DEBUG_MEMORY */
+
+
+/* END */