/*
 *			l z d c m 3 . c
 *
 * Read codes from the input stream.
 */

#include "lz.h"


#if !vax_asm
static readonly char_type rmask[9] = {
	0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF
};
#endif
#if DEBUG
extern int	col;
static int	todump;
#endif

/*
 * getcode()
 *
 * Read one code from the standard input.  If EOF, return -1.
 * Inputs:
 * 	stdin (via GET)
 * Outputs:
 * 	code or -1 is returned.
 */

extern code_int		next_code;
extern STREAM		instream;
extern code_int		maxmaxcode;
extern short		maxbits;
static short		n_bits = INIT_BITS;
static code_int		maxcode = MAXCODE(INIT_BITS);

/*
 * buf[] contains 8-bit data read from the input stream.  getcode()
 * treats buf[] as a vector of bits, repacking it into variable-bit codes.
 */
static char_type	buf[BITS];
static int		offset = 0;	/* Offset into buf    IN BITS 	*/
static int		size = 0;	/* Actual size of buf IN BITS 	*/

code_int
getcode()
{
	/*
	 * On the VAX (4.2 bsd), it is important to have the register
	 * declarations in exactly the order given, or the asm will break.
	 */
	register code_int 	code;
	register int		r_off, bits;
	register char_type	*bp;

	bp = buf;
	if (next_code > maxcode) {
	    n_bits++;
	    if (n_bits == maxbits)
		maxcode = maxmaxcode;
	    else {
		maxcode = MAXCODE(n_bits);
	    }
	    size = 0;
#if DEBUG
	    if (verbose > 2) {
		fprintf(stderr, "\nChange to %d bits", n_bits);
		col = 74;
	    }
#endif
	}
	if (offset >= size) {
	    size = lz_getbuf(buf, n_bits, &instream);
#if DEBUG
	    if (verbose > 4 || todump > 0) {
		dumphex(buf, size, stderr);
		if (todump > 0)
		    todump -= size;
	    }
#endif
	    if (size <= 0)
		return (-1);			/* end of file		*/
	    offset = 0;
	    /*
	     * Round size down to integral number of codes in the buffer.
	     * (Expressed as a number of bits).
	     */
	    size = (size << 3) - (n_bits - 1);
	}
	r_off = offset;
	bits = n_bits;
#if vax_asm
	asm("extzv   r10,r9,(r8),r11");
#else
	/*
	 * Get to the first byte.
	 */
	bp += (r_off >> 3);
	r_off &= 7;
	/*
	 * Get first part (low order bits)
	 */
#if UCHAR
	code = (*bp++ >> r_off);
#else
	/*
	 * Don't touch the 0xFF; it prevents sign extension.
	 */
	code = ((*bp++ >> r_off) & rmask[8 - r_off]) & 0xFF;
#endif
	bits -= (8 - r_off);
	r_off = 8 - r_off;		/* now, offset into code word	*/
	/*
	 * Get any 8 bit parts in the middle (<=1 for up to 16 bits).
	 */
	if (bits >= 8) {
#if UCHAR
	    code |= *bp++ << r_off;
#else
	    code |= (*bp++ & 0xFF) << r_off;
#endif
	    r_off += 8;
	    bits -= 8;
	}
	/* high order bits. */
#if UCHAR
	code |= (*bp & rmask[bits]) << r_off;
#else
	code |= (*bp & rmask[bits]) << r_off;
#endif
	/*
	 * End of non-vax (Unix) specific code.
	 */
#endif
	offset += n_bits;
	if (code >= LZ_CLEAR && code < firstcode) {
	    switch (code) {
	    case LZ_SOH:
	    case LZ_STX:
	    case LZ_CLEAR:
		size = 0;			/* Force read next time	*/
		n_bits = INIT_BITS;
		maxcode = MAXCODE(INIT_BITS);
#if DEBUG
		if (verbose > 1) {
		    fprintf(stderr, "Read %s (%d)\n",
			lz_names[code - LZ_CLEAR], code);
		    todump = 32;
		}
#endif
		break;
	    }
	}
#if DEBUG
	if (verbose > 3) {
	    fprintf(stderr, "%c%5d %5d",
		((col += 12) >= 72) ? (col = 0, '\n') : ' ',
		code, next_code);
	    if (code >= LZ_CLEAR && code < firstcode) {
		fprintf(stderr, " = %s", lz_names[code - LZ_CLEAR]);
	        col = 74;
	    }
	}
#endif
	return (code);
}

