/**********************************************************************
MPEG-4 Audio VM
Bit stream module



This software module was originally developed by

Heiko Purnhagen (University of Hannover / ACTS-MoMuSys)

and edited by

in the course of development of the MPEG-2 NBC/MPEG-4 Audio standard
ISO/IEC 13818-7, 14496-1,2 and 3. This software module is an
implementation of a part of one or more MPEG-2 NBC/MPEG-4 Audio tools
as specified by the MPEG-2 NBC/MPEG-4 Audio standard. ISO/IEC gives
users of the MPEG-2 NBC/MPEG-4 Audio standards free license to this
software module or modifications thereof for use in hardware or
software products claiming conformance to the MPEG-2 NBC/ MPEG-4 Audio
standards. Those intending to use this software module in hardware or
software products are advised that this use may infringe existing
patents. The original developer of this software module and his/her
company, the subsequent editors and their companies, and ISO/IEC have
no liability for use of this software module or modifications thereof
in an implementation. Copyright is not released for non MPEG-2
NBC/MPEG-4 Audio conforming products. The original developer retains
full right to use the code for his/her own purpose, assign or donate
the code to a third party and to inhibit third party from using the
code for non MPEG-2 NBC/MPEG-4 Audio conforming products. This
copyright notice must be included in all copies or derivative works.

Copyright (c) 1996.



Source file: bitstream.c

$Id: bitstream.c,v 1.15 1998/05/05 20:39:06 purnhage Exp $

Required modules:
common.o		common module

Authors:
HP    Heiko Purnhagen, Uni Hannover <purnhage@tnt.uni-hannover.de>
BT    Bodo Teichmann, FhG/IIS <tmn@iis.fhg.de>

Changes:
06-jun-96   HP    added buffers, ASCII-header and BsGetBufferAhead()
07-jun-96   HP    use CommonWarning(), CommonExit()
11-jun-96   HP    ...
13-jun-96   HP    changed BsGetBit(), BsPutBit(), BsGetBuffer(),
                  BsGetBufferAhead(), BsPutBuffer()
14-jun-96   HP    fixed bug in BsOpenFileRead(), read header
18-jun-96   HP    fixed bug in BsGetBuffer()
26-aug-96   HP    CVS
23-oct-96   HP    free for BsOpenFileRead() info string
07-nov-96   HP    fixed BsBitStream info bug
15-nov-96   HP    changed int to long where required
                  added BsGetBitChar(), BsGetBitShort(), BsGetBitInt()
                  improved file header handling
04-dec-96   HP    fixed bug in BsGetBitXxx() for numBit==0		  
23-dec-96   HP    renamed mode to write in BsBitStream
10-jan-97   HP    added BsGetBitAhead(), BsGetSkip()
17-jan-97   HP    fixed read file buffer bug
30-jan-97   HP    minor bug fix in read bitstream file magic string
19-feb-97   HP    made internal data structures invisible
04-apr-97   BT/HP added BsGetBufferAppend()
07-max-97   BT    added BsGetBitBack()
14-mrz-98   sfr   added CreateEPInfo(), ReadFrameData(), BsGetBitEP(),
                        BsGetBitEP()
**********************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "bitstream.h"		/* bit stream module */
#include "common.h"		/* common module */


/* ---------- declarations ---------- */

#define MIN_FILE_BUF_SIZE 1024	/* min num bytes in file buffer */
#define HEADER_BUF_SIZE 2048	/* max size of ASCII header */

#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))

#define BYTE_NUMBIT 8		/* bits in byte (char) */
#define LONG_NUMBIT 32		/* bits in unsigned long */
#define bit2byte(a) (((a)+BYTE_NUMBIT-1)/BYTE_NUMBIT)
#define byte2bit(a) ((a)*BYTE_NUMBIT)


/* ---------- declarations (structures) ---------- */

#ifdef CLEANBITSTREAM

/* see bitstream.h for details   HP 970219 */

struct BsBitBufferStruct	/* bit buffer */
{
  unsigned char *data;		/* data bits */
  long numBit;			/* number of bits in buffer */
  long size;			/* buffer size in bits */
};

struct BsBitStreamStruct	/* bit stream */
{
  FILE *file;			/* file or NULL for buffer i/o */
  int write;			/* 0=read  1=write */
  long streamId;		/* stream id (for debug) */
  char *info;			/* info string (for read file) */
  BsBitBuffer *buffer[2];	/* bit buffers */
				/* (buffer[1] is only used for read file) */
  long currentBit;		/* current bit position in bit stream */
				/* (i.e. number of bits read/written) */
  long numByte;			/* number of bytes read/written (only file) */
};

#endif

#ifdef VERSION2

typedef struct preDefDataStruct {
  unsigned short rateEscape;
  unsigned short crcLenEscape;
} preDefData;

typedef struct tagEPInfo {
  FILE           *infoFilePtr;             /* file pointer to EP decoder info file */
  unsigned short numClass;                 /* number of classes used for error protection */
  unsigned short bitsInFrame;              /* number of bits in an audio frame     */
  unsigned short bitsInClass[MAX_CLASSES]; /* number of bits in a class of a frame */
  unsigned short curBitPos[MAX_CLASSES];   /* "pointer" of bit-position in a class */
  unsigned short priority[MAX_ELEMENTS];   /* array with the ep classes of the bitsream elements */
  preDefData     classNr[MAX_CLASSES];     /* additional info from the pre def. file */
} TAG_EP_INFO;

#endif /* VERSION2 */

/* ---------- variables ---------- */

static int BSdebugLevel = 0;			/* debug level */
static long BSbufSizeByte = MIN_FILE_BUF_SIZE;	/* num bytes file buf */
static long BSstreamId = 0;			/* stream id counter */


/* ---------- internal functions ---------- */


/* BsReadFile() */
/* Read one buffer from file. */

static int BsReadFile (
  BsBitStream *stream)		/* in: stream */
				/* returns: 0=OK  1=error */
{
  long numByte;
  long numByteRead;
  long curBuf;

  if (BSdebugLevel >= 3)
    printf("BsReadFile: id=%ld  streamNumByte=%ld  curBit=%ld\n",
	   stream->streamId,stream->numByte,stream->currentBit);

  if (feof(stream->file))
    return 0;

  numByte = bit2byte(stream->buffer[0]->size);
  if (stream->numByte % numByte != 0) {
    CommonWarning("BsReadFile: bit stream buffer error");
    return 1;
  }
  curBuf = (stream->numByte / numByte) % 2;
  numByteRead = fread(stream->buffer[curBuf]->data,sizeof(char),numByte,
		      stream->file);
  if (ferror(stream->file)) {
    CommonWarning("BsReadFile: error reading bit stream file");
    return 1;
  }
  stream->numByte += numByteRead;

  if (BSdebugLevel >= 3)
    printf("BsReadFile: numByte=%ld  numByteRead=%ld\n",numByte,numByteRead);

  return 0;
}


/* BsWriteFile() */
/* Write one buffer to file. */

