/* * Listing 4 -- bitio.c * * This routine contains a set of bit oriented i/o routines * used for arithmetic data compression. The important fact to * know about these is that the first bit is stored in the msb of * the first byte of the output, like you might expect. * * Both input and output maintain a local buffer so that they only * have to do block reads and writes. This is done in spite of the * fact that C standard I/O does the same thing. If these * routines are ever ported to assembly language the buffering * will come in handy. * */ #include #include #include "coder.h" #include "bitio.h" #define BUFFER_SIZE 256 static char buffer[ BUFFER_SIZE + 2 ]; /* This is the i/o buffer */ static char *current_byte; /* Pointer to current byte */ static int output_mask; /* During output, this byte */ /* contains the mask that is */ /* applied to the output byte*/ /* if the output bit is a 1 */ static int input_bytes_left; /* During input, these three */ static int input_bits_left; /* variables keep track of my*/ static int past_eof; /* input state. The past_eof*/ /* byte comes about because */ /* of the fact that there is */ /* a possibility the decoder */ /* can legitimately ask for */ /* more bits even after the */ /* entire file has been */ /* sucked dry. */ /* * This routine is called once to initialze the output bitstream. * All it has to do is set up the current_byte pointer, clear out * all the bits in my current output byte, and set the output mask * so it will set the proper bit next time a bit is output. */ void initialize_output_bitstream() { current_byte = buffer; *current_byte = 0; output_mask = 0x80; } /* * The output bit routine just has to set a bit in the current byte * if requested to. After that, it updates the mask. If the mask * shows that the current byte is filled up, it is time to go to the * next character in the buffer. If the next character is past the * end of the buffer, it is time to flush the buffer. */ void output_bit( FILE *stream, int bit ) { if ( bit ) *current_byte |= output_mask; output_mask >>= 1; if ( output_mask == 0 ) { output_mask = 0x80; current_byte++; if ( current_byte == ( buffer + BUFFER_SIZE ) ) { fwrite( buffer, 1, BUFFER_SIZE, stream ); current_byte = buffer; } *current_byte = 0; } } /* * When the encoding is done, there will still be a lot of bits and * bytes sitting in the buffer waiting to be sent out. This routine * is called to clean things up at that point. */ void flush_output_bitstream( FILE *stream ) { fwrite( buffer, 1, (size_t)( current_byte - buffer ) + 1, stream ); current_byte = buffer; } /* * Bit oriented input is set up so that the next time the input_bit * routine is called, it will trigger the read of a new block. That * is why input_bits_left is set to 0. */ void initialize_input_bitstream() { input_bits_left = 0; input_bytes_left = 1; past_eof = 0; } /* * This routine reads bits in from a file. The bits are all sitting * in a buffer, and this code pulls them out, one at a time. When the * buffer has been emptied, that triggers a new file read, and the * pointers are reset. This routine is set up to allow for two dummy * bytes to be read in after the end of file is reached. This is because * we have to keep feeding bits into the pipeline to be decoded so that * the old stuff that is 16 bits upstream can be pushed out. */ short int input_bit( FILE *stream ) { if ( input_bits_left == 0 ) { current_byte++; input_bytes_left--; input_bits_left = 8; if ( input_bytes_left == 0 ) { input_bytes_left = fread( buffer, 1, BUFFER_SIZE, stream ); if ( input_bytes_left == 0 ) { if ( past_eof ) { fprintf( stderr, "Bad input file\n" ); exit( -1 ); } else { past_eof = 1; input_bytes_left = 2; } } current_byte = buffer; } } input_bits_left--; return ( ( *current_byte >> input_bits_left ) & 1 ); } /* * When monitoring compression ratios, we need to know how many * bytes have been output so far. This routine takes care of telling * how many bytes have been output, including pending bytes that * haven't actually been written out. */ long bit_ftell_output( FILE *stream ) { long total; total = ftell( stream ); total += current_byte - buffer; total += underflow_bits/8; return( total ); } /* * When monitoring compression ratios, we need to know how many bits * have been read in so far. This routine tells how many bytes have * been read in, excluding bytes that are pending in the buffer. */ long bit_ftell_input( FILE *stream ) { return( ftell( stream ) - input_bytes_left + 1 ); }