V10/cmd/bcp/bitio.h

/* Copyright (c) 1989, 1990 AT&T --- All Rights Reserved.              */
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T.                */
/* The copyright notice does not imply actual or intended publication. */
/* AUTHORS:                                            */
/*     H. S. Baird - ATT-BL MH - first versions        */

/* bitio.h - view a stream file as a sequence of binary values, hiding the
   bit- and byte-packing format of the file.  The format of input and output
   files may differ.   Reading and writing are performed by macroes for speed;
   the price for this is that the file formats must be fixed at compile time.

SYNOPSIS
	#include <stdio.h>
	#include "bitio.h"

	BITFILE *bopen(stream,type);
	    FILE *stream;
	    char *type;

	int getb(bitfile);
	    BITFILE *bitfile;

	putb(bit,bitfile);
	    int bit;

	padb(bitfile,bit,bdy,len);
	    int bit,bdy,len;

	char *bbuffer(bitfile);

	unsigned long bsize(bitfile);
	
	unsigned long bflush(bitfile);

	unsigned long bclose(bitfile);

COMPILER DEPENDENCIES
   The compiler's data types must include:
	unsigned char:  8 bits each
	unsigned short: 2 unsigned chars each
	unsigned int:   2 unsigned shorts each
DESCRIPTION
	Bopen views the named stream file as a bit file to be read (if type is "r")
   or written (if type is "w" or "wb").   The stream file must already have been
   fopen(3)ed, and the first bit to be read/written will be the first bit in its
   next byte in getc(3)/putc(3) order.  Bopen returns a pointer which identifies
   the bitfile to the other functions.  System or stream I/O to/from the
   associated stream should not be used until after bclose is called.
	If bopen's type is "wb", then the entire output stream will be buffered
   in main memory until bflush or bclose are called.  At any time, bbuffer
   returns this buffer's address and bsize its length in bytes.
	Getb returns the next bit from the named bitfile.  It returns EOF on
   end of file or read error.  EOF may occur on a byte, short, or int boundary,
   depending on file format.
	Putb appends the given bit to the named bitfile.
	Padb writes 'bit' enough times (possibly 0) so that if a bitstring
   of length 'len' were written next it would end on a 'bdy'-bit boundary
   (may do the wrong thing if 'bdy' doesn't divide UINT_MAX).
	Bflush ensures that all written bits have been written to the stream
   via putc(3).  The output is padded with 0 bits to a byte, short, or int
   boundary, depending on file format.  It returns the number of bytes (not bits)
   written since bopen or the last bflush.  The bitfile remains open.  It does not
   fflush(3) the associated stream.  
	Bclose causes a bflush and frees all buffers.  It returns the total
   number of bytes (not bits) read/written since bopen.  It fflush(3)'es,
   but does not fclose(3) the associated stream.

        Bitfile formats are selected at compile time: see `FORMAT:' at the
   end of this file.  The formats for input and output may differ.  Formats
   include:
   a	each bit is an ASCII character: '0' or '1', in putc(3) order; not padded.
   0	the low-order (0001) bit in each byte is first ("little-endian"), and
	bytes are in putc(3) order; EOF and padding at a byte boundary.
   1	the high-order (0200) bit in each byte is first ("big-endian"), and
	bytes are in putc(3) order; EOF and padding at a byte boundary.
   10	the low-order (0001) bit in each byte is first ("little-endian"), but
	bytes are reversed (in each pair) from putc(3) order;  EOF and padding
	at a short boundary.
   11	the high-order (0200) bit in each byte is first ("big-endian"), but
	bytes are reversed (in each pair) from putc(3) order; EOF and padding
	at a short boundary.
   Planned (data structures are in place; code will be implemented if needed):
   100	the low-order (0001) bit in each byte is first ("little-endian"), and
	bytes (in each pair) are in putc(3) order; but shorts (in each pair)
	are reversed from putc(3) order;  EOF and padding at an int boundary.
   101, 110, 111 - by obvious analogy
BUGS
	Putting to an input bitfile or getting from an output bitfile is
   erroneous, but is not checked for.
*/

#define BUFFERED (T)	/* enable buffering of output */