static int BsWriteFile (
  BsBitStream *stream)		/* in: stream */
				/* returns: 0=OK  1=error */
{
  long numByte;
  long numByteWritten;

  if (BSdebugLevel >= 3)
    printf("BsWriteFile: id=%ld  streamNumByte=%ld  curBit=%ld\n",
	   stream->streamId,stream->numByte,stream->currentBit);

  if (stream->numByte % bit2byte(stream->buffer[0]->size) != 0) {
    CommonWarning("BsWriteFile: bit stream buffer error");
    return 1;
  }
  numByte = bit2byte(stream->currentBit) - stream->numByte;
  numByteWritten = fwrite(stream->buffer[0]->data,sizeof(char),numByte,
			  stream->file);
  if (numByteWritten != numByte || ferror(stream->file)) {
    CommonWarning("BsWriteFile: error writing bit stream file");
    return 1;
  }
  stream->numByte += numByteWritten;

  if (BSdebugLevel >= 3)
    printf("BsWriteFile: numByteWritten=%ld\n",numByteWritten);

  return 0;
}


/* BsReadAhead() */
/* If possible, ensure that the next numBit bits are available */
/* in read file buffer. */

static int BsReadAhead (
  BsBitStream *stream,		/* in: bit stream */
  long numBit)			/* in: number of bits */
				/* returns: 0=OK  1=error */
{
  if (stream->write==1 || stream->file==NULL)
    return 0;

  if (bit2byte(stream->currentBit+numBit) > stream->numByte)
    if (BsReadFile(stream)) {
      CommonWarning("BsReadAhead: error reading bit stream file");
      return 1;
    }
  return 0;
}


/* BsCheckRead() */
/* Check if numBit bits could be read from stream. */

static int BsCheckRead (
  BsBitStream *stream,		/* in: bit stream */
  long numBit)			/* in: number of bits */
				/* returns: */
				/*  0=numBit bits could be read */
				/*    (or write mode) */
				/*  1=numBit bits could not be read */
{
  if (stream->write == 1)
    return 0;		/* write mode */
  else
    if (stream->file == NULL)
      return (stream->currentBit+numBit > stream->buffer[0]->numBit) ? 1 : 0;
    else
      return (bit2byte(stream->currentBit+numBit) > stream->numByte) ? 1 : 0;
}


/* BsReadByte() */
/* Read byte from stream. */

static int BsReadByte (
  BsBitStream *stream,		/* in: stream */
  unsigned long *data,		/* out: data (max 8 bit, right justified) */
  int numBit)			/* in: num bits [1..8] */
				/* returns: 0=OK  1=error */
{
  long numUsed,idx,buf;

  if (stream->file != NULL &&
      stream->currentBit == stream->numByte*BYTE_NUMBIT)
    if (BsReadFile(stream)) {
      CommonWarning("BsReadByte: error reading bit stream file");
      return 1;
    }

  if (BsCheckRead(stream,numBit)) {
    CommonWarning("BsReadByte: not enough bits left in stream");
    return 1;
  }

  idx = (stream->currentBit / BYTE_NUMBIT) % bit2byte(stream->buffer[0]->size);
  buf = (stream->currentBit / BYTE_NUMBIT /
	 bit2byte(stream->buffer[0]->size)) % 2;
  numUsed = stream->currentBit % BYTE_NUMBIT;
  *data = (stream->buffer[buf]->data[idx] >> (BYTE_NUMBIT-numUsed-numBit)) &
    ((1<<numBit)-1);
  stream->currentBit += numBit;
  return 0;
}


/* BsWriteByte() */
/* Write byte to stream. */

static int BsWriteByte (
  BsBitStream *stream,		/* in: stream */
  unsigned long data,		/* in: data (max 8 bit, right justified) */
  int numBit)			/* in: num bits [1..8] */
				/* returns: 0=OK  1=error */
{
  long numUsed,idx;

  if (stream->file == NULL &&
      stream->buffer[0]->numBit+numBit > stream->buffer[0]->size) {
    CommonWarning("BsWriteByte: not enough bits left in buffer");
    return 1;
  }
  idx = (stream->currentBit / BYTE_NUMBIT) % bit2byte(stream->buffer[0]->size);
  numUsed = stream->currentBit % BYTE_NUMBIT;
  if (numUsed == 0)
    stream->buffer[0]->data[idx] = 0;
  stream->buffer[0]->data[idx] |= (data & ((1<<numBit)-1)) <<
    BYTE_NUMBIT-numUsed-numBit;
  stream->currentBit += numBit;
  if (stream->file==NULL)
    stream->buffer[0]->numBit = stream->currentBit;
  if (stream->file!=NULL && stream->currentBit%stream->buffer[0]->size==0)
    if (BsWriteFile(stream)) {
      CommonWarning("BsWriteByte: error writing bit stream file");
      return 1;
    }
  return 0;
}


/* ---------- functions ---------- */

#ifdef VERSION2

/* BsReadByteEP() */
/* Read byte from stream.  It's a clon of BsReadByteEP() with an additional parameter list */

static int BsReadByteEP (
  CODE_TABLE code,          /* in: code of the actual bitstream element */
  HANDLE_EP_INFO handle,        /* in: EP Info handler */
  BsBitStream    *stream,       /* in: stream */
  unsigned long  *data,         /* out: data (max 8 bit, right justified) */
  int            numBit)        /* in: num bits [1..8] */
                                /* returns: 0=OK  1=error */
{
  long           numUsed,idx,buf;
  unsigned short priorityClass;
  unsigned short lowClass;
  unsigned short sumBits;

  if (stream->file!=NULL && stream->currentBit==stream->numByte*BYTE_NUMBIT)
    if (BsReadFile(stream)) {
      CommonWarning("BsReadByte: error reading bit stream file");
      return 1;
    }

  if (BsCheckRead(stream,numBit)) {
    CommonWarning("BsReadByte: not enough bits left in stream");
    return 1;
  }
   
  priorityClass = handle->priority[(int)code]; 
  /* computes max position of current bit pointer */
  sumBits = 0;
  for( lowClass = 0; lowClass <= priorityClass; lowClass++ )
    sumBits += handle->bitsInClass[lowClass];
  /* read bits, only if current bit pointer in the correct range */
  if( handle->curBitPos[priorityClass] < sumBits ) {
    idx = (handle->curBitPos[priorityClass] / BYTE_NUMBIT) % bit2byte(stream->buffer[0]->size);
    buf = (handle->curBitPos[priorityClass] / BYTE_NUMBIT /
           bit2byte(stream->buffer[0]->size)) % 2;
    numUsed =  handle->curBitPos[ priorityClass ] % BYTE_NUMBIT;
    *data = (stream->buffer[buf]->data[idx] >> (BYTE_NUMBIT-numUsed-numBit)) &
            ((1<<numBit)-1);
    stream->currentBit += numBit;
    handle->curBitPos[ priorityClass ] += numBit;
  }
  else {
    *data = 0; /* init *data */
    if( code == ALIGN_BITS ) stream->currentBit += numBit; /* to avoid an endless loop at the end of a frame */
  }
  
  return 0;
}

/* BsGetBitEP() */
/* Read bits from bit stream. It's a clon of BSGetBit() with an additional parameter list */
/* (Current position in bit stream is advanced !!!) */

