Previous Table of Contents Next


The two new structure elements, rack and mask, manage the bit-oriented aspect of a most significant bit in the I/O byte gets or returns the first bit, and the least significant bit in the I/O byte gets or returns the last bit. This means that the mask element of the structure is initialized to 0x80 when the BIT_FILE is first opened. During output, the first write to the BIT_FILE will set or clear that bit, then the mask element will shift to the next. Once the mask has shifted to the point at which all the bits in the output rack have been set or cleared, the rack is written out to the file, and a new rack byte is started.

Performing input from a BIT_FILE is done in a similar fashion. The mask is first set to 0x80, and a single byte from the file is read into the rack element. Each call to read a bit from the file masks in a new bit, then shifts the mask over to the next lower significant bit. Eventually, all bits in the input rack have been returned, and the input routine can read in a new byte from the input file.

Two types of I/O routines are defined in BITIO.C. The first two routines read or write a single bit at a time. The second two read or write multiple bits, up to the size of an unsigned long. These four routines have the following ANSI prototype in BITIO.H:

void            OutputBit( BIT_FILE *bit_file, int bit );
void            OutputBits( BIT_FILE *bit_file,
                            unsigned long code, int count);
int             InputBit( BIT_FILE *bit_file );
unsigned long   InputBits( BIT_FILE *bit_file, int bit_count );

Specialized routines open a BIT_FILE, and two specialized routines close a BIT_FILE. The output routine makes sure that the last byte gets written out to the file. Both the input and output routines need to close their files, then free up the BIT_FILE structure allocated when the file was opened. The BIT_FILE routines used to close a file are defined in BITIO.H with these ANSI prototypes:

void    CloseInputBitFile( BIT_FILE *bit_file );
void    CloseOutputBitFile( BIT_FILE *bit_file );

The input and output routines in BITIO.H also have a pacifier feature that can be useful in testing compression code. Every BIT_FILE structure has a pacifier_counter that gets incremented every time a new byte is read in or written out to the corresponding file. Once every 2,048 bytes, a single character is written to stdout. This helps assure the impatient user that real work is being done. On MS-DOS systems, it also helps ensure that the user can break out of the program if it does not appear to be working correctly.

The header file and code for BITIO.H is shown next:.

/******************** Start of BITIO.H **********************/

#ifndef _BITIO_H
#define _BITIO_H
#include <stdio.h>

typedef struct bit_file {
     FILE *file;
     unsigned char mask;
     int rack;
     int pacifier_counter;
} BIT_FILE;

#ifdef __STDC__

BIT_FILE*     OpenInputBitFile( char *name );
BIT_FILE*     OpenOutputBitFile( char *name );
void          OutputBit( BIT_FILE *bit_file, int bit );
void          OutputBits( BIT_FILE *bit_file,
                          unsigned long code, int count );
int           InputBit( BIT_FILE *bit_file );
unsigned long InputBits( BIT_FILE *bit_file, int bit_count );
void          CloseInputBitFile( BIT_FILE *bit_file );
void          CloseOutputBitFile( BIT_FILE *bit_file );
void          FilePrintBinary( FILE *file, unsigned int code, int bits);

#else /* __STDC__ */

BIT_FILE*     OpenInputBitFile();
BIT_FILE*     OpenOutputBitFile();
void          OutputBit();
void          OutputBits();
int           InputBit();
unsigned long InputBits();
void          CloseInputBitFile();
void          CloseOutputBitFile();
void          FilePrintBinary();

#endif /* __STDC__ */

#endif /* _BITIO_H */

/********************** End of BITIO.H *********************/

/******************** Start of BITIO.C ********************/

/*
* This utility file contains all of the routines needed to implement
* bit oriented routines under either ANSI or K&R C.  It needs to be
* linked with every program used in the book.
*/
#include <stdio.h>
#include <stdlib.h>
#include "bitio.h"
#include "errhand.h"

BIT_FILE *OpenOutputBitFile( name )
char *name;
{
     BIT_FILE *bit_file;

     bit_file = (BIT_FILE *) calloc( 1, sizeof( BIT_FILE ) );
     if ( bit_file == NULL )
          return( bit_file );
     bit_file->file = fopen( name, "rb" );
     bit_file->rack = 0;
     bit_file->mask = 0x80;
     bit_file->pacifier_counter = 0;
     return( bit_file );
}

