/*$Source: /usr/home/dhesi/zoo/RCS/encode.c,v $*/
/*$Id: encode.c,v 1.41 91/07/09 01:39:47 dhesi Exp $*/

/*
Adapted from "ar" archiver written by Haruhiko Okumura.
*/

/*

Adapted from "zoo" written by Rahul Dhesi

-- Matthias Meixner

*/

#include <stdlib.h>
#include <string.h>
#include <limits.h>

#include "zooarc.h"
#include "ar.h"
#include "lzh.h"


extern char *out_buf_adr;

static char *in_f;
static long size;

/*
sliding dictionary with percolating update
*/

#define PERCOLATE  1
#define NIL  0
#define MAX_HASH_VAL (3 * DICSIZ + (DICSIZ / 512 + 1) * UCHAR_MAX)

typedef short node;

static uchar *text, *childcount;
static node pos, matchpos, avail,
*position, *parent, *prev, *next = NULL;
static int remainder, matchlen;

#if MAXMATCH <= (UCHAR_MAX + 1)
static uchar *level;
# define T_LEVEL   uchar *
#else
static ushort *level;
# define T_LEVEL   ushort *
#endif

void makechild(node, int /*=uchar*/, node);
node child(node, int /*=uchar*/);
void split(node);
void delete_node(void);
void insert_node(void);
static void allocate_memory(void);
void init_slide(void);
void get_next_match(void);

void free_encode()
{
   if(level) free(level);
   if(childcount) free(childcount);
   if(position) free(position);
   if(parent) free(parent);
   if(prev) free(prev);
   if(next) free(next);
   level=NULL;
   childcount=NULL;
   position=NULL;
   parent=NULL;
   prev=NULL;
   next=NULL;
}

static void allocate_memory()
{
   if (next != NULL) return;
   /* text = (uchar *) malloc(DICSIZ * 2 + MAXMATCH); */
   text = (uchar *) out_buf_adr; /* reuse I/O buffer used elsewhere */
   level = (T_LEVEL) malloc((DICSIZ + UCHAR_MAX + 1) * sizeof(*level));
   childcount = (uchar *)malloc((DICSIZ + UCHAR_MAX + 1) * sizeof(*childcount));
#ifdef PERCOLATE
   position = (node *) malloc((DICSIZ + UCHAR_MAX + 1) * sizeof(*position));
#else
   position = (node *) malloc(DICSIZ * sizeof(*position));
#endif
   parent     = (node *) malloc(DICSIZ * 2 * sizeof(*parent));
   prev        = (node *) malloc(DICSIZ * 2 * sizeof(*prev));
   next        = (node *) malloc((MAX_HASH_VAL + 1) * sizeof(*next));

   if(!text || !level || !childcount || !position || !parent || !prev || !next) {
      if(text) free(text);
      if(level) free(level);
      if(childcount) free(childcount);
      if(position) free(position);
      if(parent) free(parent);
      if(prev) free(prev);
      if(next) free(next);
      Error(XARERR_NO_MEMORY);
   }
}

void init_slide()
{
   node i;

   for (i = DICSIZ; i <= DICSIZ + UCHAR_MAX; i++) {
      level[i] = 1;
#ifdef PERCOLATE
      position[i] = NIL;  /* sentinel */
#endif
   }
   for (i = DICSIZ; i < DICSIZ * 2; i++) parent[i] = NIL;
   avail = 1;
   for (i = 1; i < DICSIZ - 1; i++) next[i] = i + 1;
   next[DICSIZ - 1] = NIL;
   for (i = DICSIZ * 2; i <= MAX_HASH_VAL; i++) next[i] = NIL;
}

#define HASH(p, c) ((p) + ((c) << (DICBIT - 9)) + DICSIZ * 2)

node child(node q, int c)
/* q's child for character c (NIL if not found) */
{
   node r;

   r = next[HASH(q, c)];
   parent[NIL] = q;   /* sentinel */
   while (parent[r] != q) r = next[r];
   return r;
}

void makechild(node q,int c,node r)
/* Let r be q's child for character c. */
{
   node h, t;

   h = HASH(q, c);
   t = next[h];  next[h] = r;  next[r] = t;
   prev[t] = r;  prev[r] = h;
   parent[r] = q; childcount[q]++;
}

void split(node old)
{
   node new, t;

   new = avail;  avail = next[new];  childcount[new] = 0;
   t = prev[old]; prev[new] = t; next[t] = new;
   t = next[old]; next[new] = t; prev[t] = new;
   parent[new] = parent[old];
   level[new] = matchlen;
   position[new] = pos;
   makechild(new, text[matchpos + matchlen], old);
   makechild(new, text[pos + matchlen], pos);
}

