270 lines
6.7 KiB
C
270 lines
6.7 KiB
C
/*
|
|
** lzcommon.c - Routines common to LZ compression / expansion.
|
|
**
|
|
** Author: DavidDi
|
|
*/
|
|
|
|
|
|
// Headers
|
|
///////////
|
|
|
|
#include "lz_common.h"
|
|
#include "lz_buffers.h"
|
|
#include "lzcommon.h"
|
|
|
|
/*
|
|
** bool LZInitTree(void);
|
|
**
|
|
** Initializes trees used in LZ compression.
|
|
**
|
|
** Arguments: none
|
|
**
|
|
** Returns: true/false
|
|
**
|
|
** Globals: RightChild[] and Parent[] arrays reset to NIL to begin
|
|
** encoding.
|
|
*/
|
|
BOOL LZInitTree(PLZINFO pLZI)
|
|
{
|
|
INT i;
|
|
|
|
/*
|
|
** For i = 0 to RING_BUF_LEN - 1, rightChild[i] and leftChild[i] will be the
|
|
** right and left children of node i. These nodes need not be initialized.
|
|
** Also, parent[i] is the parent of node i. These are initialized to
|
|
** NIL (= N), which stands for 'not used.'
|
|
** For i = 0 to 255, rightChild[RING_BUF_LEN + i + 1] is the root of the tree
|
|
** for strings that begin with character i. These are initialized to NIL.
|
|
** n.b., there are 256 trees.
|
|
*/
|
|
|
|
if (!pLZI->rightChild) {
|
|
if (!(pLZI->rightChild = (INT*)LocalAlloc(LPTR, (RING_BUF_LEN + 257) * sizeof(INT)))) {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
if (!pLZI->leftChild) {
|
|
if (!(pLZI->leftChild = (INT*)LocalAlloc(LPTR, (RING_BUF_LEN + 1) * sizeof(INT)))) {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
if (!pLZI->parent) {
|
|
if (!(pLZI->parent = (INT*)LocalAlloc(LPTR, (RING_BUF_LEN + 1) * sizeof(INT)))) {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
for (i = RING_BUF_LEN + 1; i <= RING_BUF_LEN + 256; i++)
|
|
pLZI->rightChild[i] = NIL;
|
|
|
|
for (i = 0; i < RING_BUF_LEN; i++)
|
|
pLZI->parent[i] = NIL;
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
VOID
|
|
LZFreeTree(PLZINFO pLZI)
|
|
{
|
|
// Sanity check
|
|
if (!pLZI) {
|
|
return;
|
|
}
|
|
|
|
if (pLZI->rightChild) {
|
|
LocalFree((HLOCAL)pLZI->rightChild);
|
|
pLZI->rightChild = NULL;
|
|
}
|
|
|
|
if (pLZI->leftChild) {
|
|
LocalFree((HLOCAL)pLZI->leftChild);
|
|
pLZI->leftChild = NULL;
|
|
}
|
|
|
|
if (pLZI->parent) {
|
|
LocalFree((HLOCAL)pLZI->parent);
|
|
pLZI->parent = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
** void LZInsertNode(int nodeToInsert, BOOL bDoArithmeticInsert);
|
|
**
|
|
** Inserts a new tree into the forest. Inserts string of length
|
|
** cbMaxMatchLen, rgbyteRingBuf[r..r + cbMaxMatchLen - 1], into one of the trees
|
|
** (rgbyteRingBuf[r]'th tree).
|
|
**
|
|
** Arguments: nodeToInsert - start of string in ring buffer to insert
|
|
** (also, associated tree root)
|
|
** bDoArithmeticInsert - flag for performing regular LZ node
|
|
** insertion or arithmetic encoding node
|
|
** insertion
|
|
**
|
|
** Returns: void
|
|
**
|
|
** Globals: cbCurMatch - set to length of longest match
|
|
** iCurMatch - set to start index of longest matching string in
|
|
** ring buffer
|
|
**
|
|
** N.b., if cbCurMatch == cbMaxMatchLen, we remove the old node in favor of
|
|
** the new one, since the old node will be deleted sooner.
|
|
*/
|
|
VOID LZInsertNode(INT nodeToInsert, BOOL bDoArithmeticInsert, PLZINFO pLZI)
|
|
{
|
|
INT i, p, cmp, temp;
|
|
BYTE FAR *key;
|
|
|
|
// Sanity check
|
|
if (!pLZI) {
|
|
return;
|
|
}
|
|
|
|
cmp = 1;
|
|
|
|
key = pLZI->rgbyteRingBuf + nodeToInsert;
|
|
p = RING_BUF_LEN + 1 + key[0];
|
|
|
|
pLZI->rightChild[nodeToInsert] = pLZI->leftChild[nodeToInsert] = NIL;
|
|
pLZI->cbCurMatch = 0;
|
|
|
|
FOREVER
|
|
{
|
|
if (cmp >= 0)
|
|
{
|
|
if (pLZI->rightChild[p] != NIL)
|
|
p = pLZI->rightChild[p];
|
|
else
|
|
{
|
|
pLZI->rightChild[p] = nodeToInsert;
|
|
pLZI->parent[nodeToInsert] = p;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pLZI->leftChild[p] != NIL)
|
|
p = pLZI->leftChild[p];
|
|
else
|
|
{
|
|
pLZI->leftChild[p] = nodeToInsert;
|
|
pLZI->parent[nodeToInsert] = p;
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (i = 1; i < pLZI->cbMaxMatchLen; i++)
|
|
if ((cmp = key[i] - pLZI->rgbyteRingBuf[p + i]) != 0)
|
|
break;
|
|
|
|
if (bDoArithmeticInsert == TRUE)
|
|
{
|
|
// Do node insertion for arithmetic encoding.
|
|
if (i > MAX_LITERAL_LEN)
|
|
{
|
|
if (i > pLZI->cbCurMatch)
|
|
{
|
|
pLZI->iCurMatch = (nodeToInsert - p) & (RING_BUF_LEN - 1);
|
|
if ((pLZI->cbCurMatch = i) >= pLZI->cbMaxMatchLen)
|
|
break;
|
|
}
|
|
else if (i == pLZI->cbCurMatch)
|
|
{
|
|
if ((temp = (nodeToInsert - p) & (RING_BUF_LEN - 1)) < pLZI->iCurMatch)
|
|
pLZI->iCurMatch = temp;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Do node insertion for LZ.
|
|
if (i > pLZI->cbCurMatch)
|
|
{
|
|
pLZI->iCurMatch = p;
|
|
if ((pLZI->cbCurMatch = i) >= pLZI->cbMaxMatchLen)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
pLZI->parent[nodeToInsert] = pLZI->parent[p];
|
|
pLZI->leftChild[nodeToInsert] = pLZI->leftChild[p];
|
|
pLZI->rightChild[nodeToInsert] = pLZI->rightChild[p];
|
|
|
|
pLZI->parent[pLZI->leftChild[p]] = nodeToInsert;
|
|
pLZI->parent[pLZI->rightChild[p]] = nodeToInsert;
|
|
|
|
if (pLZI->rightChild[pLZI->parent[p]] == p)
|
|
pLZI->rightChild[pLZI->parent[p]] = nodeToInsert;
|
|
else
|
|
pLZI->leftChild[pLZI->parent[p]] = nodeToInsert;
|
|
|
|
// Remove p.
|
|
pLZI->parent[p] = NIL;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** void LZDeleteNode(int nodeToDelete);
|
|
**
|
|
** Delete a tree from the forest.
|
|
**
|
|
** Arguments: nodeToDelete - tree to delete from forest
|
|
**
|
|
** Returns: void
|
|
**
|
|
** Globals: Parent[], RightChild[], and LeftChild[] updated to reflect the
|
|
** deletion of nodeToDelete.
|
|
*/
|
|
VOID LZDeleteNode(INT nodeToDelete, PLZINFO pLZI)
|
|
{
|
|
INT q;
|
|
|
|
// Sanity check
|
|
if (!pLZI) {
|
|
return;
|
|
}
|
|
|
|
if (pLZI->parent[nodeToDelete] == NIL)
|
|
// Tree nodeToDelete is not in the forest.
|
|
return;
|
|
|
|
if (pLZI->rightChild[nodeToDelete] == NIL)
|
|
q = pLZI->leftChild[nodeToDelete];
|
|
else if (pLZI->leftChild[nodeToDelete] == NIL)
|
|
q = pLZI->rightChild[nodeToDelete];
|
|
else
|
|
{
|
|
q = pLZI->leftChild[nodeToDelete];
|
|
if (pLZI->rightChild[q] != NIL)
|
|
{
|
|
do
|
|
{
|
|
q = pLZI->rightChild[q];
|
|
} while (pLZI->rightChild[q] != NIL);
|
|
|
|
pLZI->rightChild[pLZI->parent[q]] = pLZI->leftChild[q];
|
|
pLZI->parent[pLZI->leftChild[q]] = pLZI->parent[q];
|
|
pLZI->leftChild[q] = pLZI->leftChild[nodeToDelete];
|
|
pLZI->parent[pLZI->leftChild[nodeToDelete]] = q;
|
|
}
|
|
pLZI->rightChild[q] = pLZI->rightChild[nodeToDelete];
|
|
pLZI->parent[pLZI->rightChild[nodeToDelete]] = q;
|
|
}
|
|
pLZI->parent[q] = pLZI->parent[nodeToDelete];
|
|
|
|
if (pLZI->rightChild[pLZI->parent[nodeToDelete]] == nodeToDelete)
|
|
pLZI->rightChild[pLZI->parent[nodeToDelete]] = q;
|
|
else
|
|
pLZI->leftChild[pLZI->parent[nodeToDelete]] = q;
|
|
|
|
// Remove nodeToDelete.
|
|
pLZI->parent[nodeToDelete] = NIL;
|
|
|
|
return;
|
|
}
|
|
|