int BsGetBitEP (
  CODE_TABLE code,          /* in: code of the aac bitstream element */
  HANDLE_EP_INFO handle,        /* in: handle with info to decode EP bitstream */    
  BsBitStream    *stream,       /* in: bit stream */
  unsigned long  *data,         /* out: bits read */
                                /*      (may be NULL if numBit==0) */
  int numBit)                   /* in: num bits to read */
                                /*     [0..32] */
                                /* returns: */
                                /*  0=OK  1=error */
{
  int num,maxNum,curNum;
  unsigned long bits;

  if (stream->write != 0)
    CommonExit(1,"BsGetBitEP: stream not in read mode");
  if (numBit<0 || numBit>LONG_NUMBIT) {
    /*    CommonExit(1,"BsGetBitEP: number of bits out of range (%d)",numBit);*/
    fprintf( stderr, "BsGetBitEP: number of bits out of range (%d)\n", numBit);
    return 1;
  }   

  if (data != NULL)
    *data = 0;
  if (numBit == 0)
    return 0;

  /* read bits in packets according to buffer byte boundaries */
  num = 0;
  maxNum = BYTE_NUMBIT - handle->curBitPos[ handle->priority[(int)code]] % BYTE_NUMBIT;
  while (num < numBit) {
    curNum = min(numBit-num,maxNum);
    if (BsReadByteEP(code, handle, stream, &bits, curNum )) {
      CommonWarning("BsGetBit: error reading bit stream");
      return 1;
    }
    *data |= bits<<numBit-num-curNum;
    num += curNum;
    maxNum = BYTE_NUMBIT;
  }

  return 0;
}


/* BsGetBufferAheadEP () */
/* Read bits ahead of current position from bit stream to buffer. It is a clon of BsGetBufferAhead()*/
/* (Current position in bit stream is NOT advanced !!!) */
/* (Buffer is cleared first - previous data in buffer is lost !!!) */

int BsGetBufferAheadEP (
  BsBitStream *stream,          /* in: bit stream */
  BsBitBuffer *buffer,          /* out: buffer read */
                                /*      (may be NULL if numBit==0) */
  long numBit,                  /* in: num bits to read */
  HANDLE_EP_INFO handle)        /* ep info handler */
                                /* returns: */
                                /*  0=OK  1=error */
{
  long                  oldCurrentBit;
  int                   result;
  static unsigned short prevBitsInFrame = 0; /* number of bits in the previous frame */
  static unsigned short callNum = 0;         /* number of the function calls */
  static unsigned long  correctNumBits = 0;   /* correct number of bits */

  callNum += 1;
  if( callNum > 1 ) {
    /* update correct number of reading bits */
    correctNumBits += prevBitsInFrame;
  }
  /* if a frame is lost, find the correct beginning of the next frame */
  if( stream->currentBit != (long) correctNumBits ) {
    stream->currentBit = correctNumBits;
  }

  if (BSdebugLevel >= 2)
    printf("BsGetBufferAheadEP: %s  id=%ld  numBit=%ld\n",
           (stream->file!=NULL)?"file":"buffer",stream->streamId,numBit);

  if (numBit > stream->buffer[0]->size)
    CommonExit(1,"BsGetBufferAheadEP: "
               "number of bits to look ahead too high (%ld)",numBit);

  oldCurrentBit = stream->currentBit;
  result = BsGetBuffer(stream,buffer,numBit);
  stream->currentBit = oldCurrentBit;
  if (result)
    CommonWarning("BsGetBufferAheadEP: error reading bit stream");
  prevBitsInFrame = handle->bitsInFrame; /* store current number of bits in frame */
  return result;
}

/***************************************************************************
* Function : ReadPreDefFile                                                *
* Purpose  : Reads from the predefinition file the pre-definition data of  *
*            a frame.                                                      *
* Arguments: inputFile -- path and filename                                *
* Returns  : Number of classes.                                            *
***************************************************************************/

unsigned short ReadPreDefFile(char *inputFile, HANDLE_EP_INFO handle) 
{
  FILE           *fpointer;                /* file pointer to the pre def. file */
  unsigned short indexSet;
  unsigned short indexClass;
  unsigned short mustBeOne;                /* lenEscape must for AAC equal to one ! */
  unsigned short dummy;                    /* for data, not used in the decoder */
  unsigned short numPreDefSet;             /* not in use, last set is up to date */
  unsigned short numClass;
  int            closeFlag;
  
  fpointer= fopen( inputFile, "r");
  if ( fpointer == 0 ) {
    printf("can't open %s\n", inputFile);
    exit(8);
  }
  fscanf(fpointer, "%hu\n", &numPreDefSet);
  fscanf(fpointer, "%hu\n", &dummy);
  for ( indexSet = 0; indexSet < numPreDefSet; indexSet++ ) {
    fscanf(fpointer, "%hu\n", &numClass);
    for ( indexClass = 0; indexClass < numClass; indexClass++ ) {
      /* Read the EP Parameter for each class */
      fscanf(fpointer,"%hu %hu %hu\n", &mustBeOne,
                                   &handle->classNr[indexClass].rateEscape,  
                                   &handle->classNr[indexClass].crcLenEscape);
      fscanf(fpointer, "%hu\n", &dummy); 
      if ( handle->classNr[indexClass].rateEscape == 0 )
         fscanf(fpointer, "%hu\n", &dummy); /* fixed code rate */
      if ( handle->classNr[indexClass].crcLenEscape == 0 ) 
         fscanf(fpointer, "%hu\n", &dummy); /* fixed crc length */
    }
  }
  closeFlag = fclose( fpointer );
  if ( closeFlag == EOF ) {
    printf("error while closing %s\n", inputFile);
    exit( 8 );
  }
  return( numClass );
}

/*********************************************************************************
   Function : CreateEPInfo()
   Purpose  : creates an info handler and reads all the important data from files
   Argumnets: decParam     -- contents the name of the aac priority table file
              infoFileName -- name of the info file
   Returns  : pointer to the info in the heap
***********************************************************************************/

HANDLE_EP_INFO CreateEPInfo ( char *decParam, char *infoFileName, char *predFileName) {

  HANDLE_EP_INFO handle;
  FILE          *fpointer;         /* local file pointer to the priority tab file  */
  char           line[99];         /* name of the aac bitstream element            */
  char           tabFileName[100];
  char           *curChar;
  unsigned short code;             /* code of aac bitstream element                */
  unsigned short priorityClass;    /* priority class of aac bitstream element      */
  unsigned short numClass;         /* number of classes, get from the priority tab */
  unsigned short numClassFromPred; /* number of classes, get from the pre definition file
                                      numClass <= numClassFromPred, else error */
  unsigned short i;                /* index to count classes                       */
  
  /* parse decParam to find the name of the aac priority table file */ 
  curChar =  strstr ( decParam, "-tab " ); /* do not delete the space after -tab !! */
  if ( curChar == NULL )
    CommonExit(1, "-tab <error protection classification file> is missing within the decoder parameter string");
  while( *curChar != ' ' ) {
    curChar++;
  }
  while( *curChar == ' ' ) {
    curChar++;
  }
  i = 0;
  while( *curChar != '\0' ) {
    tabFileName[i] = *curChar;
    curChar++;
    i++;
  }
  tabFileName[i] = *curChar;
  /* allocate memory in the heap for the EP handler */
  handle = (HANDLE_EP_INFO) malloc ( sizeof(TAG_EP_INFO) );
  /* open the pre-definition file and read in the data */
  numClassFromPred = ReadPreDefFile(predFileName, handle);
  /* open EP info file (only ep mode) */
  handle->infoFilePtr = fopen(infoFileName, "r"); 
  if( handle->infoFilePtr == NULL ) {
    fprintf(stderr, "Can't open %s\n", infoFileName);
    exit(8);
  }
  /* set variables to zero */
  handle->bitsInFrame = 0;
  for( i = 0; i < MAX_CLASSES; i++ ) {
    handle->bitsInClass[i]    = 0;
    handle->curBitPos[i] = 0;
  }
  numClass = 1;
  /* open priority tab and read in*/
  fpointer = fopen(tabFileName, "r");  
  if( fpointer == NULL ) {
    fprintf(stderr, "Can't open %s\n", tabFileName);
    exit(8);
  }
  while( fgets(line, sizeof(line), fpointer) != NULL ) {
    sscanf(line,"%hu %hu",&code, &priorityClass);
    handle->priority[code] = priorityClass;
    if( priorityClass >= numClass) {
      numClass = priorityClass+1;
    }
  }
  handle->numClass = numClass;
  /* check if aac table has the correct number of classes */
  if (numClass > numClassFromPred) {
    fprintf( stderr, "Too much classes in the aac priority table!\n");
  }
  return ( handle );
}