void insert_node()
{
   node q, r, j, t;
   uchar c, *t1, *t2;

   if (matchlen >= 4) {
      matchlen--;
      r = (matchpos + 1) | DICSIZ;
      while ((q = parent[r]) == NIL) r = next[r];
      while (level[q] >= matchlen) {
         r = q;   q = parent[q];
      }
#ifdef PERCOLATE
      t = q;
      while (position[t] < 0) {
         position[t] = pos;  t = parent[t];
      }
      if (t < DICSIZ) position[t] = pos | PERC_FLAG;
#else
      t = q;
      while (t < DICSIZ) {
         position[t] = pos;  t = parent[t];
      }
#endif
   } else {
      q = text[pos] + DICSIZ;  c = text[pos + 1];
      if ((r = child(q, c)) == NIL) {
         makechild(q, c, pos);  matchlen = 1;
         return;
      }
      matchlen = 2;
   }
   for ( ; ; ) {
      if (r >= DICSIZ) {
         j = MAXMATCH;   matchpos = r;
      } else {
         j = level[r];
         matchpos = position[r] & ~PERC_FLAG;
      }
      if (matchpos >= pos) matchpos -= DICSIZ;
      t1 = &text[pos + matchlen];  t2 = &text[matchpos + matchlen];
      while (matchlen < j) {
         if (*t1 != *t2) {  split(r);  return;  }
         matchlen++;  t1++;  t2++;
      }
      if (matchlen >= MAXMATCH) break;
      position[r] = pos;
      q = r;
      if ((r = child(q, *t1)) == NIL) {
         makechild(q, *t1, pos);  return;
      }
      matchlen++;
   }
   t = prev[r];  prev[pos] = t;   next[t] = pos;
   t = next[r];  next[pos] = t;   prev[t] = pos;
   parent[pos] = q;   parent[r] = NIL;
   next[r] = pos; /* special use of next[] */
}

void delete_node(void)
{
#ifdef PERCOLATE
   node q, r, s, t, u;
#else
   node r, s, t, u;
#endif

   if (parent[pos] == NIL) return;
   r = prev[pos]; s = next[pos];
   next[r] = s;  prev[s] = r;
   r = parent[pos];   parent[pos] = NIL;
   if (r >= DICSIZ || --childcount[r] > 1) return;
#ifdef PERCOLATE
   t = position[r] & ~PERC_FLAG;
#else
   t = position[r];
#endif
   if (t >= pos) t -= DICSIZ;
#ifdef PERCOLATE
   s = t;   q = parent[r];
   while ((u = position[q]) & PERC_FLAG) {
      u &= ~PERC_FLAG;   if (u >= pos) u -= DICSIZ;
      if (u > s) s = u;
      position[q] = (s | DICSIZ);  q = parent[q];
   }
   if (q < DICSIZ) {
      if (u >= pos) u -= DICSIZ;
      if (u > s) s = u;
      position[q] = s | DICSIZ | PERC_FLAG;
   }
#endif
   s = child(r, text[t + level[r]]);
   t = prev[s];  u = next[s];
   next[t] = u;  prev[u] = t;
   t = prev[r];  next[t] = s;  prev[s] = t;
   t = next[r];  prev[t] = s;  next[s] = t;
   parent[s] = parent[r];   parent[r] = NIL;
   next[r] = avail;   avail = r;
}

void get_next_match()
{
   int n;

   remainder--;
   if (++pos == DICSIZ * 2) {
#ifdef CHECK_BREAK
      check_break();
#endif
      (void) MOVE_LEFT((char *) &text[0], (char *) &text[DICSIZ], DICSIZ + MAXMATCH);

      n=size>DICSIZ?DICSIZ:size;
      memcpy(&text[DICSIZ+MAXMATCH],in_f,n);
      in_f+=n;
      size-=n;

/*
 *     n = fread_crc(&text[DICSIZ + MAXMATCH], DICSIZ, lzh_infile);
 */

      remainder += n;  pos = DICSIZ;
#ifdef SHOW_DOTS
      (void) putc('.', stderr);
      (void) fflush(stderr);
#endif
   }
   delete_node();  insert_node();
}



/* read from infile, compress, write to outfile */
void encode(struct XarSubIO *xio)
{
   int lastmatchlen;
   node lastmatchpos;

   /* make input/output files visible to other functions */
   in_f = xio->IOMem;
   size=xio->Size;

   allocate_memory();  init_slide();  huf_encode_start();

   remainder=size>DICSIZ+MAXMATCH?DICSIZ+MAXMATCH:size;
   memcpy(&text[DICSIZ],in_f,remainder);
   in_f+=remainder;
   size-=remainder;

/*
 *   remainder = fread_crc(&text[DICSIZ], DICSIZ + MAXMATCH, lzh_infile);
 */

#ifdef SHOW_DOTS
   (void) putc('.', stderr);
   (void) fflush(stderr);
#endif

   matchlen = 0;
   pos = DICSIZ;   insert_node();
   if (matchlen > remainder) matchlen = remainderatchlen =rr)M e
   if(po 
tion) fre ibcount[-);
   if(  n =r inislocate wemory( FreeMem()  rr)-3 * Dt[DICSIZ]   fileture.der);
      eated. +3M t    i,a

   emainder;  
   }
   deeMem()  rfread_crc  emainder = fread_crc(&ncludATCH, lzh  ainder) ct @{#ifdef SH  u > s) hias id) putc(  ICSIZ+MAXMATCH:siz    = (no       ATCH]suppl

   matc       p[new] = t;ve is
 ] =  (Mart_node()  der;

/*
 *    sentinel ore th :siz -     mainderat  ()  der;
  d (       em()t[r]if (r     [-);
   i  le (pv        =        )LAG;
 tia
    en   vaLen cer)  el ore thM )r-loc((atc  tn =  tati