PackIt bsp.c

/*		Bit-stream Pipes

	This module contains the routines for implementing bit-stream pipes.

	Initial coding 4/9/86 by Harry R. Chesley.

	© Copyright 1986 by Harry R. Chesley.
	All rights reserved.
*/

#include <types.h>
#include <memory.h>
#include "bsp.h"

#define NIL 0L

static BspPtr InBsp;	/* The current ingoing bit-stream. */
static BspPtr OutBsp;	/* The current outgoing bit-stream. */

/*	NewBsp(bStorage,bbPtr,bbSz,gFunc,getBsp)

	Function: Set up a new bit-stream pipe. If bStorage is non-NIL, it's a pointer to the storage to
	use for the pipe; if it is NIL, the storage should be allocated from the heap. bbPtr is a pointer
	to the block to use for the bit-stream; the block is bbSz bytes long. If bbPtr is NIL, the block
	should be allocated from the heap. gFunc is a function to call when the bit-stream data is
	exhausted, and getBsp is a pointer to another bit-stream to be passed to gFunc().

	Algorithm: Allocate and fill in the parts.

	Comments: It would be interesting to have an alternative new function that took its configuration
	information from the resource file.
*/

BspPtr NewBsp(bStorage,bbPtr,bbSz,gFunc,getBsp)

register BspPtr bStorage;
Handle bbPtr;
long bbSz;
int (*gFunc)();
BspPtr getBsp;

{
	if (bStorage == NIL) {
		if ((bStorage = NewPtr((long) sizeof(Bsp))) == NIL) return(NIL);
		bStorage->storAllo = TRUE;
	} else bStorage->storAllo = FALSE;

	if ((bStorage->block = bbPtr) == NIL) {
		if ((bStorage->block = NewPtr(bbSz)) == NIL) return(NIL);
		bStorage->blockAllo = TRUE;
	} else bStorage->blockAllo = FALSE;
	bStorage->endBlock = bStorage->block + bbSz - 1;
	bStorage->inPtr = bStorage->outPtr = bStorage->block - 1;
	bStorage->inCnt = bStorage->outCnt = 0;
	bStorage->eos = FALSE;
	bStorage->getFunc = gFunc;
	bStorage->fromBsp = getBsp;
	bStorage->margin = bStorage->endBlock - ((long) (*gFunc)(INITBSP,bStorage,getBsp));
}

/*	DisposeBsp(bPtr)

	Function: Dispose all allocated data areas associated with the BSP bPtr.

	Algorithm: If we allocated the bit-stream block  or BSP storage, free it. Otherwise, do nothing.

	Comments: None.
*/

DisposeBsp(bPtr)

BspPtr bPtr;

{
	if (bPtr->blockAllo) DisposPtr(bPtr->block);
	if (bPtr->storAllo) DisposPtr(bPtr);
}

/*	RewindBsp(bPtr)

	Function: Reset the bit-stream to the beginning.

	Algorithm: Get rid of anything in the bit-stream at the moment, and call the bspFunc() with a
	rewind command.

	Comments: None.
*/

RewindBsp(bPtr)

BspPtr bPtr;

{
	bPtr->inPtr = bPtr->outPtr = bPtr->block - 1;
	bPtr->inCnt = bPtr->outCnt = 0;
	bPtr->eos = FALSE;
	(*bPtr->getFunc)(REWINDBSP,bPtr,bPtr->fromBsp);
}

/*	SetBsp(in,out)

	Function: Set the current ingoing and outgoing BSPs.

	Algorithm: Just remember the two BSP pointers for later.

	Comments: None.
*/

SetBsp(in,out)

BspPtr in;
BspPtr out;

{
	InBsp = in;
	OutBsp = out;
}

/*	Encoding

	Function: BitEncode(), ByteEncode(), and BlkEncode() encode bits, bytes, and blocks onto the
	current outgoing BSP. BlkToEncode() returns the address and size of the unused portion of
	the bit-stream block (or a NIL address and zero size if it isn't byte alligned). SizeEncoded()
	can then be used to specify how many of the bytes where actually used. BrakesOn() returns
	TRUE if the current outgoing BSP is close to full.

	Algorithm: Simple...

	Comments: None.
*/

BitEncode(bit)

int bit;

{
	register BspPtr bp;

	bp = OutBsp;

	if (bp->inCnt == 0) {
		if (bp->inPtr >= bp->endBlock) fatalError("\PBSP out of space on output!");
		*(++(bp->inPtr)) = 0;
		bp->inCnt = 8;
	};
	*(bp->inPtr) |= (bit & 1) << (--(bp->inCnt));
}

ByteEncode(byte)

register unsigned byte;