/*********************************************************************************
   Function : ReadFrameData()
   Purpose  : reads frame data from file and init bit position array
   Argumnets: handle = contents all needed data for computing
   Returns  : nothing
***********************************************************************************/

void ReadFrameData( HANDLE_EP_INFO handle ) {

  unsigned short crcCheckResult[8];
  unsigned short classCounter;
  unsigned short lowClass;
  unsigned short dummy;

  /* reads from the EP decoder info file               */
  fscanf( handle->infoFilePtr, "%hu\n", &handle->bitsInFrame);               /* number of bits in frame */
  for( classCounter = 0; classCounter < handle->numClass; classCounter++ ) {
    if( classCounter+1 == handle->numClass )
      fscanf( handle->infoFilePtr, "%hu\n", &crcCheckResult[classCounter]);/* crc check result each class */
    else
      fscanf( handle->infoFilePtr, "%hu", &crcCheckResult[classCounter]);
  }
  for( classCounter = 0; classCounter < handle->numClass; classCounter++ ) {
    fscanf( handle->infoFilePtr, "%hu\n", &handle->bitsInClass[classCounter]); /* read bits in each class  */
    handle->curBitPos[classCounter] = 0;                        /* set bit position in each class to zero */
    for( lowClass = 0; lowClass < classCounter; lowClass++ )
      handle->curBitPos[classCounter] += handle->bitsInClass[lowClass];      /* add offset for each class */
    if ( handle->classNr[classCounter].rateEscape == 1 )
      fscanf( handle->infoFilePtr,"%hu\n", &dummy); /* variable code rate, not important for the decoder */
    if ( handle->classNr[classCounter].crcLenEscape == 1 )
      fscanf( handle->infoFilePtr,"%hu\n", &dummy); /* variable crc length,not important for the decoder */
  }
}

#endif /* VERSION2 */


/* BsInit() */
/* Init bit stream module. */

void BsInit (
  long maxReadAhead,		/* in: max num bits to be read ahead */
				/*     (determines read file buffer size) */
  int debugLevel)		/* in: debug level */
				/*     0=off  1=basic  2=medium  3=full */
{
  BSbufSizeByte = max(MIN_FILE_BUF_SIZE,bit2byte(maxReadAhead));
  BSdebugLevel = debugLevel;
  if (BSdebugLevel >= 1)
    printf("BsInit: debugLevel=%d  bufSizeByte=%ld\n",
	   BSdebugLevel,BSbufSizeByte);
}


/* BsOpenFileRead() */
/* Open bit stream file for reading. */

BsBitStream *BsOpenFileRead (
  char *fileName,		/* in: file name */
  char *magic,			/* in: magic string */
				/*     or NULL (no ASCII header in file) */
  char **info)			/* out: info string */
				/*      or NULL (no info string in file) */
				/* returns: */
				/*  bit stream (handle) */
				/*  or NULL if error */
{
  BsBitStream *stream;
  char header[HEADER_BUF_SIZE];
  int tmp;
  long i,len;

  if (BSdebugLevel >= 1) {
    printf("BsOpenFileRead: fileName=\"%s\"  id=%ld  bufSize=%ld  ",
	   fileName,BSstreamId,byte2bit(BSbufSizeByte));
    if (magic != NULL)
      printf("magic=\"%s\"\n",magic);
    else
      printf("no header\n");
  }

  if ((stream=(BsBitStream*)malloc(sizeof(BsBitStream))) == NULL)
    CommonExit(1,"BsOpenFileRead: memory allocation error (stream)");
  stream->buffer[0]=BsAllocBuffer(byte2bit(BSbufSizeByte));
  stream->buffer[1]=BsAllocBuffer(byte2bit(BSbufSizeByte));

  stream->write = 0;
  stream->streamId = BSstreamId++;
  stream->info = NULL;
 
  if ((stream->file=fopen(fileName,"rb")) == NULL) {
    CommonWarning("BsOpenFileRead: error opening bit stream file %s",
		  fileName);
    BsFreeBuffer(stream->buffer[0]);
    BsFreeBuffer(stream->buffer[1]);
    free(stream);
    return NULL;
  }

  if (magic != NULL) {
    /* read ASCII header */
    /* read magic string */
    len = strlen(magic);
    if (len >= HEADER_BUF_SIZE) {
      CommonWarning("BsOpenFileRead: magic string too long");
      BsClose(stream);
      return NULL;
    }
    for (i=0; i<len; i++)
      header[i]=tmp=fgetc(stream->file);
      if (tmp == EOF) {			/* EOF requires int (not char) */
	CommonWarning("BsOpenFileRead: "
		      "error reading bit stream file (header)");
	BsClose(stream);
	return NULL;
      }
    header[i] = '\0';

    if (strcmp(header,magic) != 0) {
      CommonWarning("BsOpenFileRead: magic string error "
		    "(found \"%s\", need \"%s\")",header,magic);
      BsClose(stream);
      return NULL;
    }
    
    if (info != NULL) {
      /* read info string */
      i = 0;
      while ((header[i]=tmp=fgetc(stream->file)) != '\0') {
	if (tmp == EOF) {		/* EOF requires int (not char) */
	  CommonWarning("BsOpenFileRead: "
			"error reading bit stream file (header)");
	  BsClose(stream);
	  return NULL;
	}
	i++;
	if (i >= HEADER_BUF_SIZE) {
	  CommonWarning("BsOpenFileRead: info string too long");
	  BsClose(stream);
	  return NULL;
	}
      }

      if (BSdebugLevel >= 1)
	printf("BsOpenFileRead: info=\"%s\"\n",header);

      if ((stream->info=(char*)malloc((strlen(header)+1)*sizeof(char)))
	  == NULL)
	CommonExit(1,"BsOpenFileRead: memory allocation error (info)");
      strcpy(stream->info,header);
      *info = stream->info;
    }
  }

  /* init buffer */
  stream->currentBit = 0;
  stream->numByte = 0;

  return stream;
}


/* BsOpenFileWrite() */
/* Open bit stream file for writing. */

