/** @file
  Main file for compression routine.

  Compression routine. The compression algorithm is a mixture of
  LZ77 and Huffman coding. LZ77 transforms the source data into a
  sequence of Original Characters and Pointers to repeated strings.
  This sequence is further divided into Blocks and Huffman codings
  are applied to each Block.

  Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/
#include <Uefi.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/ShellLib.h>

#include "Compress.h"

//
// Macro Definitions
//
typedef INT16             NODE;
#define UINT8_MAX         0xff
#define UINT8_BIT         8
#define THRESHOLD         3
#define INIT_CRC          0
#define WNDBIT            13
#define WNDSIZ            (1U << WNDBIT)
#define MAXMATCH          256
#define BLKSIZ            (1U << 14)  // 16 * 1024U
#define PERC_FLAG         0x8000U
#define CODE_BIT          16
#define NIL               0
#define MAX_HASH_VAL      (3 * WNDSIZ + (WNDSIZ / 512 + 1) * UINT8_MAX)
#define HASH(LoopVar7, LoopVar5)        ((LoopVar7) + ((LoopVar5) << (WNDBIT - 9)) + WNDSIZ * 2)
#define CRCPOLY           0xA001
#define UPDATE_CRC(LoopVar5)     mCrc = mCrcTable[(mCrc ^ (LoopVar5)) & 0xFF] ^ (mCrc >> UINT8_BIT)

//
// C: the Char&Len Set; P: the Position Set; T: the exTra Set
//
#define NC                (UINT8_MAX + MAXMATCH + 2 - THRESHOLD)
#define CBIT              9
#define NP                (WNDBIT + 1)
#define PBIT              4
#define NT                (CODE_BIT + 3)
#define TBIT              5
#if NT > NP
  #define                 NPT NT
#else
  #define                 NPT NP
#endif
//
// Function Prototypes
//

/**
  Put a dword to output stream

  @param[in] Data    The dword to put.
**/
VOID
PutDword(
  IN UINT32 Data
  );

//
//  Global Variables
//
STATIC UINT8  *mSrc;
STATIC UINT8  *mDst;
STATIC UINT8  *mSrcUpperLimit;
STATIC UINT8  *mDstUpperLimit;

STATIC UINT8  *mLevel;
STATIC UINT8  *mText;
STATIC UINT8  *mChildCount;
STATIC UINT8  *mBuf;
STATIC UINT8  mCLen[NC];
STATIC UINT8  mPTLen[NPT];
STATIC UINT8  *mLen;
STATIC INT16  mHeap[NC + 1];
STATIC INT32  mRemainder;
STATIC INT32  mMatchLen;
STATIC INT32  mBitCount;
STATIC INT32  mHeapSize;
STATIC INT32  mTempInt32;
STATIC UINT32 mBufSiz = 0;
STATIC UINT32 mOutputPos;
STATIC UINT32 mOutputMask;
STATIC UINT32 mSubBitBuf;
STATIC UINT32 mCrc;
STATIC UINT32 mCompSize;
STATIC UINT32 mOrigSize;

STATIC UINT16 *mFreq;
STATIC UINT16 *mSortPtr;
STATIC UINT16 mLenCnt[17];
STATIC UINT16 mLeft[2 * NC - 1];
STATIC UINT16 mRight[2 * NC - 1];
STATIC UINT16 mCrcTable[UINT8_MAX + 1];
STATIC UINT16 mCFreq[2 * NC - 1];
STATIC UINT16 mCCode[NC];
STATIC UINT16 mPFreq[2 * NP - 1];
STATIC UINT16 mPTCode[NPT];
STATIC UINT16 mTFreq[2 * NT - 1];

STATIC NODE   mPos;
STATIC NODE   mMatchPos;
STATIC NODE   mAvail;
STATIC NODE   *mPosition;
STATIC NODE   *mParent;
STATIC NODE   *mPrev;
STATIC NODE   *mNext = NULL;
INT32         mHuffmanDepth = 0;

/**
  Make a CRC table.

**/
VOID
MakeCrcTable (
  VOID
  )
{
  UINT32  LoopVar1;

  UINT32  LoopVar2;

  UINT32  LoopVar4;

  for (LoopVar1 = 0; LoopVar1 <= UINT8_MAX; LoopVar1++) {
    LoopVar4 = LoopVar1;
    for (LoopVar2 = 0; LoopVar2 < UINT8_BIT; LoopVar2++) {
      if ((LoopVar4 & 1) != 0) {
        LoopVar4 = (LoopVar4 >> 1) ^ CRCPOLY;
      } else {
        LoopVar4 >>= 1;
      }
    }

    mCrcTable[LoopVar1] = (UINT16) LoopVar4;
  }
}

