/******************************************************************** ** ** Copyright (c) 1989 Mark R. Nelson ** ** LZW data compression/expansion demonstration program. ** ** April 13, 1989 ** *****************************************************************************/ /* Modified to C++ by Mark Fenner Sept. 20, 1999 */ /* MF modified */ #include #include #include #define BITS 12 /* Setting the number of bits to 12, 13*/ #define HASHING_SHIFT BITS-8 /* or 14 affects several constants. */ #define MAX_VALUE (1 << BITS) - 1 /* Note that MS-DOS machines need to */ #define MAX_CODE MAX_VALUE - 1 /* compile their code in large model if*/ /* 14 bits are selected. */ #if BITS == 14 #define TABLE_SIZE 18041 /* The string table size needs to be a */ #endif /* prime number that is somewhat larger*/ #if BITS == 13 /* than 2**BITS. */ #define TABLE_SIZE 9029 #endif #if BITS <= 12 #define TABLE_SIZE 5021 #endif /* MF modified */ int *code_value; /* This is the code value array */ unsigned int *prefix_code; /* This array holds the prefix codes */ unsigned char *append_character; /* This array holds the appended chars */ unsigned char decode_stack[4000]; /* This array holds the decoded string */ void compress(ifstream &,ofstream &); int find_match(int, unsigned int); void expand(istream &, ostream &); unsigned char *decode_string(unsigned char *, unsigned int); int input_code(istream &); void output_code(ostream &,unsigned int); /******************************************************************** ** ** This program gets a file name from the command line. It compresses the ** file, placing its output in a file named test.lzw. It then expands ** test.lzw into test.out. Test.out should then be an exact duplicate of ** the input file. ** *************************************************************************/ int main(int argc, char *argv[]) { ifstream input_file; ifstream lzw_file_i; ofstream output_file; ofstream lzw_file_o; char input_file_name[81]; /* ** The three buffers are needed for the compression phase. */ code_value=new int[TABLE_SIZE*sizeof(unsigned int)]; prefix_code=new unsigned int[TABLE_SIZE*sizeof(unsigned int)]; append_character=new unsigned char[TABLE_SIZE*sizeof(unsigned char)]; if (code_value==NULL || prefix_code==NULL || append_character==NULL) { cout << "Fatal error allocating table space!\n"; exit(0); } /* ** Get the file name, open it up, and open up the lzw output file. */ if (argc>1) { strcpy(input_file_name, argv[1]); } else { cout << "Input file name: "; cin >> input_file_name; } input_file.open(input_file_name, ios::binary); /*input_file=fopen(input_file_name,"rb");*/ lzw_file_o.open("test.lzw", ios::binary); /*lzw_file=fopen("test.lzw","wb");*/ if (!input_file || !lzw_file_o) { cout << "Fatal error opening files.\n"; exit(0); }; /* ** Compress the file. */ compress(input_file,lzw_file_o); input_file.close(); /*fclose(input_file);*/ lzw_file_o.close(); /*fclose(lzw_file);*/ delete code_value; /* ** Now open the files for the expansion. */ lzw_file_i.open("test.lzw", ios::binary); /*lzw_file=fopen("test.lzw","rb");*/ output_file.open("test.out", ios::binary); /*output_file=fopen("test.out","wb");*/ if (!lzw_file_i || !output_file) { cout << "Fatal error opening files.\n"; exit(0); }; /* ** Expand the file. */ expand(lzw_file_i, output_file); lzw_file_i.close(); output_file.close(); delete prefix_code; delete append_character; } /* ** This is the compression routine. The code should be a fairly close ** match to the algorithm accompanying the article. ** */ void compress(ifstream &input,ofstream &output) { unsigned int next_code; unsigned int character; unsigned int string_code; unsigned int index; int i; ofstream output_code_file; output_code_file.open("outputcodes", ios::out); next_code=256; /* Next code is the next available string code*/ for (i=0;i=next_code) { *decode_stack=character; string=decode_string(decode_stack+1,old_code); } /* ** Otherwise we do a straight decode of the new code. */ else string=decode_string(decode_stack,new_code); /* ** Now we output the decoded string in reverse order. */ character=*string; while (string >= decode_stack) { /* cout << "String: " << *string << "\n"; */ output.put(*string--); } /* ** Finally, if possible, add a new code to the string table. */ if (next_code <= MAX_CODE) { prefix_code[next_code]=old_code; append_character[next_code]=character; next_code++; } old_code=new_code; } cout << "\n"; } /* ** This routine simply decodes a string from the string table, storing ** it in a buffer. The buffer can then be output in reverse order by ** the expansion program. */ unsigned char *decode_string(unsigned char *buffer,unsigned int code) { int i; i=0; while (code > 255) { *buffer++ = append_character[code]; code=prefix_code[code]; if (i++>=4094) { cout << "Fatal error during code expansion.\n"; exit(0); } } *buffer=code; return(buffer); } /* ** The following two routines are used to output variable length ** codes. They are written strictly for clarity, and are not ** particularyl efficient. */ int input_code(istream &input) { unsigned int return_value; static int input_bit_count=0; static unsigned long input_bit_buffer=0L; while (input_bit_count <= 24) { input_bit_buffer |= (unsigned long) input.get() << (24-input_bit_count); input_bit_count += 8; } return_value=input_bit_buffer >> (32-BITS); input_bit_buffer <<= BITS; input_bit_count -= BITS; return(return_value); } void output_code(ostream &output,unsigned int code) { static int output_bit_count=0; static unsigned long output_bit_buffer=0L; output_bit_buffer |= (unsigned long) code << (32-BITS-output_bit_count); output_bit_count += BITS; while (output_bit_count >= 8) { /* cout << "Output bit buffer: " << output_bit_buffer << "\n"; cout << "Output bit buffer >> 24: " << (output_bit_buffer >> 24) << "\n"; cout << "Output bit buffer >> 24 (char): " << (output_bit_buffer >> 24) << "\n"; */ output.put((char) (output_bit_buffer >> 24)); output_bit_buffer <<= 8; output_bit_count -= 8; } }