ref: c1b6d08291087287bceb4dc2ec2c82a295adc575
parent: 7e30776ee8b96dd2d5a9646db5a4ed3356690447
author: David Turner <[email protected]>
date: Thu Oct 20 11:33:34 EDT 2005
* src/base/ftdbgmem.c: fixes to better account for memory reallocations * src/lzw/ftlzw2.c, src/lzw/ftzopen.h, src/lzw/ftzopen.c, src/lzw/rules.mk: first version of LZW loader re-implementation. Apparently, saves about 260 KB of heap memory when loading tir24.pcf.Z
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2005-10-20 David Turner <[email protected]>
+
+ * src/base/ftdbgmem.c: fixes to better account for memory reallocations
+
+ * src/lzw/ftlzw2.c, src/lzw/ftzopen.h, src/lzw/ftzopen.c, src/lzw/rules.mk:
+ first version of LZW loader re-implementation. Apparently, saves about
+ 260 KB of heap memory when loading timR24.pcf.Z
+
2005-10-20 Chia-I Wu <[email protected]>
* include/freetype/ftbitmap.h (FT_Bitmap_Copy, FT_Bitmap_Embolden),
--- a/src/base/ftdbgmem.c
+++ b/src/base/ftdbgmem.c
@@ -493,7 +493,8 @@
static void
ft_mem_table_set( FT_MemTable table,
FT_Byte* address,
- FT_ULong size )
+ FT_ULong size,
+ FT_Long delta )
{
FT_MemNode *pnode, node;
@@ -536,19 +537,33 @@
node->address = address;
node->size = size;
+ node->source = source = ft_mem_table_get_source( table );
- 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;
+ }
- 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're growing or shrinking a realloc-ed block */
+ source->cur_size += delta;
+ }
+ else
+ {
+ /* we're allocating a new block */
+ source->cur_size += size;
+ }
+
source->all_size += size;
- source->cur_size += size;
+
if ( source->cur_size > source->max_size )
source->max_size = source->cur_size;
@@ -560,8 +575,16 @@
pnode[0] = node;
table->nodes++;
- table->alloc_total += size;
- table->alloc_current += size;
+ if ( delta != 0 )
+ {
+ table->alloc_total += size;
+ table->alloc_current += delta;
+ }
+ else
+ {
+ table->alloc_total += size;
+ table->alloc_current += size;
+ }
if ( table->alloc_current > table->alloc_max )
table->alloc_max = table->alloc_current;
@@ -574,7 +597,8 @@
static void
ft_mem_table_remove( FT_MemTable table,
- FT_Byte* address )
+ FT_Byte* address,
+ FT_Long delta )
{
if ( table )
{
@@ -599,13 +623,17 @@
/* scramble the node's content for additional safety */
FT_MEM_SET( address, 0xF3, node->size );
- table->alloc_current -= node->size;
- source = node->source;
+ if ( delta == 0 )
+ {
+ source = node->source;
- source->cur_blocks--;
- source->cur_size -= node->size;
+ 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 */
@@ -662,9 +690,11 @@
block = (FT_Byte *)ft_mem_table_alloc( table, size );
if ( block )
- ft_mem_table_set( table, block, (FT_ULong)size );
+ {
+ ft_mem_table_set( table, block, (FT_ULong)size, 0 );
- table->alloc_count++;
+ table->alloc_count++;
+ }
table->file_name = NULL;
table->line_no = 0;
@@ -685,7 +715,7 @@
FT_FILENAME( table->file_name ),
table->line_no );
- ft_mem_table_remove( table, (FT_Byte*)block );
+ ft_mem_table_remove( table, (FT_Byte*)block, 0 );
if ( !table->keep_alive )
ft_mem_table_free( table, block );
@@ -706,6 +736,7 @@
FT_MemTable table = (FT_MemTable)memory->user;
FT_MemNode node, *pnode;
FT_Pointer new_block;
+ FT_Long delta;
const char* file_name = FT_FILENAME( table->file_name );
FT_Long line_no = table->line_no;
@@ -743,6 +774,7 @@
"%ld instead of %ld in (%s:%ld)",
block, cur_size, node->size, file_name, line_no );
+#if 0
new_block = ft_mem_debug_alloc( memory, new_size );
if ( new_block == NULL )
return NULL;
@@ -753,6 +785,43 @@
table->line_no = line_no;
ft_mem_debug_free( memory, (FT_Byte*)block );
+
+#else
+ /* 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, new_block, new_size, delta );
+
+ table->file_name = NULL;
+ table->line_no = 0;
+
+ 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_table_remove( table, (FT_Byte*)block, delta );
+
+ if ( !table->keep_alive )
+ ft_mem_table_free( table, block );
+
+ table->alloc_current += delta;
+
+#endif
return new_block;
}
--- /dev/null
+++ b/src/lzw/ftlzw2.c
@@ -1,0 +1,411 @@
+/***************************************************************************/
+/* */
+/* ftlzw.c */
+/* */
+/* FreeType support for .Z compressed files. */
+/* */
+/* This optional component relies on NetBSD's zopen(). It should mainly */
+/* be used to parse compressed PCF fonts, as found with many X11 server */
+/* distributions. */
+/* */
+/* Copyright 2004, 2005 by */
+/* Albert Chin-A-Young. */
+/* */
+/* Based on code in src/gzip/ftgzip.c, Copyright 2004 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_INTERNAL_MEMORY_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_DEBUG_H
+#include <string.h>
+#include <stdio.h>
+
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX LZW_Err_
+#define FT_ERR_BASE FT_Mod_Err_LZW
+
+#include FT_ERRORS_H
+
+
+#ifdef FT_CONFIG_OPTION_USE_LZW
+
+#include "ftzopen.h"
+
+
+/***************************************************************************/
+/***************************************************************************/
+/***** *****/
+/***** M E M O R Y M A N A G E M E N T *****/
+/***** *****/
+/***************************************************************************/
+/***************************************************************************/
+
+/***************************************************************************/
+/***************************************************************************/
+/***** *****/
+/***** F I L E D E S C R I P T O R *****/
+/***** *****/
+/***************************************************************************/
+/***************************************************************************/
+
+#define FT_LZW_BUFFER_SIZE 4096
+
+ typedef struct FT_LZWFileRec_
+ {
+ FT_Stream source; /* parent/source stream */
+ FT_Stream stream; /* embedding stream */
+ FT_Memory memory; /* memory allocator */
+ FT_LzwStateRec lzw; /* lzw decompressor state */
+
+ FT_Byte buffer[ FT_LZW_BUFFER_SIZE ]; /* output buffer */
+ FT_ULong pos; /* position in output */
+ FT_Byte* cursor;
+ FT_Byte* limit;
+
+ } FT_LZWFileRec, *FT_LZWFile;
+
+
+ /* check and skip .Z header */
+ static FT_Error
+ ft_lzw_check_header( FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Byte head[2];
+
+
+ if ( FT_STREAM_SEEK( 0 ) ||
+ FT_STREAM_READ( head, 2 ) )
+ goto Exit;
+
+ /* head[0] && head[1] are the magic numbers */
+ if ( head[0] != 0x1f ||
+ head[1] != 0x9d )
+ error = LZW_Err_Invalid_File_Format;
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ ft_lzw_file_init( FT_LZWFile zip,
+ FT_Stream stream,
+ FT_Stream source )
+ {
+ FT_LzwState lzw = &zip->lzw;
+ FT_Error error = LZW_Err_Ok;
+
+
+ zip->stream = stream;
+ zip->source = source;
+ zip->memory = stream->memory;
+
+ zip->limit = zip->buffer + FT_LZW_BUFFER_SIZE;
+ zip->cursor = zip->limit;
+ zip->pos = 0;
+
+ /* check and skip .Z header */
+ {
+ stream = source;
+
+ error = ft_lzw_check_header( source );
+ if ( error )
+ goto Exit;
+ }
+
+ /* initialize internal lzw variable */
+ ft_lzwstate_init( lzw, source );
+
+ Exit:
+ return error;
+ }
+
+
+ static void
+ ft_lzw_file_done( FT_LZWFile zip )
+ {
+ /* clear the rest */
+ ft_lzwstate_done( &zip->lzw );
+
+ zip->memory = NULL;
+ zip->source = NULL;
+ zip->stream = NULL;
+ }
+
+
+ static FT_Error
+ ft_lzw_file_reset( FT_LZWFile zip )
+ {
+ FT_Stream stream = zip->source;
+ FT_Error error;
+
+
+ if ( !FT_STREAM_SEEK( 0 ) )
+ {
+ ft_lzwstate_reset( &zip->lzw );
+
+ zip->limit = zip->buffer + FT_LZW_BUFFER_SIZE;
+ zip->cursor = zip->limit;
+ zip->pos = 0;
+ }
+
+ return error;
+ }
+
+
+ static FT_Error
+ ft_lzw_file_fill_output( FT_LZWFile zip )
+ {
+ FT_LzwState lzw = &zip->lzw;
+ FT_ULong count;
+ FT_Error error = 0;
+
+
+ zip->cursor = zip->buffer;
+
+ count = ft_lzwstate_io( lzw, zip->buffer, FT_LZW_BUFFER_SIZE );
+
+ zip->limit = zip->cursor + count;
+
+ if ( count == 0 )
+ error = LZW_Err_Invalid_Stream_Operation;
+
+ return error;
+ }
+
+
+ /* fill output buffer; `count' must be <= FT_LZW_BUFFER_SIZE */
+ static FT_Error
+ ft_lzw_file_skip_output( FT_LZWFile zip,
+ FT_ULong count )
+ {
+ FT_Error error = LZW_Err_Ok;
+
+ /* first, we skip what we can from the output buffer
+ */
+ {
+ FT_ULong delta = (FT_ULong)( zip->limit - zip->cursor );
+
+ if ( delta >= count )
+ delta = count;
+
+ zip->cursor += delta;
+ zip->pos += delta;
+
+ count -= delta;
+ }
+
+ /* next, we skip as many bytes remaining as possible
+ */
+ while ( count > 0 )
+ {
+ FT_ULong delta = FT_LZW_BUFFER_SIZE;
+ FT_ULong numread;
+
+ if ( delta > count )
+ delta = count;
+
+ numread = ft_lzwstate_io( &zip->lzw, NULL, delta );
+ if ( numread < delta )
+ {
+ /* not enough bytes */
+ error = LZW_Err_Invalid_Stream_Operation;
+ break;
+ }
+
+ zip->pos += delta;
+ count -= delta;
+ }
+
+ return error;
+ }
+
+
+ static FT_ULong
+ ft_lzw_file_io( FT_LZWFile zip,
+ FT_ULong pos,
+ FT_Byte* buffer,
+ FT_ULong count )
+ {
+ FT_ULong result = 0;
+ FT_Error error;
+
+
+ /* seeking backwards. */
+ if ( pos < zip->pos )
+ {
+ /* if the new position is within the output buffer, simply
+ * decrement pointers, otherwise, we'll reset the stream completely !!
+ */
+ if ( (zip->pos - pos) <= (FT_ULong)(zip->cursor - zip->buffer) )
+ {
+ zip->cursor -= (zip->pos - pos);
+ zip->pos = pos;
+ }
+ else
+ {
+ error = ft_lzw_file_reset( zip );
+ if ( error )
+ goto Exit;
+ }
+ }
+
+ /* skip unwanted bytes */
+ if ( pos > zip->pos )
+ {
+ error = ft_lzw_file_skip_output( zip, (FT_ULong)( pos - zip->pos ) );
+ if ( error )
+ goto Exit;
+ }
+
+ if ( count == 0 )
+ goto Exit;
+
+ /* now read the data */
+ for (;;)
+ {
+ FT_ULong delta;
+
+
+ delta = (FT_ULong)( zip->limit - zip->cursor );
+ if ( delta >= count )
+ delta = count;
+
+ FT_MEM_COPY( buffer + result, zip->cursor, delta );
+ result += delta;
+ zip->cursor += delta;
+ zip->pos += delta;
+
+ count -= delta;
+ if ( count == 0 )
+ break;
+
+ error = ft_lzw_file_fill_output( zip );
+ if ( error )
+ break;
+ }
+
+ Exit:
+ return result;
+ }
+
+
+/***************************************************************************/
+/***************************************************************************/
+/***** *****/
+/***** L Z W E M B E D D I N G S T R E A M *****/
+/***** *****/
+/***************************************************************************/
+/***************************************************************************/
+
+ static void
+ ft_lzw_stream_close( FT_Stream stream )
+ {
+ FT_LZWFile zip = (FT_LZWFile)stream->descriptor.pointer;
+ FT_Memory memory = stream->memory;
+
+
+ if ( zip )
+ {
+ /* finalize lzw file descriptor */
+ ft_lzw_file_done( zip );
+
+ FT_FREE( zip );
+
+ stream->descriptor.pointer = NULL;
+ }
+ }
+
+
+ static FT_ULong
+ ft_lzw_stream_io( FT_Stream stream,
+ FT_ULong pos,
+ FT_Byte* buffer,
+ FT_ULong count )
+ {
+ FT_LZWFile zip = (FT_LZWFile)stream->descriptor.pointer;
+
+
+ return ft_lzw_file_io( zip, pos, buffer, count );
+ }
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stream_OpenLZW( FT_Stream stream,
+ FT_Stream source )
+ {
+ FT_Error error;
+ FT_Memory memory = source->memory;
+ FT_LZWFile zip;
+
+
+ /*
+ * Check the header right now; this prevents allocation a huge
+ * LZWFile object (400 KByte of heap memory) if not necessary.
+ *
+ * Did I mention that you should never use .Z compressed font
+ * file?
+ */
+ error = ft_lzw_check_header( source );
+ if ( error )
+ goto Exit;
+
+ FT_ZERO( stream );
+ stream->memory = memory;
+
+ if ( !FT_NEW( zip ) )
+ {
+ error = ft_lzw_file_init( zip, stream, source );
+ if ( error )
+ {
+ FT_FREE( zip );
+ goto Exit;
+ }
+
+ stream->descriptor.pointer = zip;
+ }
+
+ stream->size = 0x7FFFFFFFL; /* don't know the real size! */
+ stream->pos = 0;
+ stream->base = 0;
+ stream->read = ft_lzw_stream_io;
+ stream->close = ft_lzw_stream_close;
+
+ Exit:
+ return error;
+ }
+
+#include "ftzopen.c"
+
+
+#else /* !FT_CONFIG_OPTION_USE_LZW */
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stream_OpenLZW( FT_Stream stream,
+ FT_Stream source )
+ {
+ FT_UNUSED( stream );
+ FT_UNUSED( source );
+
+ return LZW_Err_Unimplemented_Feature;
+ }
+
+
+#endif /* !FT_CONFIG_OPTION_USE_LZW */
+
+
+/* END */
--- /dev/null
+++ b/src/lzw/ftzopen.c
@@ -1,0 +1,355 @@
+#include "ftzopen.h"
+#include FT_INTERNAL_MEMORY_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_DEBUG_H
+
+/* refill input buffer, return 0 on success, or -1 if eof
+ */
+static int
+ft_lzwstate_refill( FT_LzwState state )
+{
+ int result = -1;
+
+ if ( !state->in_eof )
+ {
+ FT_ULong count = FT_Stream_TryRead( state->source,
+ state->in_buff,
+ sizeof( state->in_buff ) );
+
+ state->in_cursor = state->in_buff;
+ state->in_limit = state->in_buff + count;
+ state->in_eof = FT_BOOL( count < sizeof( state->in_buff ) );
+
+ if ( count > 0 )
+ result = 0;
+ }
+ return result;
+}
+
+
+/* return new code of 'num_bits', or -1 if eof
+ */
+static FT_Int32
+ft_lzwstate_get_code( FT_LzwState state,
+ FT_UInt num_bits )
+{
+ FT_Int32 result = -1;
+ FT_UInt32 pad = state->pad;
+ FT_UInt pad_bits = state->pad_bits;
+
+ while ( num_bits > pad_bits )
+ {
+ if ( state->in_cursor >= state->in_limit &&
+ ft_lzwstate_refill( state ) < 0 )
+ goto Exit;
+
+ pad |= (FT_UInt32)(*state->in_cursor++) << pad_bits;
+ pad_bits += 8;
+ }
+
+ result = (FT_Int32)( pad & LZW_MASK(num_bits) );
+ state->pad_bits = pad_bits - num_bits;
+ state->pad = pad >> num_bits;
+
+Exit:
+ return result;
+}
+
+
+/* grow the character stack
+ */
+static int
+ft_lzwstate_stack_grow( FT_LzwState state )
+{
+ if ( state->stack_top >= state->stack_size )
+ {
+ FT_Memory memory = state->memory;
+ FT_Error error;
+ FT_UInt old_size = state->stack_size;
+ FT_UInt new_size = old_size;
+
+ new_size = new_size + (new_size >> 1) + 4;
+
+ if ( state->stack == state->stack_0 )
+ {
+ state->stack = NULL;
+ old_size = 0;
+ }
+
+ if ( FT_RENEW_ARRAY( state->stack, old_size, new_size ) )
+ return -1;
+
+ state->stack_size = new_size;
+ }
+ return 0;
+}
+
+
+/* grow the prefix/suffix arrays
+ */
+static int
+ft_lzwstate_prefix_grow( FT_LzwState state )
+{
+ FT_UInt old_size = state->prefix_size;
+ FT_UInt new_size = old_size;
+ FT_Memory memory = state->memory;
+ FT_Error error;
+
+ if ( new_size == 0 ) /* first allocation -> 9 bits */
+ new_size = 512;
+ else
+ new_size += (new_size >> 2); /* don't grow too fast */
+
+ /* note that the 'suffix' array is located in the same memory block
+ * pointed to by 'prefix'
+ *
+ * I know that sizeof(FT_Byte) == 1 by definition, but it's clearer
+ * to write it literally
+ */
+ if ( FT_REALLOC( state->prefix,
+ old_size*(sizeof(FT_UShort)+sizeof(FT_Byte)),
+ new_size*(sizeof(FT_UShort)+sizeof(FT_Byte)) ) )
+ return -1;
+
+ /* now adjust 'suffix' and move the data accordingly */
+ state->suffix = (FT_Byte*)(state->prefix + new_size);
+
+ FT_MEM_MOVE( state->suffix,
+ state->prefix + old_size,
+ old_size * sizeof(FT_Byte) );
+
+ state->prefix_size = new_size;
+ return 0;
+}
+
+
+FT_LOCAL_DEF( void )
+ft_lzwstate_reset( FT_LzwState state )
+{
+ state->in_cursor = state->in_buff;
+ state->in_limit = state->in_buff;
+ state->in_eof = 0;
+ state->pad_bits = 0;
+ state->pad = 0;
+
+ state->stack_top = 0;
+ state->num_bits = LZW_INIT_BITS;
+ state->phase = FT_LZW_PHASE_START;
+}
+
+
+FT_LOCAL_DEF( void )
+ft_lzwstate_init( FT_LzwState state,
+ FT_Stream source )
+{
+ FT_ZERO( state );
+
+ state->source = source;
+ state->memory = source->memory;
+
+ state->prefix = NULL;
+ state->suffix = NULL;
+ state->prefix_size = 0;
+
+ state->stack = state->stack_0;
+ state->stack_size = sizeof(state->stack_0);
+
+ ft_lzwstate_reset( state );
+}
+
+
+FT_LOCAL_DEF( void )
+ft_lzwstate_done( FT_LzwState state )
+{
+ FT_Memory memory = state->memory;
+
+ ft_lzwstate_reset( state );
+
+ if ( state->stack != state->stack_0 )
+ FT_FREE( state->stack );
+
+ FT_FREE( state->prefix );
+ state->suffix = NULL;
+
+ FT_ZERO( state );
+}
+
+
+#define FTLZW_STACK_PUSH(c) \
+ FT_BEGIN_STMNT \
+ if ( state->stack_top >= state->stack_size && \
+ ft_lzwstate_stack_grow( state ) < 0 ) \
+ goto Eof; \
+ \
+ state->stack[ state->stack_top++ ] = (FT_Byte)(c); \
+ FT_END_STMNT
+
+
+
+FT_LOCAL_DEF( FT_ULong )
+ft_lzwstate_io( FT_LzwState state,
+ FT_Byte* buffer,
+ FT_ULong out_size )
+{
+ FT_ULong result = 0;
+
+ FT_UInt num_bits = state->num_bits;
+ FT_UInt free_ent = state->free_ent;
+ FT_UInt old_char = state->old_char;
+ FT_UInt old_code = state->old_code;
+ FT_UInt in_code = state->in_code;
+
+ if ( out_size == 0 )
+ goto Exit;
+
+ switch ( state->phase )
+ {
+ case FT_LZW_PHASE_START:
+ {
+ FT_Byte max_bits;
+ FT_Int32 c;
+
+ /* skip magic bytes, and read max_bits + block_flag */
+ if ( FT_Stream_Seek( state->source, 2 ) != 0 ||
+ FT_Stream_TryRead( state->source, &max_bits, 1 ) != 1 )
+ goto Eof;
+
+ state->max_bits = max_bits & LZW_BIT_MASK;
+ state->block_mode = max_bits & LZW_BLOCK_MASK;
+ state->max_free = (FT_UInt)((1UL << state->max_bits) - 256);
+
+ if ( state->max_bits > LZW_MAX_BITS )
+ goto Eof;
+
+ num_bits = LZW_INIT_BITS;
+ free_ent = (state->block_mode ? LZW_FIRST : LZW_CLEAR) - 256;
+ in_code = 0;
+
+ state->free_bits = num_bits < state->max_bits
+ ? (FT_UInt)((1UL << num_bits) - 256)
+ : state->max_free+1;
+
+ c = ft_lzwstate_get_code( state, num_bits );
+ if ( c < 0 )
+ goto Eof;
+
+ old_code = old_char = (FT_UInt)c;
+
+ if ( buffer )
+ buffer[result] = (FT_Byte)old_char;
+
+ if ( ++result >= out_size )
+ goto Exit;
+
+ state->phase = FT_LZW_PHASE_CODE;
+ }
+ /* fall-through */
+
+ case FT_LZW_PHASE_CODE:
+ {
+ FT_Int32 c;
+ FT_UInt code;
+
+ NextCode:
+ c = ft_lzwstate_get_code( state, num_bits );
+ if ( c < 0 ) goto Eof;
+
+ code = (FT_UInt)c;
+
+ if ( code == LZW_CLEAR && state->block_mode )
+ {
+ free_ent = (LZW_FIRST-1)-256; /* why not LZW_FIRST-256 ? */
+ num_bits = LZW_INIT_BITS;
+
+ state->free_bits = num_bits < state->max_bits
+ ? (FT_UInt)((1UL << num_bits) - 256)
+ : state->max_free+1;
+
+ c = ft_lzwstate_get_code( state, num_bits );
+ if ( c < 0 ) goto Eof;
+
+ code = (FT_UInt)c;
+ }
+
+ in_code = code; /* save code for later */
+
+ if ( code >= 256U )
+ {
+ /* special case for KwKwKwK */
+ if ( code-256U >= free_ent )
+ {
+ FTLZW_STACK_PUSH( old_char );
+ code = old_code;
+ }
+
+ while ( code >= 256U )
+ {
+ FTLZW_STACK_PUSH( state->suffix[code-256] );
+ code = state->prefix[code-256];
+ }
+ }
+
+ old_char = code;
+ FTLZW_STACK_PUSH(old_char);
+
+ state->phase = FT_LZW_PHASE_STACK;
+ }
+ /* fall-through */
+
+ case FT_LZW_PHASE_STACK:
+ {
+ while ( state->stack_top > 0 )
+ {
+ --state->stack_top;
+
+ if ( buffer )
+ buffer[result] = state->stack[state->stack_top];
+
+ if ( ++result == out_size )
+ goto Exit;
+ }
+
+ /* now create new entry */
+ if ( free_ent < state->max_free )
+ {
+ if ( free_ent >= state->prefix_size &&
+ ft_lzwstate_prefix_grow( state ) < 0 )
+ goto Eof;
+
+ FT_ASSERT( free_ent < state->prefix_size );
+
+ state->prefix[free_ent] = (FT_UShort) old_code;
+ state->suffix[free_ent] = (FT_Byte) old_char;
+
+ if ( ++free_ent == state->free_bits )
+ {
+ num_bits++;
+
+ state->free_bits = num_bits < state->max_bits
+ ? (FT_UInt)((1UL << num_bits)-256)
+ : state->max_free+1;
+ }
+ }
+
+ old_code = in_code;
+
+ state->phase = FT_LZW_PHASE_CODE;
+ goto NextCode;
+ }
+
+ default: /* state == EOF */
+ ;
+ }
+Exit:
+ state->num_bits = num_bits;
+ state->free_ent = free_ent;
+ state->old_code = old_code;
+ state->old_char = old_char;
+ state->in_code = in_code;
+
+ return result;
+
+Eof:
+ state->phase = FT_LZW_PHASE_EOF;
+ goto Exit;
+}
--- /dev/null
+++ b/src/lzw/ftzopen.h
@@ -1,0 +1,142 @@
+#ifndef __FT_ZOPEN_H__
+#define __FT_ZOPEN_H__
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+/* this is a complete re-implementation of the LZW file reader,
+ * since the old one was incredibly badly written, and used
+ * 400 Kb of heap memory before decompressing anything.
+ */
+
+#define FT_LZW_IN_BUFF_SIZE 64
+#define FT_LZW_DEFAULT_STACK_SIZE 64
+
+#define LZW_INIT_BITS 9
+#define LZW_MAX_BITS 16
+
+#define LZW_CLEAR 256
+#define LZW_FIRST 257
+
+#define LZW_BIT_MASK 0x1f
+#define LZW_BLOCK_MASK 0x80
+#define LZW_MASK(n) ((1U << (n)) - 1U)
+
+typedef enum
+{
+ FT_LZW_PHASE_START = 0,
+ FT_LZW_PHASE_CODE,
+ FT_LZW_PHASE_STACK,
+ FT_LZW_PHASE_EOF
+
+} FT_LzwPhase;
+
+
+/* state of LZW decompressor
+ *
+ * small technical note:
+ *
+ * we use a few tricks in this implementation that are explained here to
+ * ease debugging and maintenance.
+ *
+ * - first of all, the "prefix" and "suffix" arrays contain the
+ * suffix and prefix for codes over 256, this means that:
+ *
+ * prefix_of(code) == state->prefix[ code-256 ]
+ * suffix_of(code) == state->suffix[ code-256 ]
+ *
+ * each prefix is a 16-bit code, and each suffix an 8-bit byte
+ *
+ * both arrays are stored in a single memory block, pointed to by
+ * 'state->prefix', this means that the following equality is always
+ * true:
+ *
+ * state->suffix == (FT_Byte*)(state->prefix + state->prefix_size)
+ *
+ * of course, state->prefix_size is the number of prefix/suffix slots
+ * in the arrays, corresponding to codes 256..255+prefix_size
+ *
+ * - 'free_ent' is the index of the next free entry in the "prefix"
+ * and "suffix" arrays. This means that the corresponding "next free
+ * code" is really '256+free_ent'
+ *
+ * moreover, 'max_free' is the maximum value that 'free_ent' can reach.
+ *
+ * 'max_free' corresponds to "(1 << max_bits) - 256". Note that this value
+ * is always <= 0xFF00, which means that both 'free_ent' and 'max_free' can
+ * be stored in FT_UInt variable, even on 16-bit machines.
+ *
+ * if 'free_ent == max_free', you cannot add new codes to the prefix/suffix
+ * table.
+ *
+ * - 'num_bits' is the current number of code bits, starting at 9 and
+ * growing each time 'free_ent' reaches the value of 'free_bits'. the
+ * latter is computed as follows:
+ *
+ * if num_bits < max_bits:
+ * free_bits = (1 << num_bits)-256
+ * else:
+ * free_bits = max_free + 1
+ *
+ * since the value of 'max_free + 1' can never be reached by 'free_ent',
+ * 'num_bits' cannot grow larger than 'max_bits'
+ */
+typedef struct
+{
+ FT_LzwPhase phase;
+
+ FT_Int in_eof;
+ FT_Byte* in_cursor; /* current buffer pos */
+ FT_Byte* in_limit; /* current buffer limit */
+
+ FT_UInt32 pad; /* a pad value where incoming bits were read */
+ FT_Int pad_bits; /* number of meaningful bits in pad value */
+
+ FT_UInt max_bits; /* max code bits, from file header */
+ FT_Int block_mode; /* block mode flag, from file header */
+ FT_UInt max_free; /* (1 << max_bits) - 256 */
+
+ FT_UInt num_bits; /* current code bit number */
+ FT_UInt free_ent; /* index of next free entry */
+ FT_UInt free_bits; /* if free_ent reaches this, increment num_bits */
+ FT_UInt old_code;
+ FT_UInt old_char;
+ FT_UInt in_code;
+
+ FT_UShort* prefix; /* always dynamically allocated / reallocated */
+ FT_Byte* suffix; /* suffix = (FT_Byte*)(prefix + prefix_size) */
+ FT_UInt prefix_size; /* number of slots in 'prefix' or 'suffix' */
+
+ FT_Byte* stack; /* character stack */
+ FT_UInt stack_top;
+ FT_UInt stack_size;
+
+ FT_Byte in_buff[ FT_LZW_IN_BUFF_SIZE ]; /* small buffer to read data */
+ FT_Byte stack_0[ FT_LZW_DEFAULT_STACK_SIZE ]; /* minimize heap alloc */
+
+ FT_Stream source; /* source stream */
+ FT_Memory memory;
+
+} FT_LzwStateRec, *FT_LzwState;
+
+
+FT_LOCAL( void )
+ft_lzwstate_init( FT_LzwState state,
+ FT_Stream source );
+
+FT_LOCAL( void )
+ft_lzwstate_done( FT_LzwState state );
+
+
+FT_LOCAL( void )
+ft_lzwstate_reset( FT_LzwState state );
+
+
+FT_LOCAL( FT_ULong )
+ft_lzwstate_io( FT_LzwState state,
+ FT_Byte* buffer,
+ FT_ULong out_size );
+
+/* */
+
+#endif /* __FT_ZOPEN_H__ */
--- a/src/lzw/rules.mk
+++ b/src/lzw/rules.mk
@@ -28,12 +28,12 @@
# LZW support sources (i.e., C files)
#
-LZW_DRV_SRC := $(LZW_DIR)/ftlzw.c \
- $(LZW_DIR)/zopen.c
+LZW_DRV_SRC := $(LZW_DIR)/ftlzw2.c
# LZW support headers
#
-LZW_DRV_H := $(LZW_DIR)/zopen.h
+LZW_DRV_H := $(LZW_DIR)/ftzopen.h \
+ $(LZW_DIR)/ftzopen.c
# LZW driver object(s)
@@ -41,12 +41,12 @@
# LZW_DRV_OBJ_M is used during `multi' builds
# LZW_DRV_OBJ_S is used during `single' builds
#
-LZW_DRV_OBJ_M := $(OBJ_DIR)/ftlzw.$O
-LZW_DRV_OBJ_S := $(OBJ_DIR)/ftlzw.$O
+LZW_DRV_OBJ_M := $(OBJ_DIR)/ftlzw2.$O
+LZW_DRV_OBJ_S := $(OBJ_DIR)/ftlzw2.$O
# LZW support source file for single build
#
-LZW_DRV_SRC_S := $(LZW_DIR)/ftlzw.c
+LZW_DRV_SRC_S := $(LZW_DIR)/ftlzw2.c
# LZW support - single object