BsBitStream *BsOpenFileWrite (
  char *fileName,		/* in: file name */
  char *magic,			/* in: magic string */
				/*     or NULL (no ASCII header) */
  char *info)			/* in: info string */
				/*     or NULL (no info string) */
				/* returns: */
				/*  bit stream (handle) */
				/*  or NULL if error */
{
  BsBitStream *stream;

  if (BSdebugLevel >= 1) {
    printf("BsOpenFileWrite: fileName=\"%s\"  id=%ld  bufSize=%ld  ",
	   fileName,BSstreamId,byte2bit(BSbufSizeByte));
    if (magic != NULL) {
      printf("magic=\"%s\"\n",magic);
      if (info != NULL)
	printf("BsOpenFileWrite: info=\"%s\"\n",info);
      else
	printf("BsOpenFileWrite: no info\n");
    }
    else
      printf("no header\n");
  }

  if ((stream=(BsBitStream*)malloc(sizeof(BsBitStream))) == NULL)
    CommonExit(1,"BsOpenFileWrite: memory allocation error (stream)");
  stream->buffer[0]=BsAllocBuffer(byte2bit(BSbufSizeByte));

  stream->write = 1;
  stream->streamId = BSstreamId++;
  stream->info = NULL;

  if ((stream->file=fopen(fileName,"wb")) == NULL) {
    CommonWarning("BsOpenFileWrite: error opening bit stream file %s",
		  fileName);
    BsFreeBuffer(stream->buffer[0]);
    free(stream);
    return NULL;
  }

  if (magic!=NULL) {
    /* write ASCII header */
    /* write magic string */
    if (fputs(magic,stream->file) == EOF) {
      CommonWarning("BsOpenFileWrite: error writing bit stream file (header)");
      BsClose(stream);
      return NULL;
    }
    if (info!=NULL) {
      /* write info string */
      if (fputs(info,stream->file) == EOF) {
	CommonWarning("BsOpenFileWrite: "
		      "error writing bit stream file (header)");
	BsClose(stream);
	return NULL;
      }
      if (fputc('\0',stream->file) == EOF) {
	CommonWarning("BsOpenFileWrite: "
		      "error writing bit stream file (header)");
	BsClose(stream);
	return NULL;
      }
    }
  }

  stream->currentBit = 0;
  stream->numByte = 0;

  return stream;
}


/* BsOpenBufferRead() */
/* Open bit stream buffer for reading. */

BsBitStream *BsOpenBufferRead (
  BsBitBuffer *buffer)		/* in: bit buffer */
				/* returns: */
				/*  bit stream (handle) */
{
  BsBitStream *stream;

  if (BSdebugLevel >= 2)
    printf("BsOpenBufferRead: id=%ld  bufNumBit=%ld  bufSize=%ld  "
	   "bufAddr=0x%lx\n",
	   BSstreamId,buffer->numBit,buffer->size,(unsigned long)buffer);

  if ((stream=(BsBitStream*)malloc(sizeof(BsBitStream))) == NULL)
    CommonExit(1,"BsOpenBufferRead: memory allocation error");

  stream->file = NULL;
  stream->write = 0;
  stream->streamId = BSstreamId++;
  stream->info = NULL;

  stream->buffer[0] = buffer;
  stream->currentBit = 0;
  
  return stream;
}


/* BsOpenBufferWrite() */
/* Open bit stream buffer for writing. */
/* (Buffer is cleared first - previous data in buffer is lost !!!) */

BsBitStream *BsOpenBufferWrite (
  BsBitBuffer *buffer)		/* in: bit buffer */
				/* returns: */
				/*  bit stream (handle) */
{
  BsBitStream *stream;

  if (BSdebugLevel >= 2)
    printf("BsOpenBufferWrite: id=%ld  bufNumBit=%ld  bufSize=%ld  "
	   "bufAddr=0x%lx\n",
	   BSstreamId,buffer->numBit,buffer->size,(unsigned long)buffer);

  if ((stream=(BsBitStream*)malloc(sizeof(BsBitStream))) == NULL)
    CommonExit(1,"BsOpenBufferWrite: memory allocation error");

  stream->file = NULL;
  stream->write = 1;
  stream->streamId = BSstreamId++;
  stream->info = NULL;

  stream->buffer[0] = buffer;
  BsClearBuffer(buffer);
  stream->currentBit = 0;
  
  return stream;
}


/* BsCurrentBit() */
/* Get number of bits read/written from/to stream. */

long BsCurrentBit (
  BsBitStream *stream)		/* in: bit stream */
				/* returns: */
				/*  number of bits read/written */
{
  return stream->currentBit;
}


/* BsEof() */
/* Test if end of bit stream file occurs within the next numBit bits. */

int BsEof (
  BsBitStream *stream,		/* in: bit stream */
  long numBit)			/* in: number of bits ahead scanned for EOF */
				/* returns: */
				/*  0=not EOF  1=EOF */
{
  int eof;

  if (BSdebugLevel >= 2)
    printf("BsEof: %s  id=%ld  curBit=%ld  numBit=%ld\n",
	   (stream->file!=NULL)?"file":"buffer",
	   stream->streamId,stream->currentBit,numBit);

  if (stream->file != NULL && numBit > stream->buffer[0]->size)
    CommonExit(1,"BsEof: "
	       "number of bits to look ahead too high (%ld)",numBit);

  if (BsReadAhead(stream,numBit+1)) {
    CommonWarning("BsEof: error reading bit stream");
    eof = 0;
  }
  else
    eof = BsCheckRead(stream,numBit+1);

  if (BSdebugLevel >= 2)
    printf("BsEof: eof=%d\n",eof);

  return eof;
}


/* BsClose() */
/* Close bit stream. */

int BsClose (
  BsBitStream *stream)		/* in: bit stream */
				/* returns: */
				/*  0=OK  1=error */
{
  int returnFlag = 0;

  if (stream->file != NULL && BSdebugLevel >= 1 || BSdebugLevel >= 2)
    printf("BsClose: %s  %s  id=%ld  curBit=%ld\n",
	   (stream->write)?"write":"read",
	   (stream->file!=NULL)?"file":"buffer",
	   stream->streamId,stream->currentBit);

  if (stream->file != NULL) {
    if (stream->write == 1)
      /* flush buffer to file */
      if (BsWriteFile(stream)) {
	CommonWarning("BsClose: error writing bit stream");
	returnFlag = 1;
      }

    BsFreeBuffer(stream->buffer[0]);
    if (stream->write == 0)
      BsFreeBuffer(stream->buffer[1]);

    if (fclose(stream->file)) {
      CommonWarning("BsClose: error closing bit stream file");
      returnFlag = 1;
    }
  }

  if (stream->info != NULL)
    free(stream->info);
  free(stream);
  return returnFlag;
}


/* BsGetBit() */
/* Read bits from bit stream. */
/* (Current position in bit stream is advanced !!!) */

int BsGetBit (
  BsBitStream *stream,		/* in: bit stream */
  unsigned long *data,		/* out: bits read */
				/*      (may be NULL if numBit==0) */
  int numBit)			/* in: num bits to read */
				/*     [0..32] */
				/* returns: */
				/*  0=OK  1=error */
{
  int num,maxNum,curNum;
  unsigned long bits;

  if (BSdebugLevel >= 3)
    printf("BsGetBit: %s  id=%ld  numBit=%d  curBit=%ld\n",
	   (stream->file!=NULL)?"file":"buffer",
	   stream->streamId,numBit,stream->currentBit);

  if (stream->write != 0)
    CommonExit(1,"BsGetBit: stream not in read mode");
  if (numBit<0 || numBit>LONG_NUMBIT)
    CommonExit(1,"BsGetBit: number of bits out of range (%d)",numBit);

  if (data != NULL)
    *data = 0;
  if (numBit == 0)
    return 0;

  /* read bits in packets according to buffer byte boundaries */
  num = 0;
  maxNum = BYTE_NUMBIT - stream->currentBit % BYTE_NUMBIT;
  while (num < numBit) {
    curNum = min(numBit-num,maxNum);
    if (BsReadByte(stream,&bits,curNum)) {
      CommonWarning("BsGetBit: error reading bit stream");
      return 1;
    }
    *data |= bits<<numBit-num-curNum;
    num += curNum;
    maxNum = BYTE_NUMBIT;
  }

  if (BSdebugLevel >= 3)
    printf("BsGetBit: data=0x%lx\n",*data);

  return 0;
}