/**
  Put a dword to output stream

  @param[in] Data    The dword to put.
**/
VOID
PutDword (
  IN UINT32 Data
  )
{
  if (mDst < mDstUpperLimit) {
    *mDst++ = (UINT8) (((UINT8) (Data)) & 0xff);
  }

  if (mDst < mDstUpperLimit) {
    *mDst++ = (UINT8) (((UINT8) (Data >> 0x08)) & 0xff);
  }

  if (mDst < mDstUpperLimit) {
    *mDst++ = (UINT8) (((UINT8) (Data >> 0x10)) & 0xff);
  }

  if (mDst < mDstUpperLimit) {
    *mDst++ = (UINT8) (((UINT8) (Data >> 0x18)) & 0xff);
  }
}

/**
  Allocate memory spaces for data structures used in compression process.

  @retval EFI_SUCCESS           Memory was allocated successfully.
  @retval EFI_OUT_OF_RESOURCES  A memory allocation failed.
**/
EFI_STATUS
AllocateMemory (
  VOID
  )
{
  mText       = AllocateZeroPool (WNDSIZ * 2 + MAXMATCH);
  mLevel      = AllocateZeroPool ((WNDSIZ + UINT8_MAX + 1) * sizeof (*mLevel));
  mChildCount = AllocateZeroPool ((WNDSIZ + UINT8_MAX + 1) * sizeof (*mChildCount));
  mPosition   = AllocateZeroPool ((WNDSIZ + UINT8_MAX + 1) * sizeof (*mPosition));
  mParent     = AllocateZeroPool (WNDSIZ * 2 * sizeof (*mParent));
  mPrev       = AllocateZeroPool (WNDSIZ * 2 * sizeof (*mPrev));
  mNext       = AllocateZeroPool ((MAX_HASH_VAL + 1) * sizeof (*mNext));

  mBufSiz     = BLKSIZ;
  mBuf        = AllocateZeroPool (mBufSiz);
  while (mBuf == NULL) {
    mBufSiz = (mBufSiz / 10U) * 9U;
    if (mBufSiz < 4 * 1024U) {
      return EFI_OUT_OF_RESOURCES;
    }

    mBuf = AllocateZeroPool (mBufSiz);
  }

  mBuf[0] = 0;

  return EFI_SUCCESS;
}

/**
  Called when compression is completed to free memory previously allocated.

**/
VOID
FreeMemory (
  VOID
  )
{
  SHELL_FREE_NON_NULL (mText);
  SHELL_FREE_NON_NULL (mLevel);
  SHELL_FREE_NON_NULL (mChildCount);
  SHELL_FREE_NON_NULL (mPosition);
  SHELL_FREE_NON_NULL (mParent);
  SHELL_FREE_NON_NULL (mPrev);
  SHELL_FREE_NON_NULL (mNext);
  SHELL_FREE_NON_NULL (mBuf);
}

/**
  Initialize String Info Log data structures.
**/
VOID
InitSlide (
  VOID
  )
{
  NODE  LoopVar1;

  SetMem (mLevel + WNDSIZ, (UINT8_MAX + 1) * sizeof (UINT8), 1);
  SetMem (mPosition + WNDSIZ, (UINT8_MAX + 1) * sizeof (NODE), 0);

  SetMem (mParent + WNDSIZ, WNDSIZ * sizeof (NODE), 0);

  mAvail = 1;
  for (LoopVar1 = 1; LoopVar1 < WNDSIZ - 1; LoopVar1++) {
    mNext[LoopVar1] = (NODE) (LoopVar1 + 1);
  }

  mNext[WNDSIZ - 1] = NIL;
  SetMem (mNext + WNDSIZ * 2, (MAX_HASH_VAL - WNDSIZ * 2 + 1) * sizeof (NODE), 0);
}

/**
  Find child node given the parent node and the edge character

  @param[in] LoopVar6       The parent node.
  @param[in] LoopVar5       The edge character.

  @return             The child node.
  @retval NIL(Zero)   No child could be found.

**/
NODE
Child (
  IN NODE   LoopVar6,
  IN UINT8  LoopVar5
  )
{
  NODE  LoopVar4;

  LoopVar4             = mNext[HASH (LoopVar6, LoopVar5)];
  mParent[NIL]  = LoopVar6;  /* sentinel */
  while (mParent[LoopVar4] != LoopVar6) {
    LoopVar4 = mNext[LoopVar4];
  }

  return LoopVar4;
}

/**
  Create a new child for a given parent node.

  @param[in] LoopVar6       The parent node.
  @param[in] LoopVar5       The edge character.
  @param[in] LoopVar4       The child node.
**/
VOID
MakeChild (
  IN NODE   LoopVar6,
  IN UINT8  LoopVar5,
  IN NODE   LoopVar4
  )
{
  NODE  LoopVar12;

  NODE  LoopVar10;

  LoopVar12          = (NODE) HASH (LoopVar6, LoopVar5);
  LoopVar10          = mNext[LoopVar12];
  mNext[LoopVar12]   = LoopVar4;
  mNext[LoopVar4]    = LoopVar10;
  mPrev[LoopVar10]   = LoopVar4;
  mPrev[LoopVar4]    = LoopVar12;
  mParent[LoopVar4]  = LoopVar6;
  mChildCount[LoopVar6]++;
}

