blob: 4fd74ccee56af4e33021e01c4cca42f03e4c1405 [file] [log] [blame]
/** @file
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) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
//
// Macro Definitions
//
#undef UINT8_MAX
typedef INT16 NODE;
#define UINT8_MAX 0xff
#define UINT8_BIT 8
#define THRESHOLD 3
#define INIT_CRC 0
#define WNDBIT 13
#define WNDSIZ (1 << WNDBIT)
#define MAXMATCH 256
#define PERC_FLAG 0x8000U
#define CODE_BIT 16
#define NIL 0
#define MAX_HASH_VAL (3 * WNDSIZ + (WNDSIZ / 512 + 1) * UINT8_MAX)
#define HASH(p, c) ((p) + ((c) << (WNDBIT - 9)) + WNDSIZ * 2)
#define CRCPOLY 0xA001
#define UPDATE_CRC(c) mCrc = mCrcTable[(mCrc ^ (c)) & 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
//
STATIC
VOID
PutDword(
IN UINT32 Data
);
STATIC
EFI_STATUS
AllocateMemory (
);
STATIC
VOID
FreeMemory (
);
STATIC
VOID
InitSlide (
);
STATIC
NODE
Child (
IN NODE q,
IN UINT8 c
);
STATIC
VOID
MakeChild (
IN NODE q,
IN UINT8 c,
IN NODE r
);
STATIC
VOID
Split (
IN NODE Old
);
STATIC
VOID
InsertNode (
);
STATIC
VOID
DeleteNode (
);
STATIC
VOID
GetNextMatch (
);
STATIC
EFI_STATUS
Encode (
);
STATIC
VOID
CountTFreq (
);
STATIC
VOID
WritePTLen (
IN INT32 n,
IN INT32 nbit,
IN INT32 Special
);
STATIC
VOID
WriteCLen (
);
STATIC
VOID
EncodeC (
IN INT32 c
);
STATIC
VOID
EncodeP (
IN UINT32 p
);
STATIC
VOID
SendBlock (
);
STATIC
VOID
Output (
IN UINT32 c,
IN UINT32 p
);
STATIC
VOID
HufEncodeStart (
);
STATIC
VOID
HufEncodeEnd (
);
STATIC
VOID
MakeCrcTable (
);
STATIC
VOID
PutBits (
IN INT32 n,
IN UINT32 x
);
STATIC
INT32
FreadCrc (
OUT UINT8 *p,
IN INT32 n
);
STATIC
VOID
InitPutBits (
);
STATIC
VOID
CountLen (
IN INT32 i
);
STATIC
VOID
MakeLen (
IN INT32 Root
);
STATIC
VOID
DownHeap (
IN INT32 i
);
STATIC
VOID
MakeCode (
IN INT32 n,
IN UINT8 Len[],
OUT UINT16 Code[]
);
STATIC
INT32
MakeTree (
IN INT32 NParm,
IN UINT16 FreqParm[],
OUT UINT8 LenParm[],
OUT UINT16 CodeParm[]
);
//
// Global Variables
//
STATIC UINT8 *mSrc, *mDst, *mSrcUpperLimit, *mDstUpperLimit;
STATIC UINT8 *mLevel, *mText, *mChildCount, *mBuf, mCLen[NC], mPTLen[NPT], *mLen;
STATIC INT16 mHeap[NC + 1];
STATIC INT32 mRemainder, mMatchLen, mBitCount, mHeapSize, mN;
STATIC UINT32 mBufSiz = 0, mOutputPos, mOutputMask, mSubBitBuf, mCrc;
STATIC UINT32 mCompSize, mOrigSize;
STATIC UINT16 *mFreq, *mSortPtr, mLenCnt[17], mLeft[2 * NC - 1], mRight[2 * NC - 1],
mCrcTable[UINT8_MAX + 1], mCFreq[2 * NC - 1],mCCode[NC],
mPFreq[2 * NP - 1], mPTCode[NPT], mTFreq[2 * NT - 1];
STATIC NODE mPos, mMatchPos, mAvail, *mPosition, *mParent, *mPrev, *mNext = NULL;
//
// functions
//
EFI_STATUS
EfiCompress (
IN UINT8 *SrcBuffer,
IN UINT32 SrcSize,
IN UINT8 *DstBuffer,
IN OUT UINT32 *DstSize
)
/*++
Routine Description:
The main compression routine.
Arguments:
SrcBuffer - The buffer storing the source data
SrcSize - The size of source data
DstBuffer - The buffer to store the compressed data
DstSize - On input, the size of DstBuffer; On output,
the size of the actual compressed data.
Returns:
EFI_BUFFER_TOO_SMALL - The DstBuffer is too small. In this case,
DstSize contains the size needed.
EFI_SUCCESS - Compression is successful.
--*/
{
EFI_STATUS Status = EFI_SUCCESS;
//
// 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;
}
}
STATIC
VOID
PutDword(
IN UINT32 Data
)
/*++
Routine Description:
Put a dword to output stream
Arguments:
Data - the dword to put
Returns: (VOID)
--*/
{
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);
}
}
STATIC
EFI_STATUS
AllocateMemory ()
/*++
Routine Description:
Allocate memory spaces for data structures used in compression process
Arguments: (VOID)
Returns:
EFI_SUCCESS - Memory is allocated successfully
EFI_OUT_OF_RESOURCES - Allocation fails
--*/
{
UINT32 i;
mText = malloc (WNDSIZ * 2 + MAXMATCH);
if (mText == NULL) {
return EFI_OUT_OF_RESOURCES;
}
for (i = 0 ; i < WNDSIZ * 2 + MAXMATCH; i ++) {
mText[i] = 0;
}
mLevel = malloc ((WNDSIZ + UINT8_MAX + 1) * sizeof(*mLevel));
mChildCount = malloc ((WNDSIZ + UINT8_MAX + 1) * sizeof(*mChildCount));
mPosition = malloc ((WNDSIZ + UINT8_MAX + 1) * sizeof(*mPosition));
mParent = malloc (WNDSIZ * 2 * sizeof(*mParent));
mPrev = malloc (WNDSIZ * 2 * sizeof(*mPrev));
mNext = malloc ((MAX_HASH_VAL + 1) * sizeof(*mNext));
if (mLevel == NULL || mChildCount == NULL || mPosition == NULL ||
mParent == NULL || mPrev == NULL || mNext == NULL) {
return EFI_OUT_OF_RESOURCES;
}
mBufSiz = 16 * 1024U;
while ((mBuf = malloc(mBufSiz)) == NULL) {
mBufSiz = (mBufSiz / 10U) * 9U;
if (mBufSiz < 4 * 1024U) {
return EFI_OUT_OF_RESOURCES;
}
}
mBuf[0] = 0;
return EFI_SUCCESS;
}
VOID
FreeMemory ()
/*++
Routine Description:
Called when compression is completed to free memory previously allocated.
Arguments: (VOID)
Returns: (VOID)
--*/
{
if (mText) {
free (mText);
}
if (mLevel) {
free (mLevel);
}
if (mChildCount) {
free (mChildCount);
}
if (mPosition) {
free (mPosition);
}
if (mParent) {
free (mParent);
}
if (mPrev) {
free (mPrev);
}
if (mNext) {
free (mNext);
}
if (mBuf) {
free (mBuf);
}
return;
}
STATIC
VOID
InitSlide ()
/*++
Routine Description:
Initialize String Info Log data structures
Arguments: (VOID)
Returns: (VOID)
--*/
{
NODE i;
for (i = WNDSIZ; i <= WNDSIZ + UINT8_MAX; i++) {
mLevel[i] = 1;
mPosition[i] = NIL; /* sentinel */
}
for (i = WNDSIZ; i < WNDSIZ * 2; i++) {
mParent[i] = NIL;
}
mAvail = 1;
for (i = 1; i < WNDSIZ - 1; i++) {
mNext[i] = (NODE)(i + 1);
}
mNext[WNDSIZ - 1] = NIL;
for (i = WNDSIZ * 2; i <= MAX_HASH_VAL; i++) {
mNext[i] = NIL;
}
}
STATIC
NODE
Child (
IN NODE q,
IN UINT8 c
)
/*++
Routine Description:
Find child node given the parent node and the edge character
Arguments:
q - the parent node
c - the edge character
Returns:
The child node (NIL if not found)
--*/
{
NODE r;
r = mNext[HASH(q, c)];
mParent[NIL] = q; /* sentinel */
while (mParent[r] != q) {
r = mNext[r];
}
return r;
}
STATIC
VOID
MakeChild (
IN NODE q,
IN UINT8 c,
IN NODE r
)
/*++
Routine Description:
Create a new child for a given parent node.
Arguments:
q - the parent node
c - the edge character
r - the child node
Returns: (VOID)
--*/
{
NODE h, t;
h = (NODE)HASH(q, c);
t = mNext[h];
mNext[h] = r;
mNext[r] = t;
mPrev[t] = r;
mPrev[r] = h;
mParent[r] = q;
mChildCount[q]++;
}
STATIC
VOID
Split (
NODE Old
)
/*++
Routine Description:
Split a node.
Arguments:
Old - the node to split
Returns: (VOID)
--*/
{
NODE New, t;
New = mAvail;
mAvail = mNext[New];
mChildCount[New] = 0;
t = mPrev[Old];
mPrev[New] = t;
mNext[t] = New;
t = mNext[Old];
mNext[New] = t;
mPrev[t] = New;
mParent[New] = mParent[Old];
mLevel[New] = (UINT8)mMatchLen;
mPosition[New] = mPos;
MakeChild(New, mText[mMatchPos + mMatchLen], Old);
MakeChild(New, mText[mPos + mMatchLen], mPos);
}
STATIC
VOID
InsertNode ()
/*++
Routine Description:
Insert string info for current position into the String Info Log
Arguments: (VOID)
Returns: (VOID)
--*/
{
NODE q, r, j, t;
UINT8 c, *t1, *t2;
if (mMatchLen >= 4) {
//
// We have just got a long match, the target tree
// can be located by MatchPos + 1. Traverse 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--;
r = (INT16)((mMatchPos + 1) | WNDSIZ);
while ((q = mParent[r]) == NIL) {
r = mNext[r];
}
while (mLevel[q] >= mMatchLen) {
r = q; q = mParent[q];
}
t = q;
while (mPosition[t] < 0) {
mPosition[t] = mPos;
t = mParent[t];
}
if (t < WNDSIZ) {
mPosition[t] = (NODE)(mPos | PERC_FLAG);
}
} else {
//
// Locate the target tree
//
q = (INT16)(mText[mPos] + WNDSIZ);
c = mText[mPos + 1];
if ((r = Child(q, c)) == NIL) {
MakeChild(q, c, 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 (r >= WNDSIZ) {
j = MAXMATCH;
mMatchPos = r;
} else {
j = mLevel[r];
mMatchPos = (NODE)(mPosition[r] & ~PERC_FLAG);
}
if (mMatchPos >= mPos) {
mMatchPos -= WNDSIZ;
}
t1 = &mText[mPos + mMatchLen];
t2 = &mText[mMatchPos + mMatchLen];
while (mMatchLen < j) {
if (*t1 != *t2) {
Split(r);
return;
}
mMatchLen++;
t1++;
t2++;
}
if (mMatchLen >= MAXMATCH) {
break;
}
mPosition[r] = mPos;
q = r;
if ((r = Child(q, *t1)) == NIL) {
MakeChild(q, *t1, mPos);
return;
}
mMatchLen++;
}
t = mPrev[r];
mPrev[mPos] = t;
mNext[t] = mPos;
t = mNext[r];
mNext[mPos] = t;
mPrev[t] = mPos;
mParent[mPos] = q;
mParent[r] = NIL;
//
// Special usage of 'next'
//
mNext[r] = mPos;
}
STATIC
VOID
DeleteNode ()
/*++
Routine Description:
Delete outdated string info. (The Usage of PERC_FLAG
ensures a clean deletion)
Arguments: (VOID)
Returns: (VOID)
--*/
{
NODE q, r, s, t, u;
if (mParent[mPos] == NIL) {
return;
}
r = mPrev[mPos];
s = mNext[mPos];
mNext[r] = s;
mPrev[s] = r;
r = mParent[mPos];
mParent[mPos] = NIL;
if (r >= WNDSIZ || --mChildCount[r] > 1) {
return;
}
t = (NODE)(mPosition[r] & ~PERC_FLAG);
if (t >= mPos) {
t -= WNDSIZ;
}
s = t;
q = mParent[r];
while ((u = mPosition[q]) & PERC_FLAG) {
u &= ~PERC_FLAG;
if (u >= mPos) {
u -= WNDSIZ;
}
if (u > s) {
s = u;
}
mPosition[q] = (INT16)(s | WNDSIZ);
q = mParent[q];
}
if (q < WNDSIZ) {
if (u >= mPos) {
u -= WNDSIZ;
}
if (u > s) {
s = u;
}
mPosition[q] = (INT16)(s | WNDSIZ | PERC_FLAG);
}
s = Child(r, mText[t + mLevel[r]]);
t = mPrev[s];
u = mNext[s];
mNext[t] = u;
mPrev[u] = t;
t = mPrev[r];
mNext[t] = s;
mPrev[s] = t;
t = mNext[r];
mPrev[t] = s;
mNext[s] = t;
mParent[s] = mParent[r];
mParent[r] = NIL;
mNext[r] = mAvail;
mAvail = r;
}
STATIC
VOID
GetNextMatch ()
/*++
Routine Description:
Advance the current position (read in new data if needed).
Delete outdated string info. Find a match string for current position.
Arguments: (VOID)
Returns: (VOID)
--*/
{
INT32 n;
mRemainder--;
if (++mPos == WNDSIZ * 2) {
memmove(&mText[0], &mText[WNDSIZ], WNDSIZ + MAXMATCH);
n = FreadCrc(&mText[WNDSIZ + MAXMATCH], WNDSIZ);
mRemainder += n;
mPos = WNDSIZ;
}
DeleteNode();
InsertNode();
}
STATIC
EFI_STATUS
Encode ()
/*++
Routine Description:
The main controlling routine for compression process.
Arguments: (VOID)
Returns:
EFI_SUCCESS - The compression is successful
EFI_OUT_0F_RESOURCES - Not enough memory for compression process
--*/
{
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;
GetNextMatch();
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
//
Output(mText[mPos - 1], 0);
} else {
//
// Outputting a pointer is beneficial enough, do it.
//
Output(LastMatchLen + (UINT8_MAX + 1 - THRESHOLD),
(mPos - LastMatchPos - 2) & (WNDSIZ - 1));
while (--LastMatchLen > 0) {
GetNextMatch();
}
if (mMatchLen > mRemainder) {
mMatchLen = mRemainder;
}
}
}
HufEncodeEnd();
FreeMemory();
return EFI_SUCCESS;
}
STATIC
VOID
CountTFreq ()
/*++
Routine Description:
Count the frequencies for the Extra Set
Arguments: (VOID)
Returns: (VOID)
--*/
{
INT32 i, k, n, Count;
for (i = 0; i < NT; i++) {
mTFreq[i] = 0;
}
n = NC;
while (n > 0 && mCLen[n - 1] == 0) {
n--;
}
i = 0;
while (i < n) {
k = mCLen[i++];
if (k == 0) {
Count = 1;
while (i < n && mCLen[i] == 0) {
i++;
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 {
mTFreq[k + 2]++;
}
}
}
STATIC
VOID
WritePTLen (
IN INT32 n,
IN INT32 nbit,
IN INT32 Special
)
/*++
Routine Description:
Outputs the code length array for the Extra Set or the Position Set.
Arguments:
n - the number of symbols
nbit - the number of bits needed to represent 'n'
Special - the special symbol that needs to be take care of
Returns: (VOID)
--*/
{
INT32 i, k;
while (n > 0 && mPTLen[n - 1] == 0) {
n--;
}
PutBits(nbit, n);
i = 0;
while (i < n) {
k = mPTLen[i++];
if (k <= 6) {
PutBits(3, k);
} else {
PutBits(k - 3, (1U << (k - 3)) - 2);
}
if (i == Special) {
while (i < 6 && mPTLen[i] == 0) {
i++;
}
PutBits(2, (i - 3) & 3);
}
}
}
STATIC
VOID
WriteCLen ()
/*++
Routine Description:
Outputs the code length array for Char&Length Set
Arguments: (VOID)
Returns: (VOID)
--*/
{
INT32 i, k, n, Count;
n = NC;
while (n > 0 && mCLen[n - 1] == 0) {
n--;
}
PutBits(CBIT, n);
i = 0;
while (i < n) {
k = mCLen[i++];
if (k == 0) {
Count = 1;
while (i < n && mCLen[i] == 0) {
i++;
Count++;
}
if (Count <= 2) {
for (k = 0; k < Count; k++) {
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 {
PutBits(mPTLen[k + 2], mPTCode[k + 2]);
}
}
}
STATIC
VOID
EncodeC (
IN INT32 c
)
{
PutBits(mCLen[c], mCCode[c]);
}
STATIC
VOID
EncodeP (
IN UINT32 p
)
{
UINT32 c, q;
c = 0;
q = p;
while (q) {
q >>= 1;
c++;
}
PutBits(mPTLen[c], mPTCode[c]);
if (c > 1) {
PutBits(c - 1, p & (0xFFFFU >> (17 - c)));
}
}
STATIC
VOID
SendBlock ()
/*++
Routine Description:
Huffman code the block and output it.
Argument: (VOID)
Returns: (VOID)
--*/
{
UINT32 i, k, Flags, Root, Pos, 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 (i = 0; i < Size; i++) {
if (i % UINT8_BIT == 0) {
Flags = mBuf[Pos++];
} else {
Flags <<= 1;
}
if (Flags & (1U << (UINT8_BIT - 1))) {
EncodeC(mBuf[Pos++] + (1U << UINT8_BIT));
k = mBuf[Pos++] << UINT8_BIT;
k += mBuf[Pos++];
EncodeP(k);
} else {
EncodeC(mBuf[Pos++]);
}
}
for (i = 0; i < NC; i++) {
mCFreq[i] = 0;
}
for (i = 0; i < NP; i++) {
mPFreq[i] = 0;
}
}
STATIC
VOID
Output (
IN UINT32 c,
IN UINT32 p
)
/*++
Routine Description:
Outputs an Original Character or a Pointer
Arguments:
c - The original character or the 'String Length' element of a Pointer
p - The 'Position' field of a Pointer
Returns: (VOID)
--*/
{
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) c;
mCFreq[c]++;
if (c >= (1U << UINT8_BIT)) {
mBuf[CPos] |= mOutputMask;
mBuf[mOutputPos++] = (UINT8)(p >> UINT8_BIT);
mBuf[mOutputPos++] = (UINT8) p;
c = 0;
while (p) {
p >>= 1;
c++;
}
mPFreq[c]++;
}
}
STATIC
VOID
HufEncodeStart ()
{
INT32 i;
for (i = 0; i < NC; i++) {
mCFreq[i] = 0;
}
for (i = 0; i < NP; i++) {
mPFreq[i] = 0;
}
mOutputPos = mOutputMask = 0;
InitPutBits();
return;
}
STATIC
VOID
HufEncodeEnd ()
{
SendBlock();
//
// Flush remaining bits
//
PutBits(UINT8_BIT - 1, 0);
return;
}
STATIC
VOID
MakeCrcTable ()
{
UINT32 i, j, r;
for (i = 0; i <= UINT8_MAX; i++) {
r = i;
for (j = 0; j < UINT8_BIT; j++) {
if (r & 1) {
r = (r >> 1) ^ CRCPOLY;
} else {
r >>= 1;
}
}
mCrcTable[i] = (UINT16)r;
}
}
STATIC
VOID
PutBits (
IN INT32 n,
IN UINT32 x
)
/*++
Routine Description:
Outputs rightmost n bits of x
Arguments:
n - the rightmost n bits of the data is used
x - the data
Returns: (VOID)
--*/
{
UINT8 Temp;
if (n < mBitCount) {
mSubBitBuf |= x << (mBitCount -= n);
} else {
Temp = (UINT8)(mSubBitBuf | (x >> (n -= mBitCount)));
if (mDst < mDstUpperLimit) {
*mDst++ = Temp;
}
mCompSize++;
if (n < UINT8_BIT) {
mSubBitBuf = x << (mBitCount = UINT8_BIT - n);
} else {
Temp = (UINT8)(x >> (n - UINT8_BIT));
if (mDst < mDstUpperLimit) {
*mDst++ = Temp;
}
mCompSize++;
mSubBitBuf = x << (mBitCount = 2 * UINT8_BIT - n);
}
}
}
STATIC
INT32
FreadCrc (
OUT UINT8 *p,
IN INT32 n
)
/*++
Routine Description:
Read in source data
Arguments:
p - the buffer to hold the data
n - number of bytes to read
Returns:
number of bytes actually read
--*/
{
INT32 i;
for (i = 0; mSrc < mSrcUpperLimit && i < n; i++) {
*p++ = *mSrc++;
}
n = i;
p -= n;
mOrigSize += n;
while (--i >= 0) {
UPDATE_CRC(*p++);
}
return n;
}
STATIC
VOID
InitPutBits ()
{
mBitCount = UINT8_BIT;
mSubBitBuf = 0;
}
STATIC
VOID
CountLen (
IN INT32 i
)
/*++
Routine Description:
Count the number of each code length for a Huffman tree.
Arguments:
i - the top node
Returns: (VOID)
--*/
{
STATIC INT32 Depth = 0;
if (i < mN) {
mLenCnt[(Depth < 16) ? Depth : 16]++;
} else {
Depth++;
CountLen(mLeft [i]);
CountLen(mRight[i]);
Depth--;
}
}
STATIC
VOID
MakeLen (
IN INT32 Root
)
/*++
Routine Description:
Create code length array for a Huffman tree
Arguments:
Root - the root of the tree
--*/
{
INT32 i, k;
UINT32 Cum;
for (i = 0; i <= 16; i++) {
mLenCnt[i] = 0;
}
CountLen(Root);
//
// Adjust the length count array so that
// no code will be generated longer than its designated length
//
Cum = 0;
for (i = 16; i > 0; i--) {
Cum += mLenCnt[i] << (16 - i);
}
while (Cum != (1U << 16)) {
mLenCnt[16]--;
for (i = 15; i > 0; i--) {
if (mLenCnt[i] != 0) {
mLenCnt[i]--;
mLenCnt[i+1] += 2;
break;
}
}
Cum--;
}
for (i = 16; i > 0; i--) {
k = mLenCnt[i];
while (--k >= 0) {
mLen[*mSortPtr++] = (UINT8)i;
}
}
}
STATIC
VOID
DownHeap (
IN INT32 i
)
{
INT32 j, k;
//
// priority queue: send i-th entry down heap
//
k = mHeap[i];
while ((j = 2 * i) <= mHeapSize) {
if (j < mHeapSize && mFreq[mHeap[j]] > mFreq[mHeap[j + 1]]) {
j++;
}
if (mFreq[k] <= mFreq[mHeap[j]]) {
break;
}
mHeap[i] = mHeap[j];
i = j;
}
mHeap[i] = (INT16)k;
}
STATIC
VOID
MakeCode (
IN INT32 n,
IN UINT8 Len[],
OUT UINT16 Code[]
)
/*++
Routine Description:
Assign code to each symbol based on the code length array
Arguments:
n - number of symbols
Len - the code length array
Code - stores codes for each symbol
Returns: (VOID)
--*/
{
INT32 i;
UINT16 Start[18];
Start[1] = 0;
for (i = 1; i <= 16; i++) {
Start[i + 1] = (UINT16)((Start[i] + mLenCnt[i]) << 1);
}
for (i = 0; i < n; i++) {
Code[i] = Start[Len[i]]++;
}
}
STATIC
INT32
MakeTree (
IN INT32 NParm,
IN UINT16 FreqParm[],
OUT UINT8 LenParm[],
OUT UINT16 CodeParm[]
)
/*++
Routine Description:
Generates Huffman codes given a frequency distribution of symbols
Arguments:
NParm - number of symbols
FreqParm - frequency of each symbol
LenParm - code length for each symbol
CodeParm - code for each symbol
Returns:
Root of the Huffman tree.
--*/
{
INT32 i, j, k, Avail;
//
// make tree, calculate len[], return root
//
mN = NParm;
mFreq = FreqParm;
mLen = LenParm;
Avail = mN;
mHeapSize = 0;
mHeap[1] = 0;
for (i = 0; i < mN; i++) {
mLen[i] = 0;
if (mFreq[i]) {
mHeap[++mHeapSize] = (INT16)i;
}
}
if (mHeapSize < 2) {
CodeParm[mHeap[1]] = 0;
return mHeap[1];
}
for (i = mHeapSize / 2; i >= 1; i--) {
//
// make priority queue
//
DownHeap(i);
}
mSortPtr = CodeParm;
do {
i = mHeap[1];
if (i < mN) {
*mSortPtr++ = (UINT16)i;
}
mHeap[1] = mHeap[mHeapSize--];
DownHeap(1);
j = mHeap[1];
if (j < mN) {
*mSortPtr++ = (UINT16)j;
}
k = Avail++;
mFreq[k] = (UINT16)(mFreq[i] + mFreq[j]);
mHeap[1] = (INT16)k;
DownHeap(1);
mLeft[k] = (UINT16)i;
mRight[k] = (UINT16)j;
} while (mHeapSize > 1);
mSortPtr = CodeParm;
MakeLen(k);
MakeCode(NParm, LenParm, CodeParm);
//
// return root
//
return k;
}