/*
 * 			l z c m p 3 . c
 * Output a given code.
 */
#include "lz.h"

extern STREAM	outstream;
extern code_int	next_code;
extern code_int	maxmaxcode;		/* Actual maximum output code	*/
extern short	maxbits;
extern count_int out_count;

static char_type buf[BITS];
static int	offset;
static short	n_bits = INIT_BITS;	/* # of bits in compressed file	*/
static short	n_bits8 = INIT_BITS << 3;
static code_int	maxcode = MAXCODE(INIT_BITS);

#if !vax_asm
static readonly char_type lmask[9] = {
    0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80, 0x00
};
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

outputcode(code)
code_int  code;
/*
 * Output the given code.
 * Inputs:
 * 	code:	A n_bits-bit integer.  If == -1, then EOF.  This assumes
 *		that n_bits <= (long)wordsize - 1.
 * Note: if not in "export" mode, the following values are special:
 *	LZ_CLEAR	(also in export mode if block_compress TRUE)
 *			(soft) clear out compress tables and reset the
 *			number of bits per code to the minimum.
 *	LZ_SOH, LZ_STX	(hard) clear out compress tables and reset as above.
 *	LZ_ETX, LZ_EOR	force out the current output segment, analogous
 *			to fflush.
 *			
 * Outputs:
 * 	Outputs code to the file.  If the codespace has filled
 *	(next_code >= (1 << n_bits), increase n_bits.
 *	If LZ_CLEAR, LZ_SOH, or LZ_STX is seen, reset n_bits
 *	to the initial value and call init_compress to reset
 *	the lookup and cache tables.
 *
 * Assumptions:
 *	Output chars are 8 bits long.  This is deeply hardwired
 *	into the algorithm.  It is independent, however, of the
 *	size of the input data.
 *
 * Algorithm:
 * 	Maintain a BITS character long buffer (so that 8 codes will
 *	fit in it exactly).  Use the VAX insv instruction to insert each
 *	code in turn.  When the buffer fills up empty it and start over.
 */
{
	/*
	 * On the VAX (Unix), it is important to have the register declarations
	 * in exactly the order given, or the asm will break.
	 */
	register int	r_off, bits;
	register char_type	*bp;
#if !vax_asm
	register code_int	r_code;
#endif

	r_off = offset;
	bits = n_bits;
	bp = buf;
	if (code >= 0) {
	    /*
	     * Not at EOF, add the code
	     */
#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
#if vax_asm
	    /*
	     * VAX DEPENDENT!! Implementation on other machines may be
	     * difficult.
	     *
	     * Translation: Insert BITS bits from the argument starting at
	     * offset bits from the beginning of buf.
	     */
	    0;					/* C compiler bug ??	*/
	    asm("insv	4(ap),r11,r10,(r9)");
#else
	    /*
	     * WARNING: byte/bit numbering on the vax is simulated
	     * by the following code
	     */
	    bp += (r_off >> 3);			/* -> first output slot	*/
	    r_off &= 7;
	    /*
	     * Since code is always >= 8 bits, only need to mask the first
	     * hunk on the left.
	     */
	    r_code = code;
	    *bp = (*bp & rmask[r_off]) | (r_code << r_off) & lmask[r_off];
	    bp++;
	    bits -= (8 - r_off);
	    r_code >>= 8 - r_off;
	    /*
	     * Get any 8 bit parts in the middle ( <= 1 for up to 16 bits).
	     */
	    if (bits >= 8) {
		*bp++ = r_code;
		r_code >>= 8;
		bits -= 8;
	    }
	    if (bits != 0)				/* Last bits. */
		*bp = r_code;
#endif
	    offset += n_bits;
	    if (offset == n_bits8) {
		out_count += n_bits;
		lz_putbuf(buf, n_bits, &outstream);
#if DEBUG
		if (todump > 0) {
		    dumphex(buf, n_bits, stderr);
		    todump -= n_bits;
		}
#endif
		offset = 0;
	    }
	    /*
	     * If the next entry is going to be too big for the code size,
	     * then increase it, if possible.  Note:
	     *     !export			firstcode == LZ_FIRST
	     *	   export && block_compress	firstcode == LZ_CLEAR + 1
	     *	   export && !block_compress	firstcode == LZ_CLEAR
	     */
	    if (next_code > maxcode) {
		if (offset > 0) {
		    lz_putbuf(buf, n_bits, &outstream);
		    out_count += n_bits;
		    offset = 0;
#if DEBUG
		    if (todump > 0) {
			dumphex(buf, n_bits, stderr);
			todump -= n_bits;
		    }
#endif
		}
		n_bits++;			/* Need more bits	*/
		n_bits8 += (1 << 3);
		if (n_bits == maxbits)
		    maxcode = maxmaxcode;
		else
		    maxcode = MAXCODE(n_bits);
#if DEBUG
		if (verbose > 2) {
		    fprintf(stderr,
			"%snext_code %d, change to %d bits max %d",
			(col > 0) ? "\n" : "", next_code,
			n_bits, maxcode);
		    col = 74;
		}
#endif
	    }
	    if (code >= LZ_CLEAR && code < firstcode) {
		switch (code) {
		case LZ_SOH:
		case LZ_STX:
		case LZ_CLEAR:
		    if (offset > 0) {
			lz_putbuf(buf, n_bits, &outstream);
			out_count += n_bits;
			offset = 0;
#if DEBUG
			if (todump > 0) {
			    dumphex(buf, n_bits, stderr);
			    todump -= n_bits;
			}
#endif
		    }
		    n_bits = INIT_BITS;		/* Reset codes		*/
		    n_bits8 = INIT_BITS << 3;
		    maxcode = MAXCODE(INIT_BITS);
		    init_compress(code != LZ_CLEAR);
#if DEBUG
		    if (verbose > 2) {
			fprintf(stderr,
		    "\n(%s) Change to %d bits, maxcode %d, next_code = %d",
			    lz_names[code - LZ_CLEAR],
			    n_bits, maxcode, next_code);
			col = 74;
			todump = 32;
		    }
#endif
		    break;

		case LZ_EOR:
		case LZ_ETX:			/* Just written out	*/
		    break;

		default:
		    abort();			/* Can't happen		*/
		}
	    }
	}
	else {
	    /*
	     * At EOF, write the rest of the buffer.
	     */
	    if ((r_off = offset) > 0) {
		r_off += 7;
		r_off >>= 3;
		lz_putbuf(buf, r_off, &outstream);
		out_count += r_off;
#if DEBUG
		if (todump > 0) {
		    dumphex(buf, r_off, stderr);
		    todump -= r_off;
		}
#endif
	    }
	    offset = 0;
	    lz_flush(&outstream);		/* Flush output buffer	*/
#if DEBUG
	    if (verbose > 3 || todump > 0) {
		fprintf(stderr, "\n*EOF*\n");
		col = 0;
	    }
#endif
	}
}