/**
  Split a node.

  @param[in] Old     The node to split.
**/
VOID
Split (
  IN NODE Old
  )
{
  NODE  New;

  NODE  LoopVar10;

  New               = mAvail;
  mAvail            = mNext[New];
  mChildCount[New]  = 0;
  LoopVar10                 = mPrev[Old];
  mPrev[New]        = LoopVar10;
  mNext[LoopVar10]          = New;
  LoopVar10                 = mNext[Old];
  mNext[New]        = LoopVar10;
  mPrev[LoopVar10]          = New;
  mParent[New]      = mParent[Old];
  mLevel[New]       = (UINT8) mMatchLen;
  mPosition[New]    = mPos;
  MakeChild (New, mText[mMatchPos + mMatchLen], Old);
  MakeChild (New, mText[mPos + mMatchLen], mPos);
}

/**
  Insert string info for current position into the String Info Log.

**/
VOID
InsertNode (
  VOID
  )
{
  NODE  LoopVar6;

  NODE  LoopVar4;

  NODE  LoopVar2;

  NODE  LoopVar10;
  UINT8 LoopVar5;
  UINT8 *TempString3;
  UINT8 *TempString2;

  if (mMatchLen >= 4) {
    //
    // We have just got a long match, the target tree
    // can be located by MatchPos + 1. Travese the tree
    // from bottom up to get to a proper starting point.
    // The usage of PERC_FLAG ensures proper node deletion
    // in DeleteNode() later.
    //
    mMatchLen--;
    LoopVar4 = (NODE) ((mMatchPos + 1) | WNDSIZ);
    LoopVar6 = mParent[LoopVar4];
    while (LoopVar6 == NIL) {
      LoopVar4 = mNext[LoopVar4];
      LoopVar6 = mParent[LoopVar4];
    }

    while (mLevel[LoopVar6] >= mMatchLen) {
      LoopVar4 = LoopVar6;
      LoopVar6 = mParent[LoopVar6];
    }

    LoopVar10 = LoopVar6;
    while (mPosition[LoopVar10] < 0) {
      mPosition[LoopVar10]  = mPos;
      LoopVar10             = mParent[LoopVar10];
    }

    if (LoopVar10 < WNDSIZ) {
      mPosition[LoopVar10] = (NODE) (mPos | PERC_FLAG);
    }
  } else {
    //
    // Locate the target tree
    //
    LoopVar6 = (NODE) (mText[mPos] + WNDSIZ);
    LoopVar5 = mText[mPos + 1];
    LoopVar4 = Child (LoopVar6, LoopVar5);
    if (LoopVar4 == NIL) {
      MakeChild (LoopVar6, LoopVar5, mPos);
      mMatchLen = 1;
      return ;
    }

    mMatchLen = 2;
  }
  //
  // Traverse down the tree to find a match.
  // Update Position value along the route.
  // Node split or creation is involved.
  //
  for (;;) {
    if (LoopVar4 >= WNDSIZ) {
      LoopVar2         = MAXMATCH;
      mMatchPos = LoopVar4;
    } else {
      LoopVar2         = mLevel[LoopVar4];
      mMatchPos = (NODE) (mPosition[LoopVar4] & ~PERC_FLAG);
    }

    if (mMatchPos >= mPos) {
      mMatchPos -= WNDSIZ;
    }

    TempString3  = &mText[mPos + mMatchLen];
    TempString2  = &mText[mMatchPos + mMatchLen];
    while (mMatchLen < LoopVar2) {
      if (*TempString3 != *TempString2) {
        Split (LoopVar4);
        return ;
      }

      mMatchLen++;
      TempString3++;
      TempString2++;
    }

    if (mMatchLen >= MAXMATCH) {
      break;
    }

    mPosition[LoopVar4]  = mPos;
    LoopVar6             = LoopVar4;
    LoopVar4             = Child (LoopVar6, *TempString3);
    if (LoopVar4 == NIL) {
      MakeChild (LoopVar6, *TempString3, mPos);
      return ;
    }

    mMatchLen++;
  }

  LoopVar10             = mPrev[LoopVar4];
  mPrev[mPos]   = LoopVar10;
  mNext[LoopVar10]      = mPos;
  LoopVar10             = mNext[LoopVar4];
  mNext[mPos]   = LoopVar10;
  mPrev[LoopVar10]      = mPos;
  mParent[mPos] = LoopVar6;
  mParent[LoopVar4]    = NIL;

  //
  // Special usage of 'next'
  //
  mNext[LoopVar4] = mPos;

}

