/*
 * decalign.c
 *
 * Decoding aligned offset block
 */
#include "decoder.h"


static long special_decode_aligned_block(t_decoder_context *context, long bufpos, int amount_to_decode)
{
	ulong	match_pos;
	ulong	temp_pos;
	long	bufpos_end;
	int		match_length;
	int		c;
	ulong	dec_bitbuf;
	char	dec_bitcount;
    byte   *dec_input_curpos;
    byte   *dec_end_input_pos;
	byte   *dec_mem_window;
	char	m;

	/*
	 * Store commonly used variables locally
	 */
	dec_bitcount	  = context->dec_bitcount;
	dec_bitbuf		  = context->dec_bitbuf;
	dec_input_curpos  = context->dec_input_curpos;
	dec_end_input_pos = context->dec_end_input_pos;
	dec_mem_window	  = context->dec_mem_window;

	bufpos_end = bufpos + amount_to_decode;

	while (bufpos < bufpos_end)
	{
		/*
		 * Decode an item
		 */
		DECODE_MAIN_TREE(c);

		if ((c -= NUM_CHARS) < 0)
		{
			dec_mem_window[bufpos] = (byte) c;
			dec_mem_window[context->dec_window_size + bufpos] = (byte) c;
			bufpos++;
		}
		else
		{
	 		/*
	  		 * Get match length slot
	  		 */
			if ((match_length = c & NUM_PRIMARY_LENGTHS) == NUM_PRIMARY_LENGTHS)
			{
				DECODE_LEN_TREE_NOEOFCHECK(match_length);
			}

	 		/*
	  		 * Get match position slot
	  		 */
			m = c >> NL_SHIFT;

			if (m > 2)
			{
				if (dec_extra_bits[ m ] >= 3)
				{
					if (dec_extra_bits[m]-3)
					{
						/* no need to getbits17 */
	    				GET_BITS_NOEOFCHECK(dec_extra_bits[ m ] - 3, temp_pos);
					}
					else
					{
						temp_pos = 0;
					}

	    			match_pos = MP_POS_minus2[m] + (temp_pos << 3);

	    			DECODE_ALIGNED_NOEOFCHECK(temp_pos);
	    			match_pos += temp_pos;
				}
				else
				{
					if (dec_extra_bits[m])
					{
	    				GET_BITS_NOEOFCHECK(dec_extra_bits[ m ], match_pos);

						match_pos += MP_POS_minus2[m];
					}
					else
					{
                        match_pos = 1; // MP_POS_minus2[m==3];
					}
				}

				context->dec_last_matchpos_offset[2] = context->dec_last_matchpos_offset[1];
				context->dec_last_matchpos_offset[1] = context->dec_last_matchpos_offset[0];
				context->dec_last_matchpos_offset[0] = match_pos;
			}
			else
	 		{
				match_pos = context->dec_last_matchpos_offset[m];

				if (m)
				{
					context->dec_last_matchpos_offset[m] = context->dec_last_matchpos_offset[0];
					context->dec_last_matchpos_offset[0] = match_pos;
				}
	 		}

			match_length += MIN_MATCH;

			do
			{
                dec_mem_window[bufpos] = dec_mem_window[(bufpos-match_pos) & context->dec_window_mask];

				if (bufpos < MAX_MATCH)
					dec_mem_window[context->dec_window_size+bufpos] = dec_mem_window[bufpos];

				bufpos++;
			} while (--match_length > 0);
		}
	}

	context->dec_bitcount	  = dec_bitcount;
	context->dec_bitbuf		  = dec_bitbuf;
	context->dec_input_curpos = dec_input_curpos;

    return bufpos;
}


