/* ------------------------------------------------------------- */
/*
 * Imploding
 * ---------
 *
 * The Imploding algorithm is actually a combination of two distinct
 * algorithms.  The first algorithm compresses repeated byte sequences
 * using a sliding dictionary.  The second algorithm is used to compress
 * the encoding of the sliding dictionary ouput, using multiple
 * Shannon-Fano trees.
 *
 */


   sf_tree  lit_tree;
   sf_tree  length_tree;
   sf_tree  distance_tree;
   /* s-f storage is shared with that used by other comp. methods */
   sf_node  *lit_nodes = (sf_node *) followers;     /* 2*LITVALS nodes */
   sf_node  *length_nodes = (sf_node *) suffix_of;  /* 2*LENVALS nodes */
   sf_node  *distance_nodes = (sf_node *) stack;    /* 2*DISTVALS nodes */
   boolean  lit_tree_present;
   boolean  eightK_dictionary;
   int      minimum_match_length;
   int      dict_bits;

#ifdef __TURBOC__
/* v2.0b More local prototypes */
void ReadLengths(sf_tree *tree);
void SortLengths(sf_tree *tree);
void GenerateTrees(sf_tree *tree, sf_node *nodes);
void LoadTree(sf_tree *tree, int treesize, sf_node *nodes);
void LoadTrees(void);
void ReadTree(register sf_node *nodes, int *dest);
#endif

void         SortLengths(tree)
sf_tree *tree;
  /* Sort the Bit Lengths in ascending order, while retaining the order
    of the original lengths stored in the file */
{
    register sf_entry *ejm1;    /* entry[j - 1] */
    register int j;
    register sf_entry *entry;
    register int i;
    sf_entry tmp;
    int entries;
    unsigned a, b;

    entry = &tree->entry[0];
    entries = tree->entries;

    for (i = 0; ++i < entries; )  {
        tmp = entry[i];
        b = tmp.BitLength;
        j = i;
        while ((j > 0)
        && ((a = (ejm1 = &entry[j - 1])->BitLength) >= b))  {
            if ((a == b) && (ejm1->Value <= tmp.Value))
                break;
            *(ejm1 + 1) = *ejm1;    /* entry[j] = entry[j - 1] */
            --j;
        }
        entry[j] = tmp;
    }
}


/* ----------------------------------------------------------- */

void         ReadLengths(tree)
sf_tree *tree;
{
   int    treeBytes;
   int    i;
   int    num, len;

  /* get number of bytes in compressed tree */
   READBIT(8,treeBytes);
   treeBytes++;
   i = 0;

   tree->MaxLength = 0;

/* High 4 bits: Number of values at this bit length + 1. (1 - 16)
 * Low  4 bits: Bit Length needed to represent value + 1. (1 - 16)
 */
   while (treeBytes > 0) {
      READBIT(4,len); len++;
      READBIT(4,num); num++;

      while (num > 0) {
         if (len > tree->MaxLength)
            tree->MaxLength = len;
         tree->entry[i].BitLength = len;
         tree->entry[i].Value = i;
         i++;
         num--;
      }

      treeBytes--;
   }
}


/* ----------------------------------------------------------- */

void         GenerateTrees(tree, nodes)
sf_tree *tree;
sf_node *nodes;
     /* Generate the Shannon-Fano trees */
{
    int codelen, i, j, lvlstart, next, parents;

    i = tree->entries - 1;          /* either 255 or 63 */
    lvlstart = next = 1;

    /* believe it or not, there may be a 1-bit code */

    for (codelen = tree->MaxLength; codelen >= 1; --codelen)  {

        /* create leaf nodes at level <codelen> */

        while ((i >= 0) && (tree->entry[i].BitLength == codelen))  {
            nodes[next].left = 0;
            nodes[next].right = tree->entry[i].Value;
            ++next;
            --i;
        }

        /* create parent nodes for all nodes at level <codelen>,
           but don't create the root node here */

        parents = next;
        if (codelen > 1)  {
            for (j = lvlstart; j <= parents-2; j += 2)  {
                nodes[next].left = j;
                nodes[next].right = j + 1;
                ++next;
            }
        }
        lvlstart = parents;
    }

    /* create root node */

    nodes[0].left = next - 2;
    nodes[0].right = next - 1;
}