/* this function is mainly for debugging purpose, */
/* you can call it from the debugger */
long int BsGetBitBack (
  BsBitStream *stream,		/* in: bit stream */
  int numBit)			/* in: num bits to read */
				/*     [0..32] */
				/* returns: */
				/*  if numBit is positive
				      return the last numBits bit from stream
				    else
				      return the next -numBits from stream 
				    stream->currentBit is always unchanged */
{
  int num,maxNum,curNum;
  unsigned long bits;
  long int data;
  int rewind =0;
  if (BSdebugLevel >= 3)
    printf("BsGetBitBack: %s  id=%ld  numBit=%d  curBit=%ld\n",
	   (stream->file!=NULL)?"file":"buffer",
	   stream->streamId,numBit,stream->currentBit);

/*   if (stream->write != 0) */
/*     CommonWarning("BsGetBitBack: stream not in read mode"); */
  if (numBit<-32 || numBit>LONG_NUMBIT)
    CommonExit(1,"BsGetBitBack: number of bits out of range (%d)",numBit);

  data = 0;
  if (numBit == 0)
    return 0;
  if (numBit > 0)
    stream->currentBit-=numBit;
  else {
    rewind = 1;
    numBit = -numBit;
  }
    
  if (stream->currentBit<0){
    stream->currentBit+=numBit;
    CommonWarning("BsGetBitBack: stream enough bits in stream ");
    return 0;
  }

  /* read bits in packets according to buffer byte boundaries */
  num = 0;
  maxNum = BYTE_NUMBIT - stream->currentBit % BYTE_NUMBIT;
  while (num < numBit) {
    curNum = min(numBit-num,maxNum);
    if (BsReadByte(stream,&bits,curNum)) {
      CommonWarning("BsGetBitBack: error reading bit stream");
      return 0;
    }
    data |= bits<<numBit-num-curNum;
    num += curNum;
    maxNum = BYTE_NUMBIT;
  }
  if (rewind) /* rewind */
    stream->currentBit-=numBit;

  if (BSdebugLevel >= 3)
    printf("BsGetBitBack: data=0x%lx\n",data);

  return data;
}


/* BsGetBitChar() */
/* Read bits from bit stream (char). */
/* (Current position in bit stream is advanced !!!) */

int BsGetBitChar (
  BsBitStream *stream,		/* in: bit stream */
  unsigned char *data,		/* out: bits read */
				/*      (may be NULL if numBit==0) */
  int numBit)			/* in: num bits to read */
				/*     [0..8] */
				/* returns: */
				/*  0=OK  1=error */
{
  unsigned long ultmp;
  int result;

  if (numBit > 8)
    CommonExit(1,"BsGetBitChar: number of bits out of range (%d)",numBit);
  if (data != NULL)
    *data = 0;
  if (numBit == 0)
    return 0;
  result = BsGetBit(stream,&ultmp,numBit);
  *data = ultmp;
  return result;
}


/* BsGetBitShort() */
/* Read bits from bit stream (short). */
/* (Current position in bit stream is advanced !!!) */

int BsGetBitShort (
  BsBitStream *stream,		/* in: bit stream */
  unsigned short *data,		/* out: bits read */
				/*      (may be NULL if numBit==0) */
  int numBit)			/* in: num bits to read */
				/*     [0..16] */
				/* returns: */
				/*  0=OK  1=error */
{
  unsigned long ultmp;
  int result;

  if (numBit > 16)
    CommonExit(1,"BsGetBitShort: number of bits out of range (%d)",numBit);
  if (data != NULL)
    *data = 0;
  if (numBit == 0)
    return 0;
  result = BsGetBit(stream,&ultmp,numBit);
  *data = ultmp;
  return result;
}


/* BsGetBitInt() */
/* Read bits from bit stream (int). */
/* (Current position in bit stream is advanced !!!) */

int BsGetBitInt (
  BsBitStream *stream,		/* in: bit stream */
  unsigned int *data,		/* out: bits read */
				/*      (may be NULL if numBit==0) */
  int numBit)			/* in: num bits to read */
				/*     [0..16] */
				/* returns: */
				/*  0=OK  1=error */
{
  unsigned long ultmp;
  int result;

  if (numBit > 16)
    CommonExit(1,"BsGetBitInt: number of bits out of range (%d)",numBit);
  if (data != NULL)
    *data = 0;
  if (numBit == 0)
    return 0;
  result = BsGetBit(stream,&ultmp,numBit);
  *data = ultmp;
  return result;
}


/* BsGetBitAhead() */
/* Read bits from bit stream. */
/* (Current position in bit stream is NOT advanced !!!) */

int BsGetBitAhead (
  BsBitStream *stream,		/* in: bit stream */
  unsigned long *data,		/* out: bits read */
				/*      (may be NULL if numBit==0) */
  int numBit)			/* in: num bits to read */
				/*     [0..32] */
				/* returns: */
				/*  0=OK  1=error */
{
  long oldCurrentBit;
  int result;

  if (BSdebugLevel >= 3)
    printf("BsGetBitAhead: %s  id=%ld  numBit=%d\n",
	   (stream->file!=NULL)?"file":"buffer",stream->streamId,numBit);

  oldCurrentBit = stream->currentBit;
  result = BsGetBit(stream,data,numBit);
  stream->currentBit = oldCurrentBit;
  if (result)
    CommonWarning("BsGetBitAhead: error reading bit stream");

  return result;
}


/* BsGetBitAheadChar() */
/* Read bits from bit stream (char). */
/* (Current position in bit stream is NOT advanced !!!) */

int BsGetBitAheadChar (
  BsBitStream *stream,		/* in: bit stream */
  unsigned char *data,		/* out: bits read */
				/*      (may be NULL if numBit==0) */
  int numBit)			/* in: num bits to read */
				/*     [0..8] */
				/* returns: */
				/*  0=OK  1=error */
{
  long oldCurrentBit;
  int result;

  if (BSdebugLevel >= 3)
    printf("BsGetBitAheadChar: %s  id=%ld  numBit=%d\n",
	   (stream->file!=NULL)?"file":"buffer",stream->streamId,numBit);

  oldCurrentBit = stream->currentBit;
  result = BsGetBitChar(stream,data,numBit);
  stream->currentBit = oldCurrentBit;
  if (result)
    CommonWarning("BsGetBitAheadChar: error reading bit stream");

  return result;
}


/* BsGetBitAheadShort() */
/* Read bits from bit stream (short). */
/* (Current position in bit stream is NOT advanced !!!) */

int BsGetBitAheadShort (
  BsBitStream *stream,		/* in: bit stream */
  unsigned short *data,		/* out: bits read */
				/*      (may be NULL if numBit==0) */
  int numBit)			/* in: num bits to read */
				/*     [0..16] */
				/* returns: */
				/*  0=OK  1=error */
{
  long oldCurrentBit;
  int result;

  if (BSdebugLevel >= 3)
    printf("BsGetBitAheadShort: %s  id=%ld  numBit=%d\n",
	   (stream->file!=NULL)?"file":"buffer",stream->streamId,numBit);

  oldCurrentBit = stream->currentBit;
  result = BsGetBitShort(stream,data,numBit);
  stream->currentBit = oldCurrentBit;
  if (result)
    CommonWarning("BsGetBitAheadShort: error reading bit stream");

  return result;
}