/**
  Delete outdated string info. (The Usage of PERC_FLAG
  ensures a clean deletion).

**/
VOID
DeleteNode (
  VOID
  )
{
  NODE  LoopVar6;

  NODE  LoopVar4;

  NODE  LoopVar11;

  NODE  LoopVar10;

  NODE  LoopVar9;

  if (mParent[mPos] == NIL) {
    return ;
  }

  LoopVar4             = mPrev[mPos];
  LoopVar11             = mNext[mPos];
  mNext[LoopVar4]      = LoopVar11;
  mPrev[LoopVar11]      = LoopVar4;
  LoopVar4             = mParent[mPos];
  mParent[mPos] = NIL;
  if (LoopVar4 >= WNDSIZ) {
    return ;
  }

  mChildCount[LoopVar4]--;
  if (mChildCount[LoopVar4] > 1) {
    return ;
  }

  LoopVar10 = (NODE) (mPosition[LoopVar4] & ~PERC_FLAG);
  if (LoopVar10 >= mPos) {
    LoopVar10 -= WNDSIZ;
  }

  LoopVar11 = LoopVar10;
  LoopVar6 = mParent[LoopVar4];
  LoopVar9 = mPosition[LoopVar6];
  while ((LoopVar9 & PERC_FLAG) != 0){
    LoopVar9 &= ~PERC_FLAG;
    if (LoopVar9 >= mPos) {
      LoopVar9 -= WNDSIZ;
    }

    if (LoopVar9 > LoopVar11) {
      LoopVar11 = LoopVar9;
    }

    mPosition[LoopVar6]  = (NODE) (LoopVar11 | WNDSIZ);
    LoopVar6             = mParent[LoopVar6];
    LoopVar9             = mPosition[LoopVar6];
  }

  if (LoopVar6 < WNDSIZ) {
    if (LoopVar9 >= mPos) {
      LoopVar9 -= WNDSIZ;
    }

    if (LoopVar9 > LoopVar11) {
      LoopVar11 = LoopVar9;
    }

    mPosition[LoopVar6] = (NODE) (LoopVar11 | WNDSIZ | PERC_FLAG);
  }

  LoopVar11           = Child (LoopVar4, mText[LoopVar10 + mLevel[LoopVar4]]);
  LoopVar10           = mPrev[LoopVar11];
  LoopVar9           = mNext[LoopVar11];
  mNext[LoopVar10]    = LoopVar9;
  mPrev[LoopVar9]    = LoopVar10;
  LoopVar10           = mPrev[LoopVar4];
  mNext[LoopVar10]    = LoopVar11;
  mPrev[LoopVar11]    = LoopVar10;
  LoopVar10           = mNext[LoopVar4];
  mPrev[LoopVar10]    = LoopVar11;
  mNext[LoopVar11]    = LoopVar10;
  mParent[LoopVar11]  = mParent[LoopVar4];
  mParent[LoopVar4]  = NIL;
  mNext[LoopVar4]    = mAvail;
  mAvail      = LoopVar4;
}

/**
  Read in source data

  @param[out] LoopVar7   The buffer to hold the data.
  @param[in] LoopVar8    The number of bytes to read.

  @return The number of bytes actually read.
**/
INT32
FreadCrc (
  OUT UINT8 *LoopVar7,
  IN  INT32 LoopVar8
  )
{
  INT32 LoopVar1;

  for (LoopVar1 = 0; mSrc < mSrcUpperLimit && LoopVar1 < LoopVar8; LoopVar1++) {
    *LoopVar7++ = *mSrc++;
  }

  LoopVar8 = LoopVar1;

  LoopVar7 -= LoopVar8;
  mOrigSize += LoopVar8;
  LoopVar1--;
  while (LoopVar1 >= 0) {
    UPDATE_CRC (*LoopVar7++);
    LoopVar1--;
  }

  return LoopVar8;
}

/**
  Advance the current position (read in new data if needed).
  Delete outdated string info. Find a match string for current position.

  @retval TRUE      The operation was successful.
  @retval FALSE     The operation failed due to insufficient memory.
**/
BOOLEAN
GetNextMatch (
  VOID
  )
{
  INT32 LoopVar8;
  VOID  *Temp;

  mRemainder--;
  mPos++;
  if (mPos == WNDSIZ * 2) {
    Temp = AllocateZeroPool (WNDSIZ + MAXMATCH);
    if (Temp == NULL) {
      return (FALSE);
    }
    CopyMem (Temp, &mText[WNDSIZ], WNDSIZ + MAXMATCH);
    CopyMem (&mText[0], Temp, WNDSIZ + MAXMATCH);
    FreePool (Temp);
    LoopVar8 = FreadCrc (&mText[WNDSIZ + MAXMATCH], WNDSIZ);
    mRemainder += LoopVar8;
    mPos = WNDSIZ;
  }

  DeleteNode ();
  InsertNode ();

  return (TRUE);
}

/**
  Send entry LoopVar1 down the queue.

  @param[in] LoopVar1    The index of the item to move.
**/
VOID
DownHeap (
  IN INT32 i
  )
{
  INT32 LoopVar1;

  INT32 LoopVar2;

  //
  // priority queue: send i-th entry down heap
  //
  LoopVar2 = mHeap[i];
  LoopVar1 = 2 * i;
  while (LoopVar1 <= mHeapSize) {
    if (LoopVar1 < mHeapSize && mFreq[mHeap[LoopVar1]] > mFreq[mHeap[LoopVar1 + 1]]) {
      LoopVar1++;
    }

    if (mFreq[LoopVar2] <= mFreq[mHeap[LoopVar1]]) {
      break;
    }

    mHeap[i]  = mHeap[LoopVar1];
    i         = LoopVar1;
    LoopVar1         = 2 * i;
  }

  mHeap[i] = (INT16) LoopVar2;
}