typedef struct BITFILE {
	FILE *fp;		/* associated stream */
	char type;		/* one of 'r','w' */
	int ic;			/* byte just read */
	unsigned long nb;	/* no. bytes read/written since bopen */
	unsigned long alloc;	/* no. bytes allocated in buffer */
	char *buf;		/* buffer (in malloc space) */
	char *cp;		/* next char in buffer */
	unsigned int n;		/* no. bits written so far (mod UINT_MAX) */
	unsigned char cm;	/* single-bit mask */
	unsigned short sm;	/* single-bit mask */
	unsigned int im;	/* single-bit mask */
	union {	struct {	/* used to reorder char & short order */
			union {	struct {
					unsigned char c0;
					unsigned char c1;
					} cc;
				unsigned short s;
				} s0;
			union {	struct {
					unsigned char c0;
					unsigned char c1;
					} cc;
				unsigned short s;
				} s1;
			} ss;
		unsigned int i;
		} i;
	} BITFILE;
#define Init_BITFILE {NULL,'\0',0,0L,0L,NULL,NULL,0,0,0}
#if MAIN
BITFILE empty_BITFILE = Init_BITFILE;
#else
extern BITFILE empty_BITFILE;
#endif

/* Code common to all formats: */
#if MAIN
BITFILE *bopen_rw(s,t)
    FILE *s;
    char *t;
{   BITFILE *f;
	if((f=(BITFILE *)malloc(sizeof(BITFILE)))==NULL) {
		err("bopen: can't alloc");
		return(NULL);
		};
	*f = empty_BITFILE;
	f->fp = s;
	f->type = *t;
	return(f);
	}
#else
BITFILE *bopen_rw();
#endif

#define bbuffer(f) ((f)->buf)
#define bsize(f) ((bbuffer(f)!=NULL)? ((f)->cp - (f)->buf): 0L)

#if !BUFFERED
#define bputc(c,f) putc((c),(f)->fp)
#define bbflush(f) (0L)
#else
#define BITFILE_incr (512)	/* buffer allocations are in these increments */

#if MAIN
brealloc(f)
    BITFILE *f;
{   int nbuf;	/* no. bytes in buffer */
	nbuf = bsize(f);
	f->alloc += BITFILE_incr;
	if((f->buf=(char *)realloc(f->buf,f->alloc))==NULL)
		abort("");
	f->cp = f->buf + nbuf;
	}
#endif

#define bputc(c,f) { \
	if((f)->buf==NULL) putc((c),(f)->fp); \
	else {	if(bsize(f)==(f)->alloc) brealloc(f); \
		*(++((f)->cp))=(c); \
		} \
	}

#if MAIN
unsigned long bbflush(f)
    BITFILE *f;		/* f->buf!=NULL && bsize(f)>0 */
{   register char *cp,*cq;
    unsigned long nbuf;
	nbuf = bsize(f);
	for(cq=(cp=f->buf)+nbuf; cp<cq; cp++) putc(*cp,f->fp);
	f->cp=f->buf;
	return(nbuf);
	}
#else
unsigned long bbflush();
#endif
#endif

/* Code particular to each format: */

/* Format a:  ASCII file, one printable char ('0' or '1') per bit: */
#define bopen_r_a(s) bopen_rw((s),"r")
#define bopen_w_a(s) bopen_rw((s),"w")
#define getb_a(f) ( (((f)->ic=getc((f)->fp))!=EOF)? \
			((f)->nb++, \
			 ((f)->ic=='0')? \
				0: \
				(((f)->ic=='1')? 1: EOF)): \
			EOF )
#define putb_a(b,f) { if((b)) bputc('1',f); else bputc('0',f); (f)->nb++; }
#define bflush_a(f) ( (bsize(f)>0)? bbflush(f): (0L) )

/* Format 0:  the low-order bit (0001) in each byte is first ("little-endian"),
   and bytes are in putc(3) order; */
#if MAIN
BITFILE *bopen_r_0(s)
    FILE *s;
{   BITFILE *f;
	if((f=bopen_rw(s,"r"))!=NULL) {
		f->cm=0000;
		};
	return(f);
	}
#else
BITFILE *bopen_r_0();
#endif
#if MAIN
BITFILE *bopen_w_0(s)
    FILE *s;
{   BITFILE *f;
	if((f=bopen_rw(s,"w"))!=NULL) {
		f->i.ss.s0.cc.c0=0000;
		f->cm=0001;
		};
	return(f);
	}
