/* Copyright (C) 1994, 1995, 1996, 1998 artofcode LLC.  All rights reserved.
  
  This program is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 2 of the License, or (at your
  option) any later version.

  This program is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.

  You should have received a copy of the GNU General Public License along
  with this program; if not, write to the Free Software Foundation, Inc.,
  59 Temple Place, Suite 330, Boston, MA, 02111-1307.

*/

/*$Id: sbhc.c,v 1.3.4.1.2.1 2003/01/17 00:49:05 giles Exp $ */
/* Bounded Huffman code filters */
#include "memory_.h"
#include "stdio_.h"
#include "gdebug.h"
#include "strimpl.h"
#include "sbhc.h"
#include "shcgen.h"

/* ------ BoundedHuffmanEncode ------ */

private_st_BHCE_state();

/* Initialize BoundedHuffmanEncode filter. */
private int
s_BHCE_reinit(stream_state * st)
{
    stream_BHCE_state *const ss = (stream_BHCE_state *) st;

    ss->encode.count = ss->definition.num_values;
    s_bhce_init_inline(ss);
    return 0;
}
private int
s_BHCE_init(register stream_state * st)
{
    stream_BHCE_state *const ss = (stream_BHCE_state *) st;
    hce_code *encode = ss->encode.codes =
    (hce_code *) gs_alloc_byte_array(st->memory,
				     ss->definition.num_values,
				     sizeof(hce_code), "BHCE encode");

    if (encode == 0)
	return ERRC;
/****** WRONG ******/
    hc_make_encoding(encode, &ss->definition);
    return s_BHCE_reinit(st);
}

/* Release the filter. */
private void
s_BHCE_release(stream_state * st)
{
    stream_BHCE_state *const ss = (stream_BHCE_state *) st;

    gs_free_object(st->memory, ss->encode.codes, "BHCE encode");
}

/* Process a buffer. */
private int
s_BHCE_process(stream_state * st, stream_cursor_read * pr,
	       stream_cursor_write * pw, bool last)
{
    stream_BHCE_state *const ss = (stream_BHCE_state *) st;
    const byte *p = pr->ptr;
    const byte *rlimit = pr->limit;
    byte *q = pw->ptr;
    byte *wlimit = pw->limit - (hc_bits_size >> 3);
    const hce_code *encode = ss->encode.codes;
    uint num_values = ss->definition.num_values;
    uint zero_runs = ss->EncodeZeroRuns;
    uint zero_max = num_values - zero_runs + (ss->EndOfData ? 0 : 1);
    uint zero_value = (zero_max > 1 ? 0 : 0x100);
    int zeros = ss->zeros;
    int status = 0;

    hce_declare_state;

    hce_load_state();
    while (p < rlimit && q < wlimit) {
	uint value = *++p;
	const hce_code *cp;

	if (value >= num_values) {
	    status = ERRC;
	    break;
	}
	if (value == zero_value) {	/* Accumulate a run of zeros. */
	    ++zeros;
	    if (zeros != zero_max)
		continue;
	    /* We've scanned the longest run we can encode. */
	    cp = &encode[zeros - 2 + zero_runs];
	    zeros = 0;
	    hc_put_code((stream_hc_state *) ss, q, cp);
	    continue;
	}
	/* Check whether we need to put out a zero run. */
	if (zeros > 0) {
	    --p;
	    cp = (zeros == 1 ? &encode[0] :
		  &encode[zeros - 2 + zero_runs]);
	    zeros = 0;
	    hc_put_code((stream_hc_state *) ss, q, cp);
	    continue;
	}
	cp = &encode[value];
	hc_put_code((stream_hc_state *) ss, q, cp);
    }
    if (q >= wlimit)
	status = 1;
    wlimit = pw->limit;
    if (last && status == 0) {
	if (zeros > 0) {	/* Put out a final run of zeros. */
	    const hce_code *cp = (zeros == 1 ? &encode[0] :
				  &encode[zeros - 2 + zero_runs]);

	    if (!hce_bits_available(cp->code_length))
		status = 1;
	    else {
		hc_put_code((stream_hc_state *) ss, q, cp);
		zeros = 0;
	    }
	}
	if (ss->EndOfData) {	/* Put out the EOD code if we have room. */
	    const hce_code *cp = &encode[num_values - 1];

	    if (!hce_bits_available(cp->code_length))
		status = 1;
	    else
		hc_put_code((stream_hc_state *) ss, q, cp);
	} else {
	    if (q >= wlimit)
		status = 1;
	}
	if (!status) {
	    q = hc_put_last_bits((stream_hc_state *) ss, q);
	    goto ns;
	}
    }
    hce_store_state();
  ns:pr->ptr = p;
    pw->ptr = q;
    ss->zeros = zeros;
    return (p == rlimit ? 0 : 1);
}

