Table of Contents

NAME

QccBitBuffer - generic data structure for bitstream packing

SYNOPSIS

#include "libQccPack.h"

int QccBitBufferInitialize(QccBitBuffer *bit_buffer);
int QccBitBufferStart(QccBitBuffer *bit_buffer);
int QccBitBufferEnd(QccBitBuffer *bit_buffer);
int QccBitBufferFlush(QccBitBuffer *bit_buffer);
int QccBitBufferCopy(QccBitBuffer *output_buffer, QccBitBuffer *input_buffer, int num_bits);
int QccBitBufferPutBit(QccBitBuffer *bit_buffer, int bit_value);
int QccBitBufferGetBit(QccBitBuffer *bit_buffer, int *bit_value);
int QccBitBufferPutBits(QccBitBuffer *bit_buffer, int val, int num_bits);
int QccBitBufferGetBits(QccBitBuffer *bit_buffer, int *val, int num_bits);
int QccBitBufferPutChar(QccBitBuffer *bit_buffer, unsigned char val);
int QccBitBufferGetChar(QccBitBuffer *bit_buffer, unsigned char *val);
int QccBitBufferPutInt(QccBitBuffer *bit_buffer, int val);
int QccBitBufferGetInt(QccBitBuffer *bit_buffer, int *val);
int QccBitBufferPutDouble(QccBitBuffer *bit_buffer, double val);
int QccBitBufferGetDouble(QccBitBuffer *bit_buffer, double *val);

DESCRIPTION

When one writes a stream of bits to a file, it is necessary for the bits to be "packed" into byte-sized characters, as the byte is the smallest input/output unit supported by the operating system. Likewise, bit "unpacking" from byte-sized characters is necessary to read a bitstream from a file. QccPack provides the QccBitBuffer data structure to facilitate reading and writing bitstreams. For writing, the QccBitBuffer structure and related routines automatically handle the packing of consecutive bits into a character and the writing of the resulting character, when full, to the file. For reading, bits are unpacked from the current character; when no more bits remain to be read from the character, the next character from the file is read automatically. Consequently, from the user's point of view, the QccBitBuffer structure and related routines provided bit-level access to files. Additional routines allow int and double types to be read/written from/to the bitstream without regard to the byte alignment usually required by the operating system.

DATA STRUCTURE

The QccBitBuffer data structure is defined as:

typedef unsigned char QccBitBufferChar;
typedef struct
{
QccString filename;
FILE *fileptr;
int type;
int bit_cnt;
int byte_cnt;
int bits_to_go;
QccBitBufferChar buffer;
} QccBitBuffer;

The fields of QccBitBuffer are as follows:

filename
The name of the file to which the bitstream is to be read/written.
fileptr
The FILE pointer to the file.
type
Indicates whether the buffer is output (bitstream is written to file) or input (bitstream is read from file). Takes values QCCBITBUFFER_OUTPUT or QCCBITBUFFER_INPUT, respectively.
bit_cnt
Number of bits read from or written to buffer so far.
byte_cnt
Number of bytes read from or written to the file associated to the buffer.
bits_to_go
Number of bits remaining to pack/unpack into/from current character. For internal use in QccPack routines only.
buffer
The current character being packed/unpacked.

FILE FORMAT

The file written or read by QccBitBuffer routines is merely a sequence of the packed characters representing the bitstream. The order that the bits of the bitstream are packed into the characters is least-significant bit (LSB) to most-significant bit (MSB). There is no header information stored in the file.

ROUTINES

QccBitBufferInitialize() should be called before any use of a QccBitBuffer structure. QccBitBufferInitialize() initializes the fields of bit_buffer to the following values:

filename: NULL string
fileptr: NULL
type: QCCBITBUFFER_OUTPUT
bit_cnt: 0
bits_to_go: 8
buffer: 0

QccBitBufferStart() starts a read or write on bit_buffer. bit_buffer->type must be set to indicate whether bit_buffer will read a file (QCCBITBUFFER_INPUT) or write a file (QCCBITBUFFER_OUTPUT). If bit_buffer->fileptr is NULL, the file indicated by bit_buffer->filename is opened and the resulting FILE pointer is returned in bit_buffer->fileptr. Otherwise, it is assumed that the file is already opened properly for reading or writing as appropriate, and is properly positioned to the start of binary data in the file (thus, QccBitBufferStart() permits the reading of, say, an ASCII header by some external procedure before the reading of binary data commences). In either case, if bit_buffer is being started for reading, bit_buffer->buffer is primed by reading the first character of the file.

QccBitBufferEnd() should be called after all accesses (read or write) to bit_buffer are complete. QccBitBufferEnd() closes bit_buffer->fileptr.

