/*************************************************************************** * Bit Stream File Implementation * * File : bitfile.c * Purpose : This file implements a simple library of I/O functions for * files that contain data in sizes that aren't integral bytes. * An attempt was made to make the functions in this library * analogous to functions provided to manipulate byte streams. * The functions contained in this library were created with * compression algorithms in mind, but may be suited to other * applications. * Author : Michael Dipperstein * Date : January 9, 2004 * **************************************************************************** * UPDATES * * $Id: bitfile.c,v 1.10 2007/08/26 21:53:48 michael Exp $ * $Log: bitfile.c,v $ * Revision 1.10 2007/08/26 21:53:48 michael * Changes required for LGPL v3. * * Revision 1.9 2007/07/10 05:34:07 michael * Remove ',' after last element in the enum endian_t. * * Revision 1.8 2007/02/06 06:22:07 michael * Used trim program to remove trailing spaces. * * Revision 1.7 2006/06/03 19:32:38 michael * Corrected error reporetd anonymous. The allocation of constants used to * open underlying read/write/append files did not account for a terminating * null. * * Used spell checker to correct spelling. * * Revision 1.6 2005/12/08 06:56:55 michael * Minor text corrections. * * Revision 1.5 2005/12/06 15:06:37 michael * Added BitFileGetBitsInt and BitFilePutBitsInt for integer types. * * Revision 1.4 2005/06/23 04:34:18 michael * Prevent BitfileGetBits/PutBits from accessing an extra byte when given * an integral number of bytes. * * Revision 1.3 2004/11/09 14:16:58 michael * Added functions to convert open bit_file_t to FILE and to * align open bit_file_t to the next byte. * * Revision 1.2 2004/06/15 13:15:58 michael * Use incomplete type to hide definition of bitfile structure * * Revision 1.1.1.1 2004/02/09 05:31:42 michael * Initial release * * **************************************************************************** * * Bitfile: Bit stream File I/O Routines * Copyright (C) 2004-2007 by Michael Dipperstein (mdipper@cs.ucsb.edu) * * This file is part of the bit file library. * * The bit file library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * The bit file library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * ***************************************************************************/ /*************************************************************************** * INCLUDED FILES ***************************************************************************/ #include #include #include "bitfile.h" /*************************************************************************** * TYPE DEFINITIONS ***************************************************************************/ typedef enum { BF_UNKNOWN_ENDIAN, BF_LITTLE_ENDIAN, BF_BIG_ENDIAN } endian_t; struct bit_file_t { FILE *fp; /* file pointer used by stdio functions */ endian_t endian; /* endianess of architecture */ unsigned char bitBuffer; /* bits waiting to be read/written */ unsigned char bitCount; /* number of bits in bitBuffer */ BF_MODES mode; /* open for read, write, or append */ }; /* union used to test for endianess */ typedef union { unsigned long word; unsigned char bytes[sizeof(unsigned long)]; } endian_test_t; /*************************************************************************** * PROTOTYPES ***************************************************************************/ endian_t DetermineEndianess(void); int BitFilePutBitsLE(bit_file_t *stream, void *bits, const unsigned int count); int BitFilePutBitsBE(bit_file_t *stream, void *bits, const unsigned int count, const size_t size); int BitFileGetBitsLE(bit_file_t *stream, void *bits, const unsigned int count); int BitFileGetBitsBE(bit_file_t *stream, void *bits, const unsigned int count, const size_t size); /*************************************************************************** * FUNCTIONS ***************************************************************************/ /*************************************************************************** * Function : BitFileOpen * Description: This function opens a bit file for reading, writing, * or appending. If successful, a bit_file_t data * structure will be allocated and a pointer to the * structure will be returned. * Parameters : fileName - NULL terminated string containing the name of * the file to be opened. * mode - The mode of the file to be opened * Effects : The specified file will be opened and file structure will * be allocated. * Returned : Pointer to the bit_file_t structure for the bit file * opened, or NULL on failure. errno will be set for all * failure cases. ***************************************************************************/ bit_file_t *BitFileOpen(const char *fileName, const BF_MODES mode) { char modes[3][3] = {"rb", "wb", "ab"}; /* binary modes for fopen */ bit_file_t *bf; bf = (bit_file_t *)malloc(sizeof(bit_file_t)); if (bf == NULL) { /* malloc failed */ errno = ENOMEM; } else { bf->fp = fopen(fileName, modes[mode]); if (bf->fp == NULL) { /* fopen failed */ free(bf); return NULL; } else { /* fopen succeeded fill in remaining bf data */ bf->bitBuffer = 0; bf->bitCount = 0; bf->mode = mode; /*************************************************************** * TO DO: Consider using the last byte in a file to indicate * the number of bits in the previous byte that actually have * data. If I do that, I'll need special handling of files * opened with a mode of BF_APPEND. ***************************************************************/ } } bf->endian = DetermineEndianess(); return (bf); } /*************************************************************************** * Function : MakeBitFile * Description: This function naively wraps a standard file in a * bit_file_t structure. ANSI-C doesn't support file status * functions commonly found in other C variants, so the * caller must be passed as a parameter. * Parameters : stream - pointer to the standard file being wrapped. * mode - The mode of the file being wrapped. * Effects : A bit_file_t structure will be created for the stream * passed as a parameter. * Returned : Pointer to the bit_file_t structure for the bit file * or NULL on failure. errno will be set for all failure * cases. ***************************************************************************/ bit_file_t *MakeBitFile(FILE *stream, const BF_MODES mode) { bit_file_t *bf; if (stream == NULL) { /* can't wrapper empty steam */ errno = EBADF; bf = NULL; } else { bf = (bit_file_t *)malloc(sizeof(bit_file_t)); if (bf == NULL) { /* malloc failed */ errno = ENOMEM; } else { /* set structure data */ bf->fp = stream; bf->bitBuffer = 0; bf->bitCount = 0; bf->mode = mode; } } bf->endian = DetermineEndianess(); return (bf); } /*************************************************************************** * Function : DetermineEndianess * Description: This function determines the endianess of the current * hardware architecture. An unsigned long is set to 1. If * the 1st byte of the unsigned long gets the 1, this is a * little endian machine. If the last byte gets the 1, this * is a big endian machine. * Parameters : None * Effects : None * Returned : endian_t for current machine architecture ***************************************************************************/ endian_t DetermineEndianess(void) { endian_t endian; endian_test_t endianTest; endianTest.word = 1; if (endianTest.bytes[0] == 1) { /* LSB is 1st byte (little endian)*/ endian = BF_LITTLE_ENDIAN; } else if (endianTest.bytes[sizeof(unsigned long) - 1] == 1) { /* LSB is last byte (big endian)*/ endian = BF_BIG_ENDIAN; } else { endian = BF_UNKNOWN_ENDIAN; } return endian; } /*************************************************************************** * Function : BitFileClose * Description: This function closes a bit file and frees all associated * data. * Parameters : stream - pointer to bit file stream being closed * Effects : The specified file will be closed and the file structure * will be freed. * Returned : 0 for success or EOF for failure. ***************************************************************************/ int BitFileClose(bit_file_t *stream) { int returnValue = 0; if (stream == NULL) { return(EOF); } if ((stream->mode == BF_WRITE) || (stream->mode == BF_APPEND)) { /* write out any unwritten bits */ if (stream->bitCount != 0) { (stream->bitBuffer) <<= 8 - (stream->bitCount); fputc(stream->bitBuffer, stream->fp); /* handle error? */ } } /*********************************************************************** * TO DO: Consider writing an additional byte indicating the number of * valid bits (bitCount) in the previous byte. ***********************************************************************/ /* close file */ returnValue = fclose(stream->fp); /* free memory allocated for bit file */ free(stream); return(returnValue); } /*************************************************************************** * Function : BitFileToFILE * Description: This function flushes and frees the bitfile structure, * returning a pointer to a stdio file. * Parameters : stream - pointer to bit file stream being closed * Effects : The specified bitfile will be made usable as a stdio * FILE. * Returned : Pointer to FILE. NULL for failure. ***************************************************************************/ FILE *BitFileToFILE(bit_file_t *stream) { FILE *fp = NULL; if (stream == NULL) { return(NULL); } if ((stream->mode == BF_WRITE) || (stream->mode == BF_APPEND)) { /* write out any unwritten bits */ if (stream->bitCount != 0) { (stream->bitBuffer) <<= 8 - (stream->bitCount); fputc(stream->bitBuffer, stream->fp); /* handle error? */ } } /*********************************************************************** * TO DO: Consider writing an additional byte indicating the number of * valid bits (bitCount) in the previous byte. ***********************************************************************/ /* close file */ fp = stream->fp; /* free memory allocated for bit file */ free(stream); return(fp); } /*************************************************************************** * Function : BitFileByteAlign * Description: This function aligns the bitfile to the nearest byte. For * output files, this means writing out the bit buffer with * extra bits set to 0. For input files, this means flushing * the bit buffer. * Parameters : stream - pointer to bit file stream to align * Effects : Flushes out the bit buffer. * Returned : EOF if stream is NULL. Otherwise, the contents of the * bit buffer. ***************************************************************************/ int BitFileByteAlign(bit_file_t *stream) { int returnValue; if (stream == NULL) { return(EOF); } returnValue = stream->bitBuffer; if ((stream->mode == BF_WRITE) || (stream->mode == BF_APPEND)) { /* write out any unwritten bits */ if (stream->bitCount != 0) { (stream->bitBuffer) <<= 8 - (stream->bitCount); fputc(stream->bitBuffer, stream->fp); /* handle error? */ } } stream->bitBuffer = 0; stream->bitCount = 0; return (returnValue); } /*************************************************************************** * Function : BitFileGetChar * Description: This function returns the next byte from the file passed as * a parameter. * Parameters : stream - pointer to bit file stream to read from * Effects : Reads next byte from file and updates buffer accordingly. * Returned : EOF if a whole byte cannot be obtained. Otherwise, * the character read. ***************************************************************************/ int BitFileGetChar(bit_file_t *stream) { int returnValue; unsigned char tmp; if (stream == NULL) { return(EOF); } returnValue = fgetc(stream->fp); if (stream->bitCount == 0) { /* we can just get byte from file */ return returnValue; } /* we have some buffered bits to return too */ if (returnValue != EOF) { /* figure out what to return */ tmp = ((unsigned char)returnValue) >> (stream->bitCount); tmp |= ((stream->bitBuffer) << (8 - (stream->bitCount))); /* put remaining in buffer. count shouldn't change. */ stream->bitBuffer = returnValue; returnValue = tmp; } return returnValue; } /*************************************************************************** * Function : BitFilePutChar * Description: This function writes the byte passed as a parameter to the * file passed a parameter. * Parameters : c - the character to be written * stream - pointer to bit file stream to write to * Effects : Writes a byte to the file and updates buffer accordingly. * Returned : On success, the character written, otherwise EOF. ***************************************************************************/ int BitFilePutChar(const int c, bit_file_t *stream) { unsigned char tmp; if (stream == NULL) { return(EOF); } if (stream->bitCount == 0) { /* we can just put byte from file */ return fputc(c, stream->fp); } /* figure out what to write */ tmp = ((unsigned char)c) >> (stream->bitCount); tmp = tmp | ((stream->bitBuffer) << (8 - stream->bitCount)); if (fputc(tmp, stream->fp) != EOF) { /* put remaining in buffer. count shouldn't change. */ stream->bitBuffer = c; } else { return EOF; } return tmp; } /*************************************************************************** * Function : BitFileGetBit * Description: This function returns the next bit from the file passed as * a parameter. The bit value returned is the msb in the * bit buffer. * Parameters : stream - pointer to bit file stream to read from * Effects : Reads next bit from bit buffer. If the buffer is empty, * a new byte will be read from the file. * Returned : 0 if bit == 0, 1 if bit == 1, and EOF if operation fails. ***************************************************************************/ int BitFileGetBit(bit_file_t *stream) { int returnValue; if (stream == NULL) { return(EOF); } if (stream->bitCount == 0) { /* buffer is empty, read another character */ if ((returnValue = fgetc(stream->fp)) == EOF) { return EOF; } else { stream->bitCount = 8; stream->bitBuffer = returnValue; } } /* bit to return is msb in buffer */ stream->bitCount--; returnValue = (stream->bitBuffer) >> (stream->bitCount); return (returnValue & 0x01); } /*************************************************************************** * Function : BitFilePutBit * Description: This function writes the bit passed as a parameter to the * file passed a parameter. * Parameters : c - the bit value to be written * stream - pointer to bit file stream to write to * Effects : Writes a bit to the bit buffer. If the buffer has a byte, * the buffer is written to the file and cleared. * Returned : On success, the bit value written, otherwise EOF. ***************************************************************************/ int BitFilePutBit(const int c, bit_file_t *stream) { int returnValue = c; if (stream == NULL) { return(EOF); } stream->bitCount++; stream->bitBuffer <<= 1; if (c != 0) { stream->bitBuffer |= 1; } /* write bit buffer if we have 8 bits */ if (stream->bitCount == 8) { if (fputc(stream->bitBuffer, stream->fp) == EOF) { returnValue = EOF; } /* reset buffer */ stream->bitCount = 0; stream->bitBuffer = 0; } return returnValue; } /*************************************************************************** * Function : BitFileGetBits * Description: This function reads the specified number of bits from the * file passed as a parameter and writes them to the * requested memory location (msb to lsb). * Parameters : stream - pointer to bit file stream to read from * bits - address to store bits read * count - number of bits to read * Effects : Reads bits from the bit buffer and file stream. The bit * buffer will be modified as necessary. * Returned : EOF for failure, otherwise the number of bits read. If * an EOF is reached before all the bits are read, bits * will contain every bit through the last complete byte. ***************************************************************************/ int BitFileGetBits(bit_file_t *stream, void *bits, const unsigned int count) { unsigned char *bytes, shifts; int offset, remaining, returnValue; bytes = (unsigned char *)bits; if ((stream == NULL) || (bits == NULL)) { return(EOF); } offset = 0; remaining = count; /* read whole bytes */ while (remaining >= 8) { returnValue = BitFileGetChar(stream); if (returnValue == EOF) { return EOF; } bytes[offset] = (unsigned char)returnValue; remaining -= 8; offset++; } if (remaining != 0) { /* read remaining bits */ shifts = 8 - remaining; while (remaining > 0) { returnValue = BitFileGetBit(stream); if (returnValue == EOF) { return EOF; } bytes[offset] <<= 1; bytes[offset] |= (returnValue & 0x01); remaining--; } /* shift last bits into position */ bytes[offset] <<= shifts; } return count; } /*************************************************************************** * Function : BitFilePutBits * Description: This function writes the specified number of bits from the * memory location passed as a parameter to the file passed * as a parameter. Bits are written msb to lsb. * Parameters : stream - pointer to bit file stream to write to * bits - pointer to bits to write * count - number of bits to write * Effects : Writes bits to the bit buffer and file stream. The bit * buffer will be modified as necessary. * Returned : EOF for failure, otherwise the number of bits written. If * an error occurs after a partial write, the partially * written bits will not be unwritten. ***************************************************************************/ int BitFilePutBits(bit_file_t *stream, void *bits, const unsigned int count) { unsigned char *bytes, tmp; int offset, remaining, returnValue; bytes = (unsigned char *)bits; if ((stream == NULL) || (bits == NULL)) { return(EOF); } offset = 0; remaining = count; /* write whole bytes */ while (remaining >= 8) { returnValue = BitFilePutChar(bytes[offset], stream); if (returnValue == EOF) { return EOF; } remaining -= 8; offset++; } if (remaining != 0) { /* write remaining bits */ tmp = bytes[offset]; while (remaining > 0) { returnValue = BitFilePutBit((tmp & 0x80), stream); if (returnValue == EOF) { return EOF; } tmp <<= 1; remaining--; } } return count; } /*************************************************************************** * Function : BitFileGetBitsInt * Description: This function provides a machine independent layer that * allows a single function call to stuff an arbitrary number * of bits into an integer type variable. * Parameters : stream - pointer to bit file stream to read from * bits - address to store bits read * count - number of bits to read * size - sizeof type containing "bits" * Effects : Calls a function that reads bits from the bit buffer and * file stream. The bit buffer will be modified as necessary. * the bits will be written to "bits" from least significant * byte to most significant byte. * Returned : EOF for failure, otherwise the number of bits read by the * called function. ***************************************************************************/ int BitFileGetBitsInt(bit_file_t *stream, void *bits, const unsigned int count, const size_t size) { int returnValue; if ((stream == NULL) || (bits == NULL)) { return(EOF); } if (stream->endian == BF_LITTLE_ENDIAN) { returnValue = BitFileGetBitsLE(stream, bits, count); } else if (stream->endian == BF_BIG_ENDIAN) { returnValue = BitFileGetBitsBE(stream, bits, count, size); } else { returnValue = EOF; } return returnValue; } /*************************************************************************** * Function : BitFileGetBitsLE (Little Endian) * Description: This function reads the specified number of bits from the * file passed as a parameter and writes them to the * requested memory location (LSB to MSB). * Parameters : stream - pointer to bit file stream to read from * bits - address to store bits read * count - number of bits to read * Effects : Reads bits from the bit buffer and file stream. The bit * buffer will be modified as necessary. bits is treated as * a little endian integer of length >= (count/8) + 1. * Returned : EOF for failure, otherwise the number of bits read. If * an EOF is reached before all the bits are read, bits * will contain every bit through the last successful read. ***************************************************************************/ int BitFileGetBitsLE(bit_file_t *stream, void *bits, const unsigned int count) { unsigned char *bytes, shifts; int offset, remaining, returnValue; bytes = (unsigned char *)bits; offset = 0; remaining = count; /* read whole bytes */ while (remaining >= 8) { returnValue = BitFileGetChar(stream); if (returnValue == EOF) { return EOF; } bytes[offset] = (unsigned char)returnValue; remaining -= 8; offset++; } if (remaining != 0) { /* read remaining bits */ shifts = 8 - remaining; while (remaining > 0) { returnValue = BitFileGetBit(stream); if (returnValue == EOF) { return EOF; } bytes[offset] <<= 1; bytes[offset] |= (returnValue & 0x01); remaining--; } } return count; } /*************************************************************************** * Function : BitFileGetBitsBE (Big Endian) * Description: This function reads the specified number of bits from the * file passed as a parameter and writes them to the * requested memory location (LSB to MSB). * Parameters : stream - pointer to bit file stream to read from * bits - address to store bits read * count - number of bits to read * size - sizeof type containing "bits" * Effects : Reads bits from the bit buffer and file stream. The bit * buffer will be modified as necessary. bits is treated as * a big endian integer of length size. * Returned : EOF for failure, otherwise the number of bits read. If * an EOF is reached before all the bits are read, bits * will contain every bit through the last successful read. ***************************************************************************/ int BitFileGetBitsBE(bit_file_t *stream, void *bits, const unsigned int count, const size_t size) { unsigned char *bytes, shifts; int offset, remaining, returnValue; if (count > (size * 8)) { /* too many bits to read */ return EOF; } bytes = (unsigned char *)bits; offset = size - 1; remaining = count; /* read whole bytes */ while (remaining >= 8) { returnValue = BitFileGetChar(stream); if (returnValue == EOF) { return EOF; } bytes[offset] = (unsigned char)returnValue; remaining -= 8; offset--; } if (remaining != 0) { /* read remaining bits */ shifts = 8 - remaining; while (remaining > 0) { returnValue = BitFileGetBit(stream); if (returnValue == EOF) { return EOF; } bytes[offset] <<= 1; bytes[offset] |= (returnValue & 0x01); remaining--; } } return count; } /*************************************************************************** * Function : BitFilePutBitsInt * Description: This function provides a machine independent layer that * allows a single function call to write an arbitrary number * of bits from an integer type variable into a file. * Parameters : stream - pointer to bit file stream to write to * bits - pointer to bits to write * count - number of bits to write * size - sizeof type containing "bits" * Effects : Calls a function that writes bits to the bit buffer and * file stream. The bit buffer will be modified as necessary. * the bits will be written to the file stream from least * significant byte to most significant byte. * Returned : EOF for failure, otherwise the number of bits written. If * an error occurs after a partial write, the partially * written bits will not be unwritten. ***************************************************************************/ int BitFilePutBitsInt(bit_file_t *stream, void *bits, const unsigned int count, const size_t size) { int returnValue; if ((stream == NULL) || (bits == NULL)) { return(EOF); } if (stream->endian == BF_LITTLE_ENDIAN) { returnValue = BitFilePutBitsLE(stream, bits, count); } else if (stream->endian == BF_BIG_ENDIAN) { returnValue = BitFilePutBitsBE(stream, bits, count, size); } else { returnValue = EOF; } return returnValue; } /*************************************************************************** * Function : BitFilePutBitsLE (Little Endian) * Description: This function writes the specified number of bits from the * memory location passed as a parameter to the file passed * as a parameter. Bits are written LSB to MSB. * Parameters : stream - pointer to bit file stream to write to * bits - pointer to bits to write * count - number of bits to write * Effects : Writes bits to the bit buffer and file stream. The bit * buffer will be modified as necessary. bits is treated as * a little endian integer of length >= (count/8) + 1. * Returned : EOF for failure, otherwise the number of bits written. If * an error occurs after a partial write, the partially * written bits will not be unwritten. ***************************************************************************/ int BitFilePutBitsLE(bit_file_t *stream, void *bits, const unsigned int count) { unsigned char *bytes, tmp; int offset, remaining, returnValue; bytes = (unsigned char *)bits; offset = 0; remaining = count; /* write whole bytes */ while (remaining >= 8) { returnValue = BitFilePutChar(bytes[offset], stream); if (returnValue == EOF) { return EOF; } remaining -= 8; offset++; } if (remaining != 0) { /* write remaining bits */ tmp = bytes[offset]; tmp <<= (8 - remaining); while (remaining > 0) { returnValue = BitFilePutBit((tmp & 0x80), stream); if (returnValue == EOF) { return EOF; } tmp <<= 1; remaining--; } } return count; } /*************************************************************************** * Function : BitFilePutBitsBE (Big Endian) * Description: This function writes the specified number of bits from the * memory location passed as a parameter to the file passed * as a parameter. Bits are written LSB to MSB. * Parameters : stream - pointer to bit file stream to write to * bits - pointer to bits to write * count - number of bits to write * Effects : Writes bits to the bit buffer and file stream. The bit * buffer will be modified as necessary. bits is treated as * a big endian integer of length size. * Returned : EOF for failure, otherwise the number of bits written. If * an error occurs after a partial write, the partially * written bits will not be unwritten. ***************************************************************************/ int BitFilePutBitsBE(bit_file_t *stream, void *bits, const unsigned int count, const size_t size) { unsigned char *bytes, tmp; int offset, remaining, returnValue; if (count > (size * 8)) { /* too many bits to write */ return EOF; } bytes = (unsigned char *)bits; offset = size - 1; remaining = count; /* write whole bytes */ while (remaining >= 8) { returnValue = BitFilePutChar(bytes[offset], stream); if (returnValue == EOF) { return EOF; } remaining -= 8; offset--; } if (remaining != 0) { /* write remaining bits */ tmp = bytes[offset]; tmp <<= (8 - remaining); while (remaining > 0) { returnValue = BitFilePutBit((tmp & 0x80), stream); if (returnValue == EOF) { return EOF; } tmp <<= 1; remaining--; } } return count; }