static void ft_mem_table_set( FT_MemTable table, FT_Byte* address, FT_ULong size ) { FT_MemNode *pnode, node; if ( table ) { pnode = ft_mem_table_get_nodep( table, address ); node = *pnode; if ( node ) { if ( node->size < 0 ) { /* this block was already freed. This means that our memory is */ /* now completely corrupted! */ 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)" ); } } /* 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->alloc_file_name = table->file_name; node->alloc_line_no = table->line_no; node->free_file_name = NULL; node->free_line_no = 0; node->link = pnode[0]; pnode[0] = node; table->nodes++; table->alloc_total += size; table->alloc_current += 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 ); } }
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; const char* file_name = FT_FILENAME( table->file_name ); FT_Long line_no = table->line_no; /* 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 ); new_block = ft_mem_debug_alloc( memory, new_size ); if ( new_block == NULL ) return NULL; ft_memcpy( new_block, block, cur_size < new_size ? cur_size : new_size ); table->file_name = file_name; table->line_no = line_no; ft_mem_debug_free( memory, (FT_Byte*)block ); return new_block; }
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; const char* file_name = FT_FILENAME( table->file_name ); FT_Long line_no = table->line_no; if ( block == NULL || cur_size == 0 ) ft_mem_debug_panic( "trying to reallocate NULL in (%s:%ld)", file_name, line_no ); 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 ); new_block = ft_mem_debug_alloc( memory, new_size ); if ( new_block == NULL ) return NULL; ft_memcpy( new_block, block, cur_size < new_size ? cur_size : new_size ); table->file_name = file_name; table->line_no = line_no; ft_mem_debug_free( memory, (FT_Byte*)block ); return new_block; }
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_current + (FT_ULong)size > table->alloc_total_max ) return NULL; block = (FT_Byte *)ft_mem_table_alloc( table, size ); if ( block ) ft_mem_table_set( table, block, (FT_ULong)size ); table->alloc_count++; table->file_name = NULL; table->line_no = 0; return (FT_Pointer) block; }
static 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 > size ) return NULL; block = (FT_Byte *)ft_mem_table_alloc( table, size ); if ( block ) { ft_mem_table_set( table, block, size, 0 ); table->alloc_count++; } _ft_debug_file = "<unknown>"; _ft_debug_lineno = 0; return (FT_Pointer)block; }
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_remove( FT_MemTable table, FT_Byte* address ) { if ( table ) { FT_MemNode *pnode, node; pnode = ft_mem_table_get_nodep( table, address ); node = *pnode; if ( node ) { 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( table->file_name ), table->line_no, FT_FILENAME( node->alloc_file_name ), node->alloc_line_no, FT_FILENAME( node->free_file_name ), node->free_line_no ); /* we simply invert the node's size to indicate that the node */ /* was freed. We also change its contents. */ FT_MEM_SET( address, 0xF3, node->size ); table->alloc_current -= node->size; node->size = -node->size; node->free_file_name = table->file_name; node->free_line_no = table->line_no; } else ft_mem_debug_panic( "trying to free unknown block at %p in (%s:%ld)\n", address, FT_FILENAME( table->file_name ), table->line_no ); } }
static FT_MemSource ft_mem_table_get_source( FT_MemTable table ) { FT_UInt32 hash; FT_MemSource node, *pnode; hash = (FT_UInt32)(void*)table->file_name + (FT_UInt32)( 5 * table->line_no ); pnode = &table->sources[hash % FT_MEM_SOURCE_BUCKETS]; for ( ;; ) { node = *pnode; if ( node == NULL ) break; if ( node->file_name == table->file_name && node->line_no == table->line_no ) 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 = table->file_name; node->line_no = table->line_no; 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; }
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( table->file_name ), table->line_no ); ft_mem_table_remove( table, (FT_Byte*)block ); /* we never really free the block */ table->file_name = NULL; table->line_no = 0; }
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 ); block = (FT_Byte *)ft_mem_table_alloc( table, size ); if ( block ) ft_mem_table_set( table, block, (FT_ULong)size ); table->file_name = NULL; table->line_no = 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; }
static void ft_mem_table_destroy( FT_MemTable table ) { FT_ULong i; if ( table ) { FT_Long leak_count = 0; FT_ULong leaks = 0; 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->alloc_file_name ), node->alloc_line_no ); leak_count++; leaks += node->size; ft_mem_table_free( table, node->address ); } node->address = NULL; node->size = 0; free( node ); node = next; } table->buckets[i] = 0; } ft_mem_table_free( table, table->buckets ); table->buckets = NULL; table->size = 0; table->nodes = 0; printf( "FreeType: total memory allocations = %ld\n", table->alloc_total ); printf( "FreeType: maximum memory footprint = %ld\n", table->alloc_max ); free( 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" ); } }
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; }
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 ); } }
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_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 ) { FT_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; } FT_PRINTF( "FreeType: total memory allocations = %ld\n", table->alloc_total ); FT_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 ); FT_PRINTF( "FreeType: No memory leaks detected!\n" ); } }