/**
  Count the number of each code length for a Huffman tree.

  @param[in] LoopVar1      The top node.
**/
VOID
CountLen (
  IN INT32 LoopVar1
  )
{
  if (LoopVar1 < mTempInt32) {
    mLenCnt[(mHuffmanDepth < 16) ? mHuffmanDepth : 16]++;
  } else {
    mHuffmanDepth++;
    CountLen (mLeft[LoopVar1]);
    CountLen (mRight[LoopVar1]);
    mHuffmanDepth--;
  }
}

/**
  Create code length array for a Huffman tree.

  @param[in] Root   The root of the tree.
**/
VOID
MakeLen (
  IN INT32 Root
  )
{
  INT32   LoopVar1;

  INT32   LoopVar2;
  UINT32  Cum;

  for (LoopVar1 = 0; LoopVar1 <= 16; LoopVar1++) {
    mLenCnt[LoopVar1] = 0;
  }

  CountLen (Root);

  //
  // Adjust the length count array so that
  // no code will be generated longer than its designated length
  //
  Cum = 0;
  for (LoopVar1 = 16; LoopVar1 > 0; LoopVar1--) {
    Cum += mLenCnt[LoopVar1] << (16 - LoopVar1);
  }

  while (Cum != (1U << 16)) {
    mLenCnt[16]--;
    for (LoopVar1 = 15; LoopVar1 > 0; LoopVar1--) {
      if (mLenCnt[LoopVar1] != 0) {
        mLenCnt[LoopVar1]--;
        mLenCnt[LoopVar1 + 1] += 2;
        break;
      }
    }

    Cum--;
  }

  for (LoopVar1 = 16; LoopVar1 > 0; LoopVar1--) {
    LoopVar2 = mLenCnt[LoopVar1];
    LoopVar2--;
    while (LoopVar2 >= 0) {
      mLen[*mSortPtr++] = (UINT8) LoopVar1;
      LoopVar2--;
    }
  }
}

/**
  Assign code to each symbol based on the code length array.

  @param[in] LoopVar8      The number of symbols.
  @param[in] Len    The code length array.
  @param[out] Code  The stores codes for each symbol.
**/
VOID
MakeCode (
  IN  INT32         LoopVar8,
  IN  UINT8 Len[    ],
  OUT UINT16 Code[  ]
  )
{
  INT32   LoopVar1;
  UINT16  Start[18];

  Start[1] = 0;
  for (LoopVar1 = 1; LoopVar1 <= 16; LoopVar1++) {
    Start[LoopVar1 + 1] = (UINT16) ((Start[LoopVar1] + mLenCnt[LoopVar1]) << 1);
  }

  for (LoopVar1 = 0; LoopVar1 < LoopVar8; LoopVar1++) {
    Code[LoopVar1] = Start[Len[LoopVar1]]++;
  }
}

/**
  Generates Huffman codes given a frequency distribution of symbols.

  @param[in] NParm      The number of symbols.
  @param[in] FreqParm   The frequency of each symbol.
  @param[out] LenParm   The code length for each symbol.
  @param[out] CodeParm  The code for each symbol.

  @return The root of the Huffman tree.
**/
INT32
MakeTree (
  IN  INT32             NParm,
  IN  UINT16  FreqParm[ ],
  OUT UINT8   LenParm[  ],
  OUT UINT16  CodeParm[ ]
  )
{
  INT32 LoopVar1;

  INT32 LoopVar2;

  INT32 LoopVar3;

  INT32 Avail;

  //
  // make tree, calculate len[], return root
  //
  mTempInt32        = NParm;
  mFreq     = FreqParm;
  mLen      = LenParm;
  Avail     = mTempInt32;
  mHeapSize = 0;
  mHeap[1]  = 0;
  for (LoopVar1 = 0; LoopVar1 < mTempInt32; LoopVar1++) {
    mLen[LoopVar1] = 0;
    if ((mFreq[LoopVar1]) != 0) {
      mHeapSize++;
      mHeap[mHeapSize] = (INT16) LoopVar1;
    }
  }

  if (mHeapSize < 2) {
    CodeParm[mHeap[1]] = 0;
    return mHeap[1];
  }

  for (LoopVar1 = mHeapSize / 2; LoopVar1 >= 1; LoopVar1--) {
    //
    // make priority queue
    //
    DownHeap (LoopVar1);
  }

  mSortPtr = CodeParm;
  do {
    LoopVar1 = mHeap[1];
    if (LoopVar1 < mTempInt32) {
      *mSortPtr++ = (UINT16) LoopVar1;
    }

    mHeap[1] = mHeap[mHeapSize--];
    DownHeap (1);
    LoopVar2 = mHeap[1];
    if (LoopVar2 < mTempInt32) {
      *mSortPtr++ = (UINT16) LoopVar2;
    }

    LoopVar3         = Avail++;
    mFreq[LoopVar3]  = (UINT16) (mFreq[LoopVar1] + mFreq[LoopVar2]);
    mHeap[1]  = (INT16) LoopVar3;
    DownHeap (1);
    mLeft[LoopVar3]  = (UINT16) LoopVar1;
    mRight[LoopVar3] = (UINT16) LoopVar2;
  } while (mHeapSize > 1);

  mSortPtr = CodeParm;
  MakeLen (LoopVar3);
  MakeCode (NParm, LenParm, CodeParm);

  //
  // return root
  //
  return LoopVar3;
}