QccBitBufferFlush() should be called at the end of writing a bitstream after all bits have been written but before QccBitBufferEnd() is called. QccBitBufferFlush() ensures that the last byte being packed with bits, which may not be full, is output to the file. If any bits are output after a call to QccBitBufferFlush(), they will be written to the next byte of the file; i.e., QccBitBufferFlush() can be used to align output to byte boundaries in the file. QccBitBufferFlush() is usually called only for buffers opened for output. In the case of an input buffer, QccBitBufferFlush() will cause the next bit read to come from the next byte of the file; i.e., on input, QccBitBufferFlush() can be used to enforce alignment to byte boundaries during reading the file. On output, QccBitBufferFlush() calls QccFileFlush(3) to ensure that all stream buffers are flushed.

QccBitBufferCopy() copies num_bits from input_buffer to output_buffer by calling QccBitBufferGetBit() on input_buffer and QccBitBufferPutBit() on output_buffer num_bits times.

QccBitBufferPutBit() outputs a bit, bit_value, to output buffer bit_buffer. The bit is packed into the current byte, and, if this byte is full, it is written to the file. bit_value gives the value of the bit; bit_value should be zero to denote a 0 bit and nonzero to denote a 1 bit.

QccBitBufferGetBit() inputs a bit, bit_value, from an input buffer bit_buffer. If the current byte is empty, the next byte is read from the file. Then, a bit is unpacked from the byte. The bit is returned in bit_value; a 0 is returned for a 0 bit, a 1 is returned for a 1 bit. If the bit_value pointer is NULL, then the bit is read from input_buffer but not returned.

QccBitBufferPutBits() outputs the least-significant num_bits bits of val to bit_buffer via num_bits consecutive calls to QccBitBufferPutBit(). The bits are output starting with the least-significant bit of val.

QccBitBufferGetBits() inputs num_bits bits from bit_buffer to the least-significant bits of val via num_bits consecutive calls to QccBitBufferGetBit(). The bits are placed into val starting with the least-significant bit first. If the val pointer is NULL, then the bits are read from input_buffer but not returned.

QccBitBufferPutChar() and QccBitBufferGetChar() write or read, respectively, an unsigned char to or from the file. That is, these two routines effectuate 8 consecutive bit writes or reads via QccBitBufferPutBit() or QccBitBufferGetBit(). If the val pointer is NULL, then QccBitBufferGetBit() reads the character from input_buffer, but it is not returned.

QccBitBufferPutInt() and QccBitBufferGetInt() use QccBinaryIntToChar(3) and QccBinaryChartoInt(3) to convert between an int and four characters. These four characters are read from or written to the file via four calls to QccBitBufferPutChar() or QccBitBufferGetChar(). If the val pointer is NULL, then QccBitBufferGetInt() reads the integer from input_buffer, but it is not returned.

QccBitBufferPutDouble() and QccBitBufferGetDouble() use QccBinaryFloatToChar(3) and QccBinaryChartoFloat(3) to convert between a double and four characters. These four characters are read from or written to the file via four calls to QccBitBufferPutChar() or QccBitBufferGetChar(). QccBitBufferPutDouble() and QccBitBufferGetDouble() actually read and write double as float; consequently, these routines may incur a loss of precision. If the val pointer is NULL, then QccBitBufferGetDouble() reads the value from input_buffer, but it is not returned.

RETURN VALUE

Each of these routines return 0 upon successful completion, 1 on error.

SEE ALSO

QccBinaryIntToChar(3) , QccBinaryChartoInt(3) , QccBinaryFloatToChar(3) , QccBinaryChartoFloat(3) , QccPack(3)

NOTES

Because bitstreams are stored in raw format files with no headers, the routines that read these files (e.g., QccBitBufferGetBit()) do not know a priori how long the bitstream is. Since a bitstream may end in the middle of a byte, the end of the file may not correspond to the end of the bitstream. It is the user's responsibility to ensure that data is not read from beyond the end of the bitstream.

On a related note, bit_buffer->bit_cnt stores the number of bits actually put into or got out of the buffer. On the other hand, bit_buffer->byte_cnt stores the number of bytes that have been read from or written to the file associated to the buffer. Calls to QccBitBufferFlush() may cause these two to appear to be "out of sync." For example, when the last byte of a file is written, if this last byte is not completely full, bit_buffer->bit_cnt is incremented by only the number of bits actually put into the buffer (something less than 8), while bit_buffer->byte_cnt is incremented once indicating that a byte (8 bits) was written to the file. In this case, bit_buffer->bit_cnt will not be equal to 8 times bit_buffer->byte_cnt as might have been expected. This issue is important to keep in mind should QccBitBufferFlush() be called multiple times during reading or writing in order to align to byte boundaries; in this case, the bits in the file that are "skipped" to get to the start of the next byte are not included in the bit_buffer->bit_cnt count.

AUTHOR

Copyright (C) 1997-2021 James E. Fowler


Table of Contents



Get QccPack at SourceForge.net. Fast, secure and Free Open Source software downloads