
/* BITOPS.C - Various bit-manipulation procedures.
 *
 *	Audit Trail
 *
 *	09/11/87 - v001 - GSK - Original
 */

#include "ctype.h"

#define FULLBYT 0xff
#define TBUFSIZ 100
#define SUCCESS	0
#define FAILURE	-1
#define NULL (char *)0

typedef unsigned BOOL;

/* *************************** GENERAL NOTES *****************************
 *
 *	Most of these routines deal with "bit fields" of arbitrary
 *	length. In general, you pass them a (char) pointer to a block of memory,
 * 	which will be interpreted and manipulated as a stream of bits, regardless
 *	of byte or word boundaries after the original pointer. Several of these 
 *	routines take a bit offset argument, which is an ABSOLUTE number of bits
 *	(zero-relative) from the base pointer; in other words, a bit offset of
 *	75 points to the 76th bit in the block, starting from the base pointer.
 *
 ****************************************************************************/


/* GETBIT - Get the value of the bit (0, 1) at passed ptr, offset by bitoffs
 *			bits. The offset may be any positive number; if >8, the byte 
 *			pointer will be incremented accordingly.
 */

short getbit(ptr, bitoffs)

char *ptr;		/* Base pointer */
short bitoffs;	/* Offset from ptr in number of bits */

{

	if (ptr == NULL || bitoffs < 0)
	    return (FAILURE);

	ptr += (bitoffs / 8);	/* Knock up ptr to byte requested */
	bitoffs %= 8;		/* Get bit relative to its own byte */

	return (short)((*ptr >> (7 - bitoffs)) & 1);
}


/* SETBIT - Set a specified bit number in a char string to 1 or 0. The bit
 *			number may be any positive value; if > 8, the byte pointer will
 *			be incremented accordingly.
 */

short setbit(ptr, bitnum, val)

char *ptr;		/* Base of string */
short bitnum;	/* Bit number, 0 relative */
short val;		/* Value to set, 1 or 0 */

{
	short byteoffs;		/* Byte offset in string */
	char mask;		/* Masking value */


	if (ptr == NULL || bitnum < 0 || (val != 0 && val != 1))
	    return (FAILURE);

	ptr += (byteoffs = bitnum / 8);	/* Calc offset, reset ptr */

	if (byteoffs)
	    bitnum %= (byteoffs * 8);	/* Find bit within byte */

/* Mask = bit string of zeroes with one in bitnum position */

 	mask = 1 << (7 - bitnum);

	if (val)			/* If turn-on bit, OR with mask */
	    *ptr |= mask;
	else				/* Else, AND with inverse (ones-complement) of mask */
	    *ptr &= ~mask;

	return (SUCCESS);
}


/* COUNTBIT - Count the bits of specified value (0 or 1) in a bit field. The
 *				field may be any number of bytes in length. If val==0, then
 *				"off" bits will be counted; otherwise, "on" bits.
 */

short countbit(basep, nbytes, val)

char *basep;	/* Starting byte to begin count */
short nbytes;	/* Number of bytes to count thru */
short val;		/* Bit value to count, 0 or 1 */

{
	short count;	/* Number of bits counted */
	short bit;	/* Bit loop counter */
	char *ptr;	/* Loop pointer */


	if (basep == NULL || nbytes <= 0 || (val != 0 && val != 1))
	    return (FAILURE);

	count = 0;

	for (ptr = basep; ptr < basep + nbytes; ptr++)
		{
	    for (bit = 0; bit < 8; bit++)
		count += (getbit(ptr, bit) == val);
		}

	return (count);
}


/* BITPATRN -  Generate an array of chars or ints corresponding to the bit
 *				pattern of the passed char. If patyp == 'C', an array of
 *				character '0's and '1's will be generated; if 'I', an array
 *				numeric 0/1's.
 *
 *			NOTE that you pass a single CHAR, not a char *. Thus if you
 *				need a pattern array more than 8 elements long, call
 *				bitpatrn() in a loop and concatenate the results.
 */

short bitpatrn(chr, patp, patyp)

char chr;	/* Char to generate pattern from */
char *patp;	/* Ptr to array of chars or ints, depending on patyp */
char patyp;	/* Pattern type: Char or Int (NOT case sensitive) */

{
	short ptrinc;	/* Pointer increment per bit */
	short bit;		/* Bit counter */
	short bitval;	/* Bit value, 1 or 0 */


	patyp = toupper(patyp);

	if (patp == NULL || (patyp != 'C' && patyp != 'I'))
	    return (FAILURE);

	ptrinc = 1 + (patyp == 'I');

	for (bit = 0; bit < 8; bit++, patp += ptrinc)
		{
	    bitval = getbit(&chr, bit);

	    if (patyp == 'I')	/* Int array: Set low bytes of each pair */
			{
			*patp = bitval;
			*(patp + 1) = 0;
	    	}
	    else
			*patp = '0' + bitval;
		}
	return (SUCCESS);
}