/**
  Outputs rightmost LoopVar8 bits of x

  @param[in] LoopVar8   The rightmost LoopVar8 bits of the data is used.
  @param[in] x   The data.
**/
VOID
PutBits (
  IN INT32    LoopVar8,
  IN UINT32   x
  )
{
  UINT8 Temp;

  if (LoopVar8 < mBitCount) {
    mSubBitBuf |= x << (mBitCount -= LoopVar8);
  } else {

    Temp = (UINT8)(mSubBitBuf | (x >> (LoopVar8 -= mBitCount)));
    if (mDst < mDstUpperLimit) {
      *mDst++ = Temp;
    }
    mCompSize++;

    if (LoopVar8 < UINT8_BIT) {
      mSubBitBuf = x << (mBitCount = UINT8_BIT - LoopVar8);
    } else {

      Temp = (UINT8)(x >> (LoopVar8 - UINT8_BIT));
      if (mDst < mDstUpperLimit) {
        *mDst++ = Temp;
      }
      mCompSize++;

      mSubBitBuf = x << (mBitCount = 2 * UINT8_BIT - LoopVar8);
    }
  }
}

/**
  Encode a signed 32 bit number.

  @param[in] LoopVar5     The number to encode.
**/
VOID
EncodeC (
  IN INT32 LoopVar5
  )
{
  PutBits (mCLen[LoopVar5], mCCode[LoopVar5]);
}

/**
  Encode a unsigned 32 bit number.

  @param[in] LoopVar7     The number to encode.
**/
VOID
EncodeP (
  IN UINT32 LoopVar7
  )
{
  UINT32  LoopVar5;

  UINT32  LoopVar6;

  LoopVar5 = 0;
  LoopVar6 = LoopVar7;
  while (LoopVar6 != 0) {
    LoopVar6 >>= 1;
    LoopVar5++;
  }

  PutBits (mPTLen[LoopVar5], mPTCode[LoopVar5]);
  if (LoopVar5 > 1) {
    PutBits(LoopVar5 - 1, LoopVar7 & (0xFFFFU >> (17 - LoopVar5)));
  }
}

/**
  Count the frequencies for the Extra Set.

**/
VOID
CountTFreq (
  VOID
  )
{
  INT32 LoopVar1;

  INT32 LoopVar3;

  INT32 LoopVar8;

  INT32 Count;

  for (LoopVar1 = 0; LoopVar1 < NT; LoopVar1++) {
    mTFreq[LoopVar1] = 0;
  }

  LoopVar8 = NC;
  while (LoopVar8 > 0 && mCLen[LoopVar8 - 1] == 0) {
    LoopVar8--;
  }

  LoopVar1 = 0;
  while (LoopVar1 < LoopVar8) {
    LoopVar3 = mCLen[LoopVar1++];
    if (LoopVar3 == 0) {
      Count = 1;
      while (LoopVar1 < LoopVar8 && mCLen[LoopVar1] == 0) {
        LoopVar1++;
        Count++;
      }

      if (Count <= 2) {
        mTFreq[0] = (UINT16) (mTFreq[0] + Count);
      } else if (Count <= 18) {
        mTFreq[1]++;
      } else if (Count == 19) {
        mTFreq[0]++;
        mTFreq[1]++;
      } else {
        mTFreq[2]++;
      }
    } else {
      ASSERT((LoopVar3+2)<(2 * NT - 1));
      mTFreq[LoopVar3 + 2]++;
    }
  }
}

/**
  Outputs the code length array for the Extra Set or the Position Set.

  @param[in] LoopVar8       The number of symbols.
  @param[in] nbit           The number of bits needed to represent 'LoopVar8'.
  @param[in] Special        The special symbol that needs to be take care of.

**/
VOID
WritePTLen (
  IN INT32 LoopVar8,
  IN INT32 nbit,
  IN INT32 Special
  )
{
  INT32 LoopVar1;

  INT32 LoopVar3;

  while (LoopVar8 > 0 && mPTLen[LoopVar8 - 1] == 0) {
    LoopVar8--;
  }

  PutBits (nbit, LoopVar8);
  LoopVar1 = 0;
  while (LoopVar1 < LoopVar8) {
    LoopVar3 = mPTLen[LoopVar1++];
    if (LoopVar3 <= 6) {
      PutBits (3, LoopVar3);
    } else {
      PutBits (LoopVar3 - 3, (1U << (LoopVar3 - 3)) - 2);
    }

    if (LoopVar1 == Special) {
      while (LoopVar1 < 6 && mPTLen[LoopVar1] == 0) {
        LoopVar1++;
      }

      PutBits (2, (LoopVar1 - 3) & 3);
    }
  }
}

