diff --git a/ChangeLog b/ChangeLog index 137891ffd..7619c5fa6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2005-10-20 David Turner + + * 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 * include/freetype/ftbitmap.h (FT_Bitmap_Copy, FT_Bitmap_Embolden), diff --git a/src/base/ftdbgmem.c b/src/base/ftdbgmem.c index 2442a41c4..fd24689db 100644 --- 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 ); - - source->all_blocks++; - source->cur_blocks++; - if ( source->cur_blocks > source->max_blocks ) - source->max_blocks = source->cur_blocks; + 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'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,12 +623,16 @@ /* 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 ) { @@ -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; @@ -754,6 +786,43 @@ 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; } diff --git a/src/lzw/ftlzw2.c b/src/lzw/ftlzw2.c new file mode 100644 index 000000000..a4ef80bc5 --- /dev/null +++ b/src/lzw/ftlzw2.c @@ -0,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 +#include FT_INTERNAL_MEMORY_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_DEBUG_H +#include +#include + + +#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 */ diff --git a/src/lzw/ftzopen.c b/src/lzw/ftzopen.c new file mode 100644 index 000000000..aa5f1a808 --- /dev/null +++ b/src/lzw/ftzopen.c @@ -0,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; +} diff --git a/src/lzw/ftzopen.h b/src/lzw/ftzopen.h new file mode 100644 index 000000000..89ede530b --- /dev/null +++ b/src/lzw/ftzopen.h @@ -0,0 +1,142 @@ +#ifndef __FT_ZOPEN_H__ +#define __FT_ZOPEN_H__ + +#include +#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__ */ diff --git a/src/lzw/rules.mk b/src/lzw/rules.mk index 73ce71804..8f748c8b4 100644 --- a/src/lzw/rules.mk +++ b/src/lzw/rules.mk @@ -28,12 +28,12 @@ LZW_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(LZW_DIR)) # 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_H := $(LZW_DIR)/zopen.h # 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