#ifndef ASM_DECODE_ALIGNED_OFFSET_BLOCK
long fast_decode_aligned_offset_block(t_decoder_context *context, long bufpos, int amount_to_decode)
{
	ulong	match_pos;
	ulong	temp_pos;
	long	bufpos_end;
	long	decode_residue;
	int		match_length;
	int		c;
	ulong	dec_bitbuf;
	char	dec_bitcount;
    byte   *dec_input_curpos;
    byte   *dec_end_input_pos;
	byte   *dec_mem_window;
	ulong	match_ptr;
	char	m;

	/*
	 * Store commonly used variables locally
	 */
	dec_bitcount	  = context->dec_bitcount;
	dec_bitbuf		  = context->dec_bitbuf;
	dec_input_curpos  = context->dec_input_curpos;
	dec_end_input_pos = context->dec_end_input_pos;
	dec_mem_window	  = context->dec_mem_window;

	bufpos_end = bufpos + amount_to_decode;

	while (bufpos < bufpos_end)
	{
		/*
		 * Decode an item
		 */
		DECODE_MAIN_TREE(c);

		if ((c -= NUM_CHARS) < 0)
		{
			dec_mem_window[bufpos++] = (byte) c;
		}
		else
		{
	 		/*
	  		 * Get match length slot
	  		 */
			if ((match_length = c & NUM_PRIMARY_LENGTHS) == NUM_PRIMARY_LENGTHS)
			{
				DECODE_LEN_TREE_NOEOFCHECK(match_length);
			}

	 		/*
	  		 * Get match position slot
	  		 */
			m = c >> NL_SHIFT;

			if (m > 2)
			{
				if (dec_extra_bits[ m ] >= 3)
				{
					if (dec_extra_bits[m]-3)
					{
						/* no need to getbits17 */
	    				GET_BITS_NOEOFCHECK(dec_extra_bits[ m ] - 3, temp_pos);
					}
					else
					{
						temp_pos = 0;
					}

	    			match_pos = MP_POS_minus2[m] + (temp_pos << 3);

	    			DECODE_ALIGNED_NOEOFCHECK(temp_pos);
	    			match_pos += temp_pos;
				}
				else
				{
					if (dec_extra_bits[m])
					{
	    				GET_BITS_NOEOFCHECK(dec_extra_bits[ m ], match_pos);

						match_pos += MP_POS_minus2[m];
					}
					else
					{
						match_pos = MP_POS_minus2[m];
					}
				}

				context->dec_last_matchpos_offset[2] = context->dec_last_matchpos_offset[1];
				context->dec_last_matchpos_offset[1] = context->dec_last_matchpos_offset[0];
				context->dec_last_matchpos_offset[0] = match_pos;
			}
			else
	 		{
				match_pos = context->dec_last_matchpos_offset[m];

				if (m)
				{
					context->dec_last_matchpos_offset[m] = context->dec_last_matchpos_offset[0];
					context->dec_last_matchpos_offset[0] = match_pos;
				}
	 		}

			match_length += MIN_MATCH;
			match_ptr = (bufpos - match_pos) & context->dec_window_mask;

			do
			{
				dec_mem_window[bufpos++] = dec_mem_window[match_ptr++];
			} while (--match_length > 0);
		}
	}

	context->dec_bitcount	  = dec_bitcount;
	context->dec_bitbuf		  = dec_bitbuf;
	context->dec_input_curpos = dec_input_curpos;

	/* should be zero */
	decode_residue = bufpos - bufpos_end;

    bufpos &= context->dec_window_mask;
	context->dec_bufpos = bufpos;

	return decode_residue;
}
#endif


int decode_aligned_offset_block(
    t_decoder_context * context, 
    long                BufPos, 
    int                 amount_to_decode
)
{
    /*
     * Special case code when BufPos is near the beginning of the window;
     * we must properly update our MAX_MATCH wrapper bytes.
     */
    if (BufPos < MAX_MATCH)
    {
        long    new_bufpos;
        long    amount_to_slowly_decode;

        amount_to_slowly_decode = (((MAX_MATCH-BufPos) < (amount_to_decode)) ? (MAX_MATCH-BufPos) : (amount_to_decode));

        /*
         * It's ok to end up decoding more than we wanted if we
         * restricted it to decoding only MAX_MATCH; there's
         * no guarantee a match doesn't straddle MAX_MATCH
         */
        new_bufpos = special_decode_aligned_block(
            context,
            BufPos,
            amount_to_slowly_decode
        );

        amount_to_decode -= (new_bufpos-BufPos);

        context->dec_bufpos = BufPos = new_bufpos;

        /*
         * Note: if amount_to_decode < 0 then we're in trouble
         */
        if (amount_to_decode <= 0)
            return amount_to_decode;
    }

    return fast_decode_aligned_offset_block(context, BufPos, amount_to_decode);
}