/**
  Outputs the code length array for Char&Length Set.
**/
VOID
WriteCLen (
  VOID
  )
{
  INT32 LoopVar1;

  INT32 LoopVar3;

  INT32 LoopVar8;

  INT32 Count;

  LoopVar8 = NC;
  while (LoopVar8 > 0 && mCLen[LoopVar8 - 1] == 0) {
    LoopVar8--;
  }

  PutBits (CBIT, LoopVar8);
  LoopVar1 = 0;
  while (LoopVar1 < LoopVar8) {
    LoopVar3 = mCLen[LoopVar1++];
    if (LoopVar3 == 0) {
      Count = 1;
      while (LoopVar1 < LoopVar8 && mCLen[LoopVar1] == 0) {
        LoopVar1++;
        Count++;
      }

      if (Count <= 2) {
        for (LoopVar3 = 0; LoopVar3 < Count; LoopVar3++) {
          PutBits (mPTLen[0], mPTCode[0]);
        }
      } else if (Count <= 18) {
        PutBits (mPTLen[1], mPTCode[1]);
        PutBits (4, Count - 3);
      } else if (Count == 19) {
        PutBits (mPTLen[0], mPTCode[0]);
        PutBits (mPTLen[1], mPTCode[1]);
        PutBits (4, 15);
      } else {
        PutBits (mPTLen[2], mPTCode[2]);
        PutBits (CBIT, Count - 20);
      }
    } else {
      ASSERT((LoopVar3+2)<NPT);
      PutBits (mPTLen[LoopVar3 + 2], mPTCode[LoopVar3 + 2]);
    }
  }
}

/**
  Huffman code the block and output it.

**/
VOID
SendBlock (
  VOID
  )
{
  UINT32  LoopVar1;

  UINT32  LoopVar3;

  UINT32  Flags;

  UINT32  Root;

  UINT32  Pos;

  UINT32  Size;
  Flags = 0;

  Root  = MakeTree (NC, mCFreq, mCLen, mCCode);
  Size  = mCFreq[Root];
  PutBits (16, Size);
  if (Root >= NC) {
    CountTFreq ();
    Root = MakeTree (NT, mTFreq, mPTLen, mPTCode);
    if (Root >= NT) {
      WritePTLen (NT, TBIT, 3);
    } else {
      PutBits (TBIT, 0);
      PutBits (TBIT, Root);
    }

    WriteCLen ();
  } else {
    PutBits (TBIT, 0);
    PutBits (TBIT, 0);
    PutBits (CBIT, 0);
    PutBits (CBIT, Root);
  }

  Root = MakeTree (NP, mPFreq, mPTLen, mPTCode);
  if (Root >= NP) {
    WritePTLen (NP, PBIT, -1);
  } else {
    PutBits (PBIT, 0);
    PutBits (PBIT, Root);
  }

  Pos = 0;
  for (LoopVar1 = 0; LoopVar1 < Size; LoopVar1++) {
    if (LoopVar1 % UINT8_BIT == 0) {
      Flags = mBuf[Pos++];
    } else {
      Flags <<= 1;
    }
    if ((Flags & (1U << (UINT8_BIT - 1))) != 0){
      EncodeC(mBuf[Pos++] + (1U << UINT8_BIT));
      LoopVar3 = mBuf[Pos++] << UINT8_BIT;
      LoopVar3 += mBuf[Pos++];

      EncodeP (LoopVar3);
    } else {
      EncodeC (mBuf[Pos++]);
    }
  }

  SetMem (mCFreq, NC * sizeof (UINT16), 0);
  SetMem (mPFreq, NP * sizeof (UINT16), 0);
}

/**
  Start the huffman encoding.

**/
VOID
HufEncodeStart (
  VOID
  )
{
  SetMem (mCFreq, NC * sizeof (UINT16), 0);
  SetMem (mPFreq, NP * sizeof (UINT16), 0);

  mOutputPos = mOutputMask = 0;

  mBitCount   = UINT8_BIT;
  mSubBitBuf  = 0;
}

/**
  Outputs an Original Character or a Pointer.

  @param[in] LoopVar5     The original character or the 'String Length' element of
                   a Pointer.
  @param[in] LoopVar7     The 'Position' field of a Pointer.
**/
VOID
CompressOutput (
  IN UINT32 LoopVar5,
  IN UINT32 LoopVar7
  )
{
  STATIC UINT32 CPos;

  if ((mOutputMask >>= 1) == 0) {
    mOutputMask = 1U << (UINT8_BIT - 1);
    if (mOutputPos >= mBufSiz - 3 * UINT8_BIT) {
      SendBlock ();
      mOutputPos = 0;
    }

    CPos        = mOutputPos++;
    mBuf[CPos]  = 0;
  }
  mBuf[mOutputPos++] = (UINT8) LoopVar5;
  mCFreq[LoopVar5]++;
  if (LoopVar5 >= (1U << UINT8_BIT)) {
    mBuf[CPos] = (UINT8)(mBuf[CPos]|mOutputMask);
    mBuf[mOutputPos++] = (UINT8)(LoopVar7 >> UINT8_BIT);
    mBuf[mOutputPos++] = (UINT8) LoopVar7;
    LoopVar5                  = 0;
    while (LoopVar7!=0) {
      LoopVar7 >>= 1;
      LoopVar5++;
    }
    mPFreq[LoopVar5]++;
  }
}