BIT_FILE *OpenInputBitFile( name )
char *name;
{
     BIT_FILE *bit_file;

     bit_file = (BIT_FILE *) calloc( 1, sizeof( BIT_FILE ) );
     if ( bit_file == NULL )
          return( bit_file );
     bit_file->file = fopen( name, "rb" );
     bit_file->rack = 0;
     bit_file->mask = 0x80;
     bit_file->pacifier_counter = 0;
     return( bit_file );
}

void CloseOutputBitFile( bit_file )
BIT_FILE *bit_file;
{

     if ( bit_file->mask != 0x80 )
      if ( putc( bit_file->rack, bit_file->file ) != bit_file->rack )
           fatal_error( "Fatal error in CloseBitFile!\n" );
     fclose( bit_file->file );
     free( (char *) bit_file );
}

void CloseInputBitFile( bit_file )
BIT_FILE *bit_file;
{
     fclose( bit_file->file );
     free( (char*) bit_file );
}
void OutputBit( bit_file, bit )
BIT_FILE *bit_file;
int bit;
{
   if ( bit )
        bit_file->rack | = bit_file->mask;
   bit_file->mask >>= 1;
   if ( bit_file->mask == 0 ) {
   if ( putc( bit_file->rack, bit_file->file ) != bit_file->rack )
          fatal_error( "Fatal error in OutputBit!\n" );
     else
          if ( ( bit_file->pacifier_counter++ & 4095 ) == 0 )
               putc( '.', stdout );
     bit_file->rack = 0;
     bit_file->mask = 0x80;
    }
}
void OutputBits( bit_file, code, count )
BIT_FILE *bit_file;
unsigned long code;
int count;
{
  unsigned long mask;

  mask = 1L << ( count - 1 );
  while ( mask != 0) {
       if ( mask & code )
            bit_file->rack | = bit_file->mask;
  bit_file->mask >>= 1;
  if ( bit_file->mask == 0 ) {
  if ( putc( bit_file->rack, bit_file->file ) != bit_file->rack )
        fatal_error( "Fatal error in OutputBit!\n" );
   else if ( ( bit_file->pacifier_counter++ & 2047 ) == 0 )
          putc( '.', stdout );
          bit_file->rack = 0;
          bit_file->mask = 0x80;
     }
     mask >>= 1;
   }
}

int InputBit( bit_file )
BIT_FILE *bit_file;
{
     int value;

     if ( bit_file->mask == 0x80 ) {
          bit_file->rack = getc( bit_file->file );
          if ( bit_file->rack == EOF )
               fatal_error( "Fatal error in InputBit!\n" );
          if ( ( bit_file->pacifier_counter++ & 2047 ) == 0 )
                 putc( '.', stdout );
     }
     value = bit_file->rack & bit_file->mask;
     bit_file->mask >>= 1;
     if ( bit_file->mask = 0 )
          bit_file->mask = 0x80;
     return ( value ? 1 : 0 );
     }

unsigned long InputBits( bit_file, bit_count )
BIT_FILE *bit_file;
int bit_count;
{

     unsigned long mask;
     unsigned long return_value;

     mask = 1L << ( bit_count - 1 );
     return_value = 0;
     while ( mask != 0) {

          if ( bit_file->mask == 0x80 ) {
               bit_file->rack = getc( bit_file->file );
               if ( bit_file->rack == EOF )
                    fatal_error( "Fatal error in InputBit!\n" );
          if ( ( bit_file->pacifier_counter++ & 2047 ) == 0 )
               putc( '.', stdout );
          }
          if ( bit_file->rack & bit_file->mask )
               return_value |=mask;
          mask >>= 1;
          bit_file->mask >>= 1;
          if ( bit_file->mask = 00 )
               bit_file->mask = 0x80;
     }
     return( return_value );
}
void FilePrintBinary( file, code, bits )
FILE *file;
unsigned int code;
int bits;
{
     unsigned int mask;
     mask = 1 << ( bits - 1 ):
     while ( mask != 0 ){
         if ( code & mask )
             fputc( '1', file );
         else
             fputc( '0', file);
         mask >>= 1;
     }
}

/********************** End of BITIO.C **********************/


Previous Table of Contents Next