#else
BITFILE *bopen_w_0();
#endif
#define getb_0(f) ( ((f)->cm)? \
			( ((f)->cm&(f)->ic)? \
				((f)->cm<<=1,1): \
				((f)->cm<<=1,0) ): \
			( (((f)->ic=getc((f)->fp))==EOF)? \
				EOF: \
				( (f)->nb++, \
				  (f)->cm=0001, \
				  ((f)->cm&(f)->ic)? \
					((f)->cm<<=1,1): \
					((f)->cm<<=1,0) ) ) )
#define putb_0(b,f) { \
	if((b)) (f)->i.ss.s0.cc.c0 |= (f)->cm; \
	if( !((f)->cm<<=1) ) { \
		bputc((f)->i.ss.s0.cc.c0,f); \
		(f)->nb++; \
		(f)->i.ss.s0.cc.c0=0000; (f)->cm=0001; \
		}; \
	(f)->n++; \
	}
#define bflush_0(f) (padb((f),0,8,0), (bsize(f)>0)? bbflush(f): 0L)

/* Format 1:  the high-order bit (0200) in each byte is first ("big-endian"), and
   bytes are in putc(3) order; */
#if MAIN
BITFILE *bopen_r_1(s)
    FILE *s;
{   BITFILE *f;
	if((f=bopen_rw(s,"r"))!=NULL) {
		f->cm=0000;
		};
	return(f);
	}
#else
BITFILE *bopen_r_1();
#endif
#if MAIN
BITFILE *bopen_w_1(s)
    FILE *s;
{   BITFILE *f;
	if((f=bopen_rw(s,"w"))!=NULL) {
		f->i.ss.s0.cc.c0=0000;
		f->cm=0200;
		};
	return(f);
	}
#else
BITFILE *bopen_w_1();
#endif
#define getb_1(f) ( ((f)->cm)? \
			( ((f)->cm&(f)->ic)? \
				((f)->cm>>=1,1): \
				((f)->cm>>=1,0) ): \
			( (((f)->ic=getc((f)->fp))==EOF)? \
				EOF: \
				( (f)->nb++, \
				  (f)->cm=0200, \
				  ((f)->cm&(f)->ic)? \
					((f)->cm>>=1,1): \
					((f)->cm>>=1,0) ) ) )
#define putb_1(b,f) { \
	if((b)) (f)->i.ss.s0.cc.c0 |= (f)->cm; \
	if( !((f)->cm>>=1) ) { \
		bputc((f)->i.ss.s0.cc.c0,f); \
		(f)->nb++; \
		(f)->i.ss.s0.cc.c0=0000; (f)->cm=0200; \
		}; \
	(f)->n++; \
	}
#define bflush_1(f) (padb((f),0,8,0), (bsize(f)>0)? bbflush(f): 0L)

/* Format 10: the low-order (0001) bit in each byte is first ("little-endian"), and
   bytes are reversed (in each pair) from putc(3) order;
 */
#if MAIN
BITFILE *bopen_r_10(s)
    FILE *s;
{   BITFILE *f;
	if((f=bopen_rw(s,"r"))!=NULL) {
		f->sm=0000000;
		};
	return(f);
	}
#else
BITFILE *bopen_r_10();
#endif
#if MAIN
BITFILE *bopen_w_10(s)
    FILE *s;
{   BITFILE *f;
	if((f=bopen_rw(s,"w"))!=NULL) {
		f->i.ss.s0.s=0000000;
		f->sm=0000001;
		};
	return(f);
	}
#else
BITFILE *bopen_w_10();
#endif
#define getb_10(f) ( ((f)->sm)? \
			( ((f)->sm&(f)->i.ss.s0.s)? \
				((f)->sm<<=1,1): \
				((f)->sm<<=1,0) ): \
			( (((f)->ic=getc((f)->fp))==EOF)? \
				EOF: \
				( (f)->nb++, \
				  (f)->i.ss.s0.cc.c1=(f)->ic&0377, \
				  ( (((f)->ic=getc((f)->fp))==EOF)? \
					EOF: \
					( (f)->nb++, \
					  (f)->i.ss.s0.cc.c0=(f)->ic&0377, \
				  	  (f)->sm=0000001, \
				  	  ((f)->sm&(f)->i.ss.s0.s)? \
						((f)->sm<<=1,1): \
						((f)->sm<<=1,0) ) ) ) ) )