/**
  End the huffman encoding.

**/
VOID
HufEncodeEnd (
  VOID
  )
{
  SendBlock ();

  //
  // Flush remaining bits
  //
  PutBits (UINT8_BIT - 1, 0);
}

/**
  The main controlling routine for compression process.

  @retval EFI_SUCCESS           The compression is successful.
  @retval EFI_OUT_0F_RESOURCES  Not enough memory for compression process.
**/
EFI_STATUS
Encode (
  VOID
  )
{
  EFI_STATUS  Status;
  INT32       LastMatchLen;
  NODE        LastMatchPos;

  Status = AllocateMemory ();
  if (EFI_ERROR (Status)) {
    FreeMemory ();
    return Status;
  }

  InitSlide ();

  HufEncodeStart ();

  mRemainder  = FreadCrc (&mText[WNDSIZ], WNDSIZ + MAXMATCH);

  mMatchLen   = 0;
  mPos        = WNDSIZ;
  InsertNode ();
  if (mMatchLen > mRemainder) {
    mMatchLen = mRemainder;
  }

  while (mRemainder > 0) {
    LastMatchLen  = mMatchLen;
    LastMatchPos  = mMatchPos;
    if (!GetNextMatch ()) {
      Status = EFI_OUT_OF_RESOURCES;
    }
    if (mMatchLen > mRemainder) {
      mMatchLen = mRemainder;
    }

    if (mMatchLen > LastMatchLen || LastMatchLen < THRESHOLD) {
      //
      // Not enough benefits are gained by outputting a pointer,
      // so just output the original character
      //
      CompressOutput(mText[mPos - 1], 0);
    } else {
      //
      // Outputting a pointer is beneficial enough, do it.
      //

      CompressOutput(LastMatchLen + (UINT8_MAX + 1 - THRESHOLD),
             (mPos - LastMatchPos - 2) & (WNDSIZ - 1));
      LastMatchLen--;
      while (LastMatchLen > 0) {
        if (!GetNextMatch ()) {
          Status = EFI_OUT_OF_RESOURCES;
        }
        LastMatchLen--;
      }

      if (mMatchLen > mRemainder) {
        mMatchLen = mRemainder;
      }
    }
  }

  HufEncodeEnd ();
  FreeMemory ();
  return (Status);
}

/**
  The compression routine.

  @param[in]       SrcBuffer     The buffer containing the source data.
  @param[in]       SrcSize       Number of bytes in SrcBuffer.
  @param[in]       DstBuffer     The buffer to put the compressed image in.
  @param[in, out]  DstSize       On input the size (in bytes) of DstBuffer, on
                                 return the number of bytes placed in DstBuffer.

  @retval EFI_SUCCESS           The compression was sucessful.
  @retval EFI_BUFFER_TOO_SMALL  The buffer was too small.  DstSize is required.
**/
EFI_STATUS
Compress (
  IN      VOID    *SrcBuffer,
  IN      UINT64  SrcSize,
  IN      VOID    *DstBuffer,
  IN OUT  UINT64  *DstSize
  )
{
  EFI_STATUS  Status;

  //
  // Initializations
  //
  mBufSiz         = 0;
  mBuf            = NULL;
  mText           = NULL;
  mLevel          = NULL;
  mChildCount     = NULL;
  mPosition       = NULL;
  mParent         = NULL;
  mPrev           = NULL;
  mNext           = NULL;

  mSrc            = SrcBuffer;
  mSrcUpperLimit  = mSrc + SrcSize;
  mDst            = DstBuffer;
  mDstUpperLimit  = mDst +*DstSize;

  PutDword (0L);
  PutDword (0L);

  MakeCrcTable ();

  mOrigSize             = mCompSize = 0;
  mCrc                  = INIT_CRC;

  //
  // Compress it
  //
  Status = Encode ();
  if (EFI_ERROR (Status)) {
    return EFI_OUT_OF_RESOURCES;
  }
  //
  // Null terminate the compressed data
  //
  if (mDst < mDstUpperLimit) {
    *mDst++ = 0;
  }
  //
  // Fill in compressed size and original size
  //
  mDst = DstBuffer;
  PutDword (mCompSize + 1);
  PutDword (mOrigSize);

  //
  // Return
  //
  if (mCompSize + 1 + 8 > *DstSize) {
    *DstSize = mCompSize + 1 + 8;
    return EFI_BUFFER_TOO_SMALL;
  } else {
    *DstSize = mCompSize + 1 + 8;
    return EFI_SUCCESS;
  }

}