/* ----------------------------------------------------------- */

void         LoadTree(tree, treesize, nodes)
sf_tree *tree;
int treesize;
sf_node *nodes;
     /* allocate and load a shannon-fano tree from the compressed file */
{
   tree->entries = treesize;
   ReadLengths(tree);
   SortLengths(tree);
   GenerateTrees(tree, nodes);
}


/* ----------------------------------------------------------- */

void         LoadTrees()
{
   eightK_dictionary = (lrec.general_purpose_bit_flag & 0x02) != 0; /* bit 1 */
   lit_tree_present = (lrec.general_purpose_bit_flag & 0x04) != 0;  /* bit 2 */

   if (eightK_dictionary)
      dict_bits = 7;
   else
      dict_bits = 6;

   if (lit_tree_present) {
      minimum_match_length = 3;
      LoadTree(&lit_tree,256,lit_nodes);
   }
   else
      minimum_match_length = 2;

   LoadTree(&length_tree,64,length_nodes);
   LoadTree(&distance_tree,64,distance_nodes);
}


/* ----------------------------------------------------------- */

#ifndef ASM

void         ReadTree(nodes, dest)
register sf_node *nodes;
int *dest;
     /* read next byte using a shannon-fano tree */
{
    register int cur;
    register int left;
    UWORD b;

    for (cur = 0; ; )  {
        if ((left = nodes[cur].left) == 0)  {
            *dest = nodes[cur].right;
            return;
        }
        READBIT(1, b);
        cur = (b ? nodes[cur].right : left);
    }
}

#endif

/* ----------------------------------------------------------- */

void         unImplode()
     /* expand imploded data */
{
   register int srcix;
   register int Length;
   register int limit;
   int          lout;
   int          Distance;

   LoadTrees();

#ifdef DEBUG
   printf("\n");
#endif
   while ((!zipeof) && ((outpos+outcnt) < ucsize)) {
      READBIT(1,lout);

      if (lout != 0) {   /* encoded data is literal data */
         if (lit_tree_present)  /* use Literal Shannon-Fano tree */  {
            ReadTree(lit_nodes,&lout);
#ifdef DEBUG
            printf("lit=%d\n", lout);
#endif
         }
         else
            READBIT(8,lout);

         OUTB(lout);
      }
      else {            /* encoded data is sliding dictionary match */
         READBIT(dict_bits,Distance);

         ReadTree(distance_nodes,&lout);
#ifdef DEBUG
     printf("d=%5d (%2d,%3d)", (lout << dict_bits) | Distance, lout,
            Distance);
#endif
         Distance |= (lout << dict_bits);
         /* using the Distance Shannon-Fano tree, read and decode the
            upper 6 bits of the Distance value */

         ReadTree(length_nodes,&lout);
         Length = lout;
#ifdef DEBUG
     printf("\tl=%3d\n", Length);
#endif
         /* using the Length Shannon-Fano tree, read and decode the
            Length value */

         if (Length == 63) {
            READBIT(8,lout);
            Length += lout;
         }
         Length += minimum_match_length;

        /* move backwards Distance+1 bytes in the output stream, and copy
          Length characters from this position to the output stream.
          (if this position is before the start of the output stream,
          then assume that all the data before the start of the output
          stream is filled with zeros.  Requires initializing outbuf
          for each file.) */

        srcix = (outcnt - (Distance + 1)) & (OUTBUFSIZ-1);
        limit = OUTBUFSIZ - Length;
        if ((srcix <= limit) && (outcnt < limit))  {
            zmemcpy(outptr, &outbuf[srcix], Length);
            outptr += Length;
            outcnt += Length;
        }
        else {
            while (Length--)  {
                OUTB(outbuf[srcix++]);
                srcix &= OUTBUFSIZ-1;
            }
        }
      }
   }
}