#define putb_10(b,f) { \
	if((b)) (f)->i.ss.s0.s |= (f)->sm; \
	if( !((f)->sm<<=1) ) { \
		bputc((f)->i.ss.s0.cc.c1,f); \
		(f)->nb++; \
		bputc((f)->i.ss.s0.cc.c0,f); \
		(f)->nb++; \
		(f)->i.ss.s0.s=0000000; (f)->sm=0000001; \
		}; \
	(f)->n++; \
	}
#define bflush_10(f) (padb((f),0,16,0), (bsize(f)>0)? bbflush(f): 0L)

/* Format 11:  the high-order (0200) bit in each byte is first ("little-endian"),
   and bytes are reversed (in each pair) from putc(3) order.
 */
#if MAIN
BITFILE *bopen_r_11(s)
    FILE *s;
{   BITFILE *f;
	if((f=bopen_rw(s,"r"))!=NULL) {
		f->sm=0000000;
		};
	return(f);
	}
#else
BITFILE *bopen_r_11();
#endif
#if MAIN
BITFILE *bopen_w_11(s)
    FILE *s;
{   BITFILE *f;
	if((f=bopen_rw(s,"w"))!=NULL) {
		f->i.ss.s0.s=0000000;
		f->sm=0100000;
		};
	return(f);
	}
#else
BITFILE *bopen_w_11();
#endif
#define getb_11(f) ( ((f)->sm)? \
			( ((f)->sm&(f)->i.ss.s0.s)? \
				((f)->sm>>=1,1): \
				((f)->sm>>=1,0) ): \
			( (((f)->ic=getc((f)->fp))==EOF)? \
				EOF: \
				( (f)->nb++, \
				  (f)->i.ss.s0.cc.c0=(f)->ic&0377, \
				  ( (((f)->ic=getc((f)->fp))==EOF)? \
					EOF: \
					( (f)->nb++, \
					  (f)->i.ss.s0.cc.c1=(f)->ic&0377, \
				  	  (f)->sm=0100000, \
				  	  ((f)->sm&(f)->i.ss.s0.s)? \
						((f)->sm>>=1,1): \
						((f)->sm>>=1,0) ) ) ) ) )
#define putb_11(b,f) { \
	if((b)) (f)->i.ss.s0.s |= (f)->sm; \
	if( !((f)->sm>>=1) ) { \
		bputc((f)->i.ss.s0.cc.c0,f); \
		(f)->nb++; \
		bputc((f)->i.ss.s0.cc.c1,f); \
		(f)->nb++; \
		(f)->i.ss.s0.s=0000000; (f)->sm=0100000; \
		}; \
	(f)->n++; \
	}
#define bflush_11(f) (padb((f),0,16,0), (bsize(f)>0)? bbflush(f): 0L)

/**************************************************************/
/* FORMAT: may be selected here (input and output may differ) */

/* Input: */
#define bopen_r(s) bopen_r_0((s))
#define getb(f) getb_0(f)
/* Output: */
#define bopen_w(s) bopen_w_0((s))
#define putb(b,f) putb_0((b),(f))
#define bflush(f) bflush_0(f)

/**************************************************************/

/* Code common to all formats: */
#if MAIN
BITFILE *bopen(s,t)
    FILE *s;
    char *t;
{   BITFILE *res;
	if(*(t)=='r') res=bopen_r(s);
	else if(*(t)=='w') {
		res=bopen_w(s);
#if BUFFERED
		if(*(t+1)=='b') {
			res->alloc = BITFILE_incr;
			if((res->buf=(char *)malloc(res->alloc))==NULL)
				abort("bopen: can't alloc buffer");
			res->cp = res->buf;
			};
#endif
		}
	else abort("bopen: bad type: \"%s\"",t);
	return(res);
	}
#else
BITFILE *bopen();
#endif

#if MAIN
padb(f,b,B,l)
    BITFILE *f;
    char b;
    int l,B;
{	while(((f)->n+(l))%(B)) putb((b),(f));
	}
#endif

#if MAIN
unsigned long bclose(f)
    BITFILE *f;
{   unsigned long nb,nbuf;
	if(f->type=='w') {
		nbuf=bflush(f);
		fflush(f->fp);
#if BUFFERED
		if(f->buf!=NULL) { free(f->buf); f->buf=NULL; }
#endif
		};
	nb=f->nb;
	free(f);
	return(nb);
	}
#else
unsigned long bclose();
#endif