/* BYTE2BIT -  Translate the byte passed to a bit pattern, placed at char
 *				*ptr, offset by bitoffs bits from left. Do not write onto next
 *				byte from ptr if lastbyt set. This allows a bit pattern to be
 *				copied to another place in memory regardless of byte
 *				boundaries. For example, if bitoffs == 3, then the left 5 bits
 *				of the passed byte will be copied to the right 5 bits of the
 *				destination ptr, and the right 3 bits of the byte onto the
 *				left 3 bits of the following location (unless lastbyt == YES),
 *				in which case only the FIRST dest char will be written on).
 *				Issuing a call with a ZERO bit offset, like:
 *
 *					byte2bit('A', dest, 0, anything);
 *
 *				is equivalent to: *dest = 'A';
 */

short byte2bit(byte, ptr, bitoffs, lastbyt)

char byte;		/* Character to translate */
char *ptr;		/* DESTINATION Ptr: Points to starting byte to write to */
short bitoffs;	/* Offset of starting bit from DESTINATION ptr, 0-7 */
BOOL lastbyt;	/* Flag: Do not write bits to next char if set */

{
	short bit;	/* Bit counter */


	if (ptr == NULL || bitoffs < 0 || bitoffs > 7)
	    return (FAILURE);

/* Write left side of passed byte to right side of 1st destination char, 
   leaving left side of dest char unchanged */

	for (bit = bitoffs; bit <= 7; bit++)
	    setbit(ptr, bit, getbit(&byte, bit - bitoffs));

/* Write right side of byte to left side of next destination char, 
   leaving right side unchanged */

	if (bitoffs && !lastbyt)
	    for (bit = 0; bit < bitoffs; bit++)
			setbit(ptr + 1, bit, getbit(&byte, bit + 8 - bitoffs));

	return (SUCCESS);
}


/* BIT2BYTE.C - Take next 8 bits from starting char *, offset by specified
 *				number of bits (bitoffs), translate to a byte and return
 *				value as a char. Starting point need not conform to a byte
 *				boundary. For example, if bitoffs = 3, then translation will
 *				begin with the 4th bit (offsets are 0-relative) of the passed
 *				ptr; if lastbyt == YES, then only the remaining 5 bits will
 *				be translated (offsets 3 thru 7); otherwise, a full 8 bits
 *				will be translated, the remaining 3 coming from the left 3
 *				bits of the byte following ptr. Issuing a call with a ZERO
 *				bit offset like:
 *
 *					mychar = bit2byte(there, 0, anything);
 *
 *				is equivalent to: mychar = *there;
 */

char bit2byte(ptr, bitoffs, lastbyt)

char *ptr;		/* Ptr to starting byte to take from */
short bitoffs;	/* Offset of starting bit from ptr, 0-7 */
BOOL lastbyt;	/* Flag: Do not take bits from next char if set */

{
	char byte;


	if (ptr == NULL || bitoffs < 0 || bitoffs > 7)
	    return (0);

	byte = *ptr << bitoffs;		/* Set left side of byte */

	if (bitoffs && !lastbyt)	/* Set right side from next byte */

	    byte |= (*(++ptr) >> (8 - bitoffs));

	return (byte);
}


/* INSBITS - Insert specified number of bits to bit field beginning at basep,
 *				starting insertion bitoffs bits from the base. Inserted bits
 *				will be all of the same	value (0 or 1). Bits above the
 *				insertion point will be pushed up in memory, up the limit of
 *				total field size fldsize (arbitrary size limit defined by
 *				TBUFSIZ).
 *
 *			NOTE: If you need a routine to insert a specific bit pattern
 *				(not just all 0's or 1's), use this as a template. You only
 *				need to change the "fill middle bytes" section.
 */

short insbits(basep, bitoffs, nbits, val, fldsize)

char *basep;	/* Ptr to base of bit field */
short bitoffs;	/* Number of bits offset from left to begin insert */
short nbits;	/* Number of bits to insert */
short val;		/* Value to insert, 1 or 0 */
short fldsize;	/* Total size of bit field in bytes */

{
	char buffer[TBUFSIZ];		/* Temporary copy buffer */
	char *firstbyt, *lastbyt;	/* Bytes where insert begins/ends */
	short firstoffs, lastoffs;	/* Bit offsets in first, last bytes */
	short n;					/* Field index counter */
	short lastbit;				/* Offset of last bit in field */
	char *ptr;					/* Temporary ptr */


	if (basep == NULL || nbits <= 0 || fldsize <= 0 || fldsize > TBUFSIZ)
	    return (FAILURE);

	firstbyt = basep + bitoffs / 8;
	lastbit = bitoffs + nbits - 1;
	lastbyt = basep + lastbit / 8;
	firstoffs = bitoffs % 8;
	lastoffs = lastbit % 8;

	if (firstbyt >= basep + fldsize)
	    return (FAILURE);

	setmem(buffer, sizeof(buffer), 0);	/* Init copy buffer */

/* Copy from firstbyt thru end of field to buffer */

	for (ptr = firstbyt, n = 0; ptr < basep + fldsize; ptr++, n++)
	    buffer[n] = bit2byte(ptr, firstoffs, ptr == basep + fldsize - 1);

/* Set the starting byte, leaving left part as is */

	if (val)
	    *firstbyt |= FULLBYT >> firstoffs;
	else
	    *firstbyt &= FULLBYT << (8 - firstoffs);

/* Fill middle bytes; do not overwrite end of field */

	for (ptr = firstbyt + 1; ptr < lastbyt; ptr++)
		{
	    if (ptr >= basep + fldsize)
			break;

		*ptr = (val) ? FULLBYT : 0;
		}

/* Set ending byte, leaving right part as is */

	if (lastbyt < basep + fldsize && lastbyt != firstbyt)
		{
	    if (val)
	    	*lastbyt |= FULLBYT << (7 - lastoffs);
	    else
	    	*lastbyt &= FULLBYT >> (lastoffs + 1);
		}

/* Write buffer from last byte to end of field */

	for (ptr = lastbyt, n = 0; ptr < basep + fldsize; ptr++, n++)
	    byte2bit(buffer[n], ptr, lastoffs + 1, ptr == basep + fldsize - 1);

	return (SUCCESS);
}