/* BsGetBitAheadInt() */
/* Read bits from bit stream (int). */
/* (Current position in bit stream is NOT advanced !!!) */

int BsGetBitAheadInt (
  BsBitStream *stream,		/* in: bit stream */
  unsigned int *data,		/* out: bits read */
				/*      (may be NULL if numBit==0) */
  int numBit)			/* in: num bits to read */
				/*     [0..16] */
				/* returns: */
				/*  0=OK  1=error */
{
  long oldCurrentBit;
  int result;

  if (BSdebugLevel >= 3)
    printf("BsGetBitAheadInt: %s  id=%ld  numBit=%d\n",
	   (stream->file!=NULL)?"file":"buffer",stream->streamId,numBit);

  oldCurrentBit = stream->currentBit;
  result = BsGetBitInt(stream,data,numBit);
  stream->currentBit = oldCurrentBit;
  if (result)
    CommonWarning("BsGetBitAheadInt: error reading bit stream");

  return result;
}


/* BsGetBuffer() */
/* Read bits from bit stream to buffer. */
/* (Current position in bit stream is advanced !!!) */
/* (Buffer is cleared first - previous data in buffer is lost !!!) */

int BsGetBuffer (
  BsBitStream *stream,		/* in: bit stream */
  BsBitBuffer *buffer,		/* out: buffer read */
				/*      (may be NULL if numBit==0) */
  long numBit)			/* in: num bits to read */
				/* returns: */
				/*  0=OK  1=error */
{
  long i,numByte,numRemain;
  unsigned long data;

  if (BSdebugLevel >= 2) {
    printf("BsGetBuffer: %s  id=%ld  numBit=%ld  ",
	   (stream->file!=NULL)?"file":"buffer",
	   stream->streamId,numBit);
      if (buffer != NULL)
	printf("bufSize=%ld  bufAddr=0x%lx  ",
	       buffer->size,(unsigned long)buffer);
      else
	printf("(bufAddr=(NULL)  ");
    printf("curBit=%ld\n",stream->currentBit);
  }

  if (stream->write != 0)
    CommonExit(1,"BsGetBuffer: stream not in read mode");

  if (numBit == 0)
    return 0;

  if (stream->buffer[0] == buffer)
    CommonExit(1,"BsGetBuffer: can not get buffer from itself");
  if (numBit < 0 || numBit > buffer->size)
    CommonExit(1,"BsGetBuffer: number of bits out of range (%ld)",numBit);

  BsClearBuffer(buffer);

  numByte = bit2byte(numBit)-1;
  for (i=0; i<numByte; i++) {
    if (BsGetBit(stream,&data,BYTE_NUMBIT)) {
      CommonWarning("BsGetBuffer: error reading bit stream");
      buffer->numBit = i*BYTE_NUMBIT;
      return 1;
    }
    buffer->data[i] = data;
  }
  numRemain = numBit-numByte*BYTE_NUMBIT;
  if (BsGetBit(stream,&data,numRemain)) {
    CommonWarning("BsGetBuffer: error reading bit stream");
    buffer->numBit = numByte*BYTE_NUMBIT;
    return 1;
  }
  buffer->data[i] = data<<BYTE_NUMBIT-numRemain;
  buffer->numBit = numBit;

  return 0;
}

/* BsGetBufferAppend() */
/* Append bits from bit stream to buffer. */
/* (Current position in bit stream is advanced !!!) */

int BsGetBufferAppend (
  BsBitStream *stream,		/* in: bit stream */
  BsBitBuffer *buffer,		/* out: buffer read */
				/*      (may be NULL if numBit==0) */
  int append,			/* in: if != 0: append bits to buffer */
				/*              (don't clear buffer) */
  long numBit)			/* in: num bits to read */
				/* returns: */
				/*  0=OK  1=error */
{
  long i,numByte,last_byte,numRemain;
  unsigned long data;
   int tmp,shift_cnt;

  if (BSdebugLevel >= 2) {
    printf("BsGetBufferAppend: %s  id=%ld  numBit=%ld  ",
	   (stream->file!=NULL)?"file":"buffer",
	   stream->streamId,numBit);
      if (buffer != NULL)
	printf("bufSize=%ld  bufAddr=0x%lx  ",
	       buffer->size,(unsigned long)buffer);
      else
	printf("(bufAddr=(NULL)  ");
    printf("curBit=%ld\n",stream->currentBit);
  }

  if (stream->write != 0)
    CommonExit(1,"BsGetBufferAppend: stream not in read mode");

  if (stream->buffer[0] == buffer)
    CommonExit(1,"BsGetBufferAppend: can not get buffer from itself");

  if (numBit < 0)
    CommonExit(1,"BsGetBufferAppend: number of bits out of range (%ld)",
	       numBit);

  if (append) {
    /* append to buffer (don't clear buffer) */

    if( (numBit+buffer->numBit)  > buffer->size )
      CommonExit(1,"BsGetBufferAppend: number of bits out of range (%ld)",
		 numBit);

    /* fill up the last possible incomplete byte */
    tmp = 8 - buffer->numBit%8;
    if (tmp == 8)
      tmp = 0;

    if (tmp <= numBit) {
      shift_cnt = 0;
    }
    else {
      shift_cnt = tmp - numBit;
      tmp = numBit;
    }

    if (tmp) {
      if (BsGetBit(stream,&data,tmp)) {
        CommonWarning("BsGetBufferAppend: error reading bit stream");
        return 1;
      }
      data <<= shift_cnt;
      numBit -= tmp;
      last_byte = buffer->numBit/8;
      data |= buffer->data[last_byte];
      buffer->numBit += tmp;
      buffer->data[last_byte] = data;
      last_byte++;
    }
    else
      last_byte = buffer->numBit/8;

  }
  else {	/* if (append) */
    /* clear buffer */
    if (numBit > buffer->size)
      CommonExit(1,"BsGetBufferAppend: number of bits out of range (%ld)",
		 numBit);
    BsClearBuffer(buffer);
    last_byte = 0;
  }

  if (numBit > 0) {
    numByte = bit2byte(numBit)-1;
    for (i=last_byte; i<last_byte+numByte; i++) {
      if (BsGetBit(stream,&data,BYTE_NUMBIT)) {
        CommonWarning("BsGetBufferAppend: error reading bit stream");
        buffer->numBit += (i-last_byte)*BYTE_NUMBIT;
        return 1;
      }
      buffer->data[i] = data;
    }
    numRemain = numBit-numByte*BYTE_NUMBIT;
    if (BsGetBit(stream,&data,numRemain)) {
      CommonWarning("BsGetBufferAppend: error reading bit stream");
      buffer->numBit += numByte*BYTE_NUMBIT;
      return 1;
    }
    buffer->data[i] = data<<BYTE_NUMBIT-numRemain;
    buffer->numBit += numBit;
  }
  return 0;
}

/* BsGetBufferAhead() */
/* Read bits ahead of current position from bit stream to buffer. */
/* (Current position in bit stream is NOT advanced !!!) */
/* (Buffer is cleared first - previous data in buffer is lost !!!) */

