/* Program Name            : ENDECODE.C                                 */
/*   Original Author       : C. K. Hung					*/
/*   Date                  : 1-JUN-1990					*/
/*   Program Description   : This file contains routines for compress 	*/
/*                           and expand files.				*/
/*                         :                                            */
/* Revision History follows                                             */
 
#include "global.h"
#include "endecode.h"
 
/*
**
**  INTERNAL GLOBAL DECLARATIONS
**
**/
 
static int input_bit_count;
static unsigned long input_bit_buffer;
static int output_bit_count;
static unsigned long output_bit_buffer;
 
 

int compress (FILE *input, FILE *output)
{
    unsigned int next_code;
    unsigned int character;
    unsigned int string_code;
    unsigned int index;
    register int i;
 
    output_bit_count = 0;
    output_bit_buffer = 0;
 
    next_code = 256;
 
    for (i = 0;  i < TABLE_SIZE;  i++)
 
        code_value[i] = -1;
 
    string_code = getc(input);
 
    while ((character = getc(input)) != EOF)
    {
	index = find_match (string_code, character);
 
	if (code_value[index] != -1)
 
	    string_code = code_value[index];
 
	else
	{
	    if (next_code <= MAX_CODE)
	    {
		code_value[index] = next_code++;
		prefix_code[index] = string_code;
		append_character[index] = character;
	    }
 
	    output_code(output, string_code);
	    string_code = character;
	}
    }
 
    output_code(output, string_code);
    output_code(output, MAX_VALUE);
    output_code(output, 0);
	return DX__NORMAL;
}
 
 

int find_match (int hash_prefix, unsigned int hash_character)
{
    register int index;
    int offset;
 
    index = (hash_character << HASHING_SHIFT) ^ hash_prefix;
    offset = (index == 0? 1 : TABLE_SIZE-index);
 
    while (1)
    {
        if (code_value[index] == -1)
	{
            return index;
	}
 
        if (prefix_code[index] == hash_prefix &&
	    append_character[index] == hash_character)
	{
            return index;
	}
 
	index -= offset;
 
        if (index < 0)
	{
            index += TABLE_SIZE;
	}
 
    }
}
 
 

int output_code (FILE *output, unsigned int code)
{
    output_bit_buffer |= (unsigned long) code << (32-BITS-output_bit_count);
    output_bit_count += BITS;
 
    while (output_bit_count >= 8)
    {
        putc(output_bit_buffer >> 24, output);
	output_bit_buffer <<= 8;
	output_bit_count -= 8;
    }
	return DX__NORMAL;
}
 
 

int expand(char *errmsg, FILE *input, FILE *output)
{
    unsigned int next_code;
    unsigned int new_code;
    unsigned int old_code;
    int character;
    unsigned char *string;
 
    input_bit_count = 0;
    input_bit_buffer = 0;
 
    next_code = 256;
 
    old_code = input_code(input);
    character = old_code;
    putc(old_code, output);
 
    while ((new_code = input_code(input)) != MAX_VALUE)
    {
        if (new_code >= next_code)
        {
            *decode_stack = character;
	    if ((string = decode_string(errmsg, decode_stack+1, old_code)) == -1)
		return -1;
        }
        else if ((string = decode_string(errmsg, decode_stack, new_code)) == -1)
	    return -1;
 
	character = *string;
 
        while (string >= decode_stack)
 
	    putc(*string--, output);
 
        if (next_code <= MAX_CODE)
        {
            prefix_code[next_code] = old_code;
	    append_character[next_code] = character;
	    next_code++;
        }
 
	old_code = new_code;
    }
 
    return 1;
}
 
 

char *decode_string(char *errmsg, unsigned char *buffer, unsigned int code)
{
    register int i;
 
    i = 0;
 
    while (code > 255)
    {
        *buffer++ = append_character[code];
	code = prefix_code[code];
        if (i++ >= 4000)
        {
	    sprintf(errmsg, "Exceeded quota during code expandion");
	    return -1;
        }
    }
 
    *buffer = code;
 
    return buffer;
}
 
 

int input_code(FILE *input)
{
    unsigned int return_value;
    unsigned int character;
 
    while (input_bit_count <= 24)
    {
        input_bit_buffer |= (unsigned long) getc(input) << (24-input_bit_count);
	input_bit_count += 8;
    }
 
    return_value = input_bit_buffer >> (32-BITS);
    input_bit_buffer <<= BITS;
    input_bit_count -= BITS;
 
    return return_value;
}