{
	register BspPtr bp;

	bp = OutBsp;

	if (bp->inCnt == 0) {
		if (bp->inPtr >= bp->endBlock) fatalError("\PBSP out of space on output!");
		*(++(bp->inPtr)) = byte;
	} else {
		BitEncode(byte >> 7);
		BitEncode(byte >> 6);
		BitEncode(byte >> 5);
		BitEncode(byte >> 4);
		BitEncode(byte >> 3);
		BitEncode(byte >> 2);
		BitEncode(byte >> 1);
		BitEncode(byte);
	};
}

BlkEncode(blk,sz)

char *blk;
long sz;

{
	register BspPtr bp;
	register char *cptr;
	register long cnt;

	bp = OutBsp;

	if (bp->inCnt == 0) {
		if ((bp->endBlock - bp->inPtr + 1L) < sz) fatalError("\PBSP out of space on output!");
		BlockMove(blk,bp->inPtr,sz);
		bp->inPtr += sz;
	} else for (cptr = blk, cnt = sz; sz--; ByteEncode(cptr++));
}

BlkToEncode(blk,sz)

char **blk;
long *sz;

{
	register BspPtr bp;

	bp = OutBsp;

	if (bp->inCnt == 0) {
		*blk = bp->inPtr + 1;
		*sz = bp->endBlock - bp->inPtr;
	} else {
		*blk = NIL;
		*sz = 0L;
	};
}

SizeEncoded(sz)

long sz;

{
	OutBsp->inPtr += sz;
}

BrakesOn()

{
	register BspPtr bp;

	bp = OutBsp;
	return(bp->inPtr >= bp->margin);
}

/*	Decoding

	Function: BitDecode(), ByteDecode(), and BlkDecode() decode bits, bytes, and blocks from the
	current incoming BSP. ByteAllign() skips bits until the incoming BSP is at a byte boundary.

	Algorithm: Simple...

	Comments: None.
*/

BitDecode()

{
	register BspPtr bp;

	bp = InBsp;

	if (bp->outCnt == 0) {
		if (bp->outPtr >= (bp->inPtr - 1)) {
			BspRead();
			if ((bp->outPtr >= bp->inPtr) && (bp->outCnt <= bp->inCnt)) return(-1);
		};
		bp->outPtr++;
		bp->outCnt = 8;
	};
	return((*(bp->outPtr) >> (--(bp->outCnt))) & 1);
}

ByteDecode()

{
	register BspPtr bp;
	register unsigned res;

	bp = InBsp;

	if (bp->outCnt == 0) {
		if (bp->outPtr >= (bp->inPtr - 1)) {
			BspRead();
			if ((bp->outPtr >= bp->inPtr) && (bp->outCnt <= bp->inCnt)) return(-1);
		};
		return (*(++(bp->outPtr)) & 0xFF);
	} else {
		res = BitDecode() << 7;
		res |= BitDecode() << 6;
		res |= BitDecode() << 5;
		res |= BitDecode() << 4;
		res |= BitDecode() << 3;
		res |= BitDecode() << 2;
		res |= BitDecode() << 1;
		res |= BitDecode();
		return(res & 0xFF);
	};
}

long BlkDecode(blk,sz)

char *blk;
long sz;

{
	register BspPtr bp;
	register char *ptr;
	register long cnt;
	register long trSz;

	bp = InBsp;

	ptr = blk; cnt = sz;
	if (bp->outCnt != 0) {
		while (cnt--) if ((*ptr++ = ByteDecode()) < 0) break;
		return(cnt+1L);
	} else {
		while (sz) {
			if (bp->outPtr >= (bp->inPtr - 1)) {
				BspRead();
				if ((bp->outPtr >= bp->inPtr) && (bp->outCnt <= bp->inCnt)) return(sz);
			};
			trSz = (sz <= (bp->inPtr - bp->outPtr - 1L)) ? sz : (bp->inPtr - bp->outPtr - 1L);
			if (bp->eos && (trSz == 0) && (bp->outCnt > bp->inCnt)) trSz++;
			if (trSz) {
				BlockMove(bp->outPtr+1,ptr,trSz);
				sz -= trSz;
				ptr += trSz;
				bp->outPtr += trSz;
			};
		};
	};
}

ByteAllign()

{
	register BspPtr bp;

	bp = InBsp;
	while (bp->outCnt != 0) BitDecode();
}

static BspRead()

{
	register BspPtr bp;
	register long sz;
	BspPtr oldIn, oldOut;

	bp = InBsp;

	if ((! bp->eos) && (bp->getFunc != NIL)) {
		if ((bp->outPtr > bp->block) && (bp->inPtr >= bp->outPtr)) {
			BlockMove(bp->outPtr,bp->block,sz = bp->inPtr - bp->outPtr + 1L);
			bp->inPtr -= sz; bp->outPtr -= sz;
		};
		oldIn = InBsp; oldOut = OutBsp;
		InBsp = bp->fromBsp; OutBsp = bp;
		bp->eos = (*bp->getFunc)(STUFFBSP,bp,bp->fromBsp);
		InBsp = oldIn; OutBsp = oldOut;
	};
}