int BsGetBufferAhead (
  BsBitStream *stream,		/* in: bit stream */
  BsBitBuffer *buffer,		/* out: buffer read */
				/*      (may be NULL if numBit==0) */
  long numBit)			/* in: num bits to read */
				/* returns: */
				/*  0=OK  1=error */
{
  long oldCurrentBit;
  int result;

  if (BSdebugLevel >= 2)
    printf("BsGetBufferAhead: %s  id=%ld  numBit=%ld\n",
	   (stream->file!=NULL)?"file":"buffer",stream->streamId,numBit);

  if (numBit > stream->buffer[0]->size)
    CommonExit(1,"BsGetBufferAhead: "
	       "number of bits to look ahead too high (%ld)",numBit);

  oldCurrentBit = stream->currentBit;
  result = BsGetBuffer(stream,buffer,numBit);
  stream->currentBit = oldCurrentBit;
  if (result)
    CommonWarning("BsGetBufferAhead: error reading bit stream");

  return result;
}


/* BsGetSkip() */
/* Skip bits in bit stream (read). */
/* (Current position in bit stream is advanced !!!) */

int BsGetSkip (
  BsBitStream *stream,		/* in: bit stream */
  long numBit)			/* in: num bits to skip */
				/* returns: */
				/*  0=OK  1=error */
{
  if (BSdebugLevel >= 2) {
    printf("BsGetSkip: %s  id=%ld  numBit=%ld  ",
	   (stream->file!=NULL)?"file":"buffer",
	   stream->streamId,numBit);
    printf("curBit=%ld\n",stream->currentBit);
  }

  if (stream->write != 0)
    CommonExit(1,"BsGetSkip: stream not in read mode");
  if (numBit < 0)
    CommonExit(1,"BsGetSkip: number of bits out of range (%ld)",numBit);

  if (numBit == 0)
    return 0;

  if (BsReadAhead(stream,numBit) || BsCheckRead(stream,numBit)) {
    CommonWarning("BsGetSkip: error reading bit stream");
    return 1;
  }
  stream->currentBit += numBit;
  return 0;
}


/* BsPutBit() */
/* Write bits to bit stream. */

int BsPutBit (
  BsBitStream *stream,		/* in: bit stream */
  unsigned long data,		/* in: bits to write */
  int numBit)			/* in: num bits to write */
				/*     [0..32] */
				/* returns: */
				/*  0=OK  1=error */
{
  int num,maxNum,curNum;
  unsigned long bits;

  if (BSdebugLevel > 3)
    printf("BsPutBit: %s  id=%ld  numBit=%d  data=0x%lx,%ld  curBit=%ld\n",
	   (stream->file!=NULL)?"file":"buffer",
	   stream->streamId,numBit,data,data,stream->currentBit);

  if (stream->write != 1)
    CommonExit(1,"BsPutBit: stream not in write mode");
  if (numBit<0 || numBit>LONG_NUMBIT)
    CommonExit(1,"BsPutBit: number of bits out of range (%d)",numBit);
  if (numBit < 32 && data > ((unsigned long)1<<numBit)-1)
    CommonExit(1,"BsPutBit: data requires more than %d bits (0x%lx)",
	       numBit,data);

  if (numBit == 0)
    return 0;

  /* write bits in packets according to buffer byte boundaries */
  num = 0;
  maxNum = BYTE_NUMBIT - stream->currentBit % BYTE_NUMBIT;
  while (num < numBit) {
    curNum = min(numBit-num,maxNum);
    bits = data>>numBit-num-curNum;
    if (BsWriteByte(stream,bits,curNum)) {
      CommonWarning("BsPutBit: error writing bit stream");
      return 1;
    }
    num += curNum;
    maxNum = BYTE_NUMBIT;
  }

  return 0;
}


/* BsPutBuffer() */
/* Write bits from buffer to bit stream. */

int BsPutBuffer (
  BsBitStream *stream,		/* in: bit stream */
  BsBitBuffer *buffer)		/* in: buffer to write */
				/* returns: */
				/*  0=OK  1=error */
{
  long i,numByte,numRemain;

  if (buffer->numBit == 0)
    return 0;

  if (BSdebugLevel >= 2)
    printf("BsPutBuffer: %s  id=%ld  numBit=%ld  bufAddr=0x%lx  curBit=%ld\n",
	   (stream->file!=NULL)?"file":"buffer",
	   stream->streamId,buffer->numBit,(unsigned long)buffer,
	   stream->currentBit);

  if (stream->write != 1)
    CommonExit(1,"BsPutBuffer: stream not in write mode");
  if (stream->buffer[0] == buffer)
    CommonExit(1,"BsPutBuffer: can not put buffer into itself");

  numByte = bit2byte(buffer->numBit)-1;
  for (i=0; i<numByte; i++)
    if (BsPutBit(stream,buffer->data[i],BYTE_NUMBIT)) {
      CommonWarning("BsPutBuffer: error writing bit stream");
      return 1;
    }
  numRemain = buffer->numBit-numByte*BYTE_NUMBIT;
  if (BsPutBit(stream,buffer->data[i]>>BYTE_NUMBIT-numRemain,numRemain)) {
    CommonWarning("BsPutBuffer: error reading bit stream");
    return 1;
  }

  return 0;
}


/* BsAllocBuffer() */
/* Allocate bit buffer. */

BsBitBuffer *BsAllocBuffer (
  long numBit)			/* in: buffer size in bits */
				/* returns: */
				/*  bit buffer (handle) */
{
  BsBitBuffer *buffer;

  if (BSdebugLevel >= 2)
    printf("BsAllocBuffer: size=%ld\n",numBit);

  if ((buffer=(BsBitBuffer*)malloc(sizeof(BsBitBuffer))) == NULL)
    CommonExit(1,"BsAllocBuffer: memory allocation error (buffer)");
  if ((buffer->data=(unsigned char*)malloc(bit2byte(numBit)*sizeof(char)))
      == NULL)
    CommonExit(1,"BsAllocBuffer: memory allocation error (data)");
  buffer->numBit = 0;
  buffer->size = numBit;

  if (BSdebugLevel >= 2)
    printf("BsAllocBuffer: bufAddr=0x%lx\n",(unsigned long)buffer);

  return buffer;
}


/* BsFreeBuffer() */
/* Free bit buffer. */

void BsFreeBuffer (
  BsBitBuffer *buffer)		/* in: bit buffer */
{
  if (BSdebugLevel >= 2)
    printf("BsFreeBuffer: size=%ld  bufAddr=0x%lx\n",
	   buffer->size,(unsigned long)buffer);

  free(buffer->data);
  free(buffer);
}


/* BsBufferNumBit() */
/* Get number of bits in buffer. */

long BsBufferNumBit (
  BsBitBuffer *buffer)		/* in: bit buffer */
				/* returns: */
				/*  number of bits in buffer */
{
  if (BSdebugLevel >= 2)
    printf("BsBufferNumBit: numBit=%ld  size=%ld  bufAddr=0x%lx\n",
	   buffer->numBit,buffer->size,(unsigned long)buffer);

  return buffer->numBit;
}


/* BsClearBuffer() */
/* Clear bit buffer (set numBit to 0). */

void BsClearBuffer (
  BsBitBuffer *buffer)		/* in: bit buffer */
{
  if (BSdebugLevel >= 2)
    printf("BsClearBuffer: size=%ld  bufAddr=0x%lx\n",
	   buffer->size,(unsigned long)buffer);

  buffer->numBit = 0;
}


/* end of bitstream.c */