/* Stream template */
const stream_template s_BHCE_template =
{&st_BHCE_state, s_BHCE_init, s_BHCE_process,
 1, hc_bits_size >> 3, s_BHCE_release, NULL, s_BHCE_reinit
};

/* ------ BoundedHuffmanDecode ------ */

private_st_BHCD_state();

#define hcd_initial_bits 7	/* arbitrary, >= 1 and <= 8 */

/* Initialize BoundedHuffmanDecode filter. */
private int
s_BHCD_reinit(stream_state * st)
{
    stream_BHCD_state *const ss = (stream_BHCD_state *) st;

    ss->decode.count = ss->definition.num_values;
    s_bhcd_init_inline(ss);
    return 0;
}
private int
s_BHCD_init(register stream_state * st)
{
    stream_BHCD_state *const ss = (stream_BHCD_state *) st;
    uint initial_bits = ss->decode.initial_bits =
    min(hcd_initial_bits, ss->definition.num_counts);
    uint dsize = hc_sizeof_decoding(&ss->definition, initial_bits);
    hcd_code *decode = ss->decode.codes =
	(hcd_code *) gs_alloc_byte_array(st->memory, dsize,
					 sizeof(hcd_code), "BHCD decode");

    if (decode == 0)
	return ERRC;
/****** WRONG ******/
    hc_make_decoding(decode, &ss->definition, initial_bits);
    st->min_left = 1;
    return s_BHCD_reinit(st);
}

/* Release the filter. */
private void
s_BHCD_release(stream_state * st)
{
    stream_BHCD_state *const ss = (stream_BHCD_state *) st;

    gs_free_object(st->memory, ss->decode.codes, "BHCD decode");
}

/* Process a buffer. */
private int
s_BHCD_process(stream_state * st, stream_cursor_read * pr,
	       stream_cursor_write * pw, bool last)
{
    stream_BHCD_state *const ss = (stream_BHCD_state *) st;

    bhcd_declare_state;
    byte *q = pw->ptr;
    byte *wlimit = pw->limit;
    const hcd_code *decode = ss->decode.codes;
    uint initial_bits = ss->decode.initial_bits;
    uint zero_runs = ss->EncodeZeroRuns;
    int status = 0;
    int eod = (ss->EndOfData ? ss->definition.num_values - 1 : -1);

    bhcd_load_state();
  z:for (; zeros > 0; --zeros) {
	if (q >= wlimit) {
	    status = 1;
	    goto out;
	}
	*++q = 0;
    }
    for (;;) {
	const hcd_code *cp;
	int clen;

	hcd_ensure_bits(initial_bits, x1);
	cp = &decode[hcd_peek_var_bits(initial_bits)];
      w1:if (q >= wlimit) {
	    status = 1;
	    break;
	}
	if ((clen = cp->code_length) > initial_bits) {
	    if (!hcd_bits_available(clen)) {	/* We don't have enough bits for */
		/* all possible codes that begin this way, */
		/* but we might have enough for */
		/* the next code. */
/****** NOT IMPLEMENTED YET ******/
		break;
	    }
	    clen -= initial_bits;
	    hcd_skip_bits(initial_bits);
	    hcd_ensure_bits(clen, out);		/* can't exit */
	    cp = &decode[cp->value + hcd_peek_var_bits(clen)];
	    hcd_skip_bits(cp->code_length);
	} else {
	    hcd_skip_bits(clen);
	}
	if (cp->value >= zero_runs) {
	    if (cp->value == eod) {
		status = EOFC;
		goto out;
	    }
	    /* This code represents a run of zeros, */
	    /* not a single output value. */
	    zeros = cp->value - zero_runs + 2;
	    goto z;
	}
	*++q = cp->value;
	continue;
	/* We don't have enough bits for all possible */
	/* codes, but we might have enough for */
	/* the next code. */
      x1:cp = &decode[(bits & ((1 << bits_left) - 1)) <<
		     (initial_bits - bits_left)];
	if ((clen = cp->code_length) <= bits_left)
	    goto w1;
	break;
    }
  out:bhcd_store_state();
    pw->ptr = q;
    return status;
}

/* Stream template */
const stream_template s_BHCD_template =
{&st_BHCD_state, s_BHCD_init, s_BHCD_process, 1, 1, s_BHCD_release,
 NULL, s_BHCD_reinit
};