/* DELBITS.C - Delete specified number of bits from bit field beginning at
 *				basep offset by bitoffs bits. The bit field may be any number
 *				of bytes in length, and the deleted section may be any number
 *				of bits in length and need not conform to byte boundaries. The
 *				bits above the deleted section are moved down to fill the
 *				gap, and the top of the field filled with the specified value.
 *
 *		NOTE: Bit field size fldsize is arbitrarily limited to TBUFSIZ as
 *				defined below.
 */

short delbits(basep, bitoffs, nbits, val, fldsize)

char *basep;	/* Ptr to base of bit field */
short bitoffs;	/* Number of bits offset from left to begin delete */
short nbits;	/* Number of bits to delete */
short val;		/* Value to fill vacated right side of field, 1 or 0 */
short fldsize;	/* Total size of bit field in BYTES */

{
	char buffer[TBUFSIZ];		/* Temporary copy buffer */
	char *firstbyt, *lastbyt;	/* Bytes where delete begins/ends */
	short firstoffs, lastoffs;	/* Bit offsets in first, last bytes */
	short n;					/* Field index counter */
	short lastbit;				/* Offset of last bit in field */
	char *ptr;					/* Temporary ptr */


	if (basep == NULL || nbits <= 0 || fldsize <= 0 || fldsize > TBUFSIZ)
	    return (FAILURE);

	firstbyt = basep + bitoffs / 8;
	lastbit = bitoffs + nbits - 1;
	lastbyt = basep + lastbit / 8;
	firstoffs = bitoffs % 8;
	lastoffs = lastbit % 8;

	if (firstbyt >= basep + fldsize)
	    return (FAILURE);

	if (val)
	    setmem(buffer, sizeof(buffer), FULLBYT);	/* Init copy buffer */
	else
	    setmem(buffer, sizeof(buffer), 0);

/* Copy to buffer: From bit after end of deleted section to end of field */

	if (++lastoffs > 7)
		{
	    lastbyt++;
	    lastoffs = 0;
		}

	for (ptr = lastbyt, n = 0; ptr < basep + fldsize; ptr++, n++)
	    buffer[n] = bit2byte(ptr, lastoffs, ptr == basep + fldsize - 1);

/* Write buffer to deleted part thru end of field; end is filled with val */

	for (ptr = firstbyt, n = 0; ptr < basep + fldsize; ptr++, n++)
	    byte2bit(buffer[n], ptr, firstoffs, ptr == basep + fldsize - 1);

	return (SUCCESS);
}


/* BITZVALU - Evaluate the next nbits bits from the starting char * and bit
 *				offset and return the value. Uses a simple counting algorithm,
 *				starting with the rightmost bit in the designated field and
 *				moving left, adding a successive power of 2 for each "on" bit.
 *
 *			EXAMPLES: Given a pointer to the array of hex values: 1E A4 7C
 *
 *						bitzvalu(ptr, 8, 8)		returns 164 (00A4)
 *						bitzvalu(ptr, 3, 4)		returns  15 (000F)
 *						bitzvalu(ptr, 10, 10)	returns 583 (0247)
 *
 *		NOTE: Returns an unsigned value. The number of bits evaluated may not
 *			  be more than 16. If bad parameters are passed, returns 0.
 */

unsigned bitzvalu(ptr, bitoffs, nbits)

char *ptr;		/* Ptr to starting byte to take from */
short bitoffs;	/* Offset of starting bit from ptr */
short nbits;	/* Number of bits to evaluate */
{
	unsigned num = 0;	/* Initialize all bits to 0 */
	int bit;
	unsigned mult = 1;

	if (ptr == NULL || nbits < 1 || nbits > 16)
		return (0);

	for (bit = nbits - 1; bit >= 0; bit--, mult *= 2)
		num += (getbit(ptr, bitoffs + bit) * mult);

	return (num);
}


/* CONTRIBUTED BY: Gordon Kramer, Liberty Tree Software, Belchertown, MA */
