class TLZWCompress {
private:
short code_value[TABLE_SIZE];
unsigned short prefix_code[TABLE_SIZE];
unsigned char append_character[TABLE_SIZE];
unsigned char decode_stack[4000];
short num_bits;
unsigned long bytes_in,bytes_out;
short max_code;
unsigned long checkpoint;
long ReadCount;
short output_bit_count;
unsigned long output_bit_buffer;
short input_bit_count;
unsigned long input_bit_buffer;
unsigned short find_match(short hash_prefix, unsigned short hash_character);
long output_code(unsigned char *OutBuffer,long count,unsigned short code);
unsigned short input_code(unsigned char *InBuffer,long len);
unsigned char *decode_string(unsigned char *buffer, unsigned short code);
public:
long Compress(unsigned char *InBuffer,long len,unsigned char *OutBuffer);
long Expand(unsigned char *InBuffer,long len,unsigned char *OutBuffer);
};
unsigned short TLZWCompress::find_match(short hash_prefix, unsigned short hash_character)
{ short index, offset;
index = (hash_character << HASHING_SHIFT ) ^ hash_prefix;
if (index == 0 ) offset=1; else offset = TABLE_SIZE - index;
while (code_value[index]!=-1)
{ if ((prefix_code[index]==hash_prefix)&&(append_character[index]==hash_character)) return (index);
index-=offset;
if (index<0) index+=TABLE_SIZE;
}
return (index);
}
long TLZWCompress::output_code(unsigned char *OutBuffer,long count,unsigned short code)
{ output_bit_buffer|=(unsigned long)code<<(32-num_bits-output_bit_count);
output_bit_count+=num_bits;
while (output_bit_count>=8)
{ OutBuffer[count]=output_bit_buffer>>24;
count++;
output_bit_buffer<<=8;
output_bit_count-=8;
bytes_out++;
}
return count;
}
long TLZWCompress::Compress(unsigned char *InBuffer,long len,unsigned char *OutBuffer)
{ unsigned short next_code=FIRST_CODE;
unsigned short character;
unsigned short string_code;
unsigned short index;
short i,ratio_new,ratio_old=100;
long OutCount=0L;
bytes_in=bytes_out=0;
checkpoint=CHECK_TIME;
output_bit_count=0;
output_bit_buffer=0L;
num_bits=INIT_BITS;
max_code=MAXVAL(num_bits);
ReadCount=0;
for (i=0;i<TABLE_SIZE;i++) code_value[i]=-1;
string_code=InBuffer[ReadCount];
ReadCount++;
while (ReadCount<len)
{ character=InBuffer[ReadCount];
ReadCount++;
++bytes_in;
index=find_match(string_code,character);
if (code_value[index] != -1) string_code=code_value[index];
else
{ if (next_code <= max_code )
{ code_value[index]=next_code++;
prefix_code[index]=string_code;
append_character[index]=character;
}
OutCount=output_code(OutBuffer,OutCount,string_code);
string_code=character;
if (next_code > max_code)
{ if ( num_bits < MAX_BITS) max_code = MAXVAL(++num_bits);
else
{ if (bytes_in > checkpoint)
{ if (num_bits == MAX_BITS )
{ ratio_new = bytes_out*100/bytes_in;
if (ratio_new > ratio_old)
{ OutCount=output_code(OutBuffer,OutCount,CLEAR_TABLE);
num_bits=INIT_BITS;
next_code=FIRST_CODE;
max_code = MAXVAL(num_bits);
bytes_in = bytes_out = 0;
ratio_old=100;
for (i=0;i<TABLE_SIZE;i++) code_value[i]=-1;
}
else ratio_old = ratio_new;
}
checkpoint = bytes_in + CHECK_TIME;
}
}
}
}
}
OutCount=output_code(OutBuffer,OutCount,string_code);
if (next_code == max_code) ++num_bits;
OutCount=output_code(OutBuffer,OutCount,TERMINATOR);
OutCount=output_code(OutBuffer,OutCount,0);
OutCount=output_code(OutBuffer,OutCount,0);
OutCount=output_code(OutBuffer,OutCount,0);
return OutCount;
}