windows-nt/Source/XPSP1/NT/base/mspatch/lzx/encoder/tree.c

214 lines
4.5 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*
* tree.c
*
* Tree building routines.
*
* These routines are originally from the Public Domain source "AR001".
*
* However, they have been modified for use in LZX.
*/
#include "encoder.h"
/* Function prototypes */
static void downheap(t_encoder_context *context, short i);
static void make_tree2(t_encoder_context *context, short avail, ushort freqparm[], ushort codeparm[]);
static void make_len(t_encoder_context *context, short root);
static void make_code(t_encoder_context *context, int n, char len[], ushort code[]);
static void count_len(t_encoder_context *context, short i) /* call with i = root */
{
if (i < context->enc_tree_n)
context->enc_tree_len_cnt[(context->enc_depth < 16) ? context->enc_depth : 16]++; /* NOTE: 16 is max len allowed */
else
{
context->enc_depth++;
count_len(context, context->enc_tree_leftright[i*2]);
count_len(context, context->enc_tree_leftright[i*2+1]);
context->enc_depth--;
}
}
static void make_len(t_encoder_context *context, short root)
{
signed short k;
ushort cum;
byte i;
for (i = 0; i <= 16; i++)
context->enc_tree_len_cnt[i] = 0;
count_len(context, root);
cum = 0;
for (i = 16; i > 0; i--)
cum += (ushort) (context->enc_tree_len_cnt[i] << (16 - i));
/* cum should equal 1<<16, which is 0 since cum is a ushort */
while (cum)
{
context->enc_tree_len_cnt[16]--;
for (i = 15; i > 0; i--)
{
if (context->enc_tree_len_cnt[i])
{
context->enc_tree_len_cnt[i]--;
context->enc_tree_len_cnt[i+1] += 2;
break;
}
}
cum--;
}
for (i = 16; i > 0; i--)
{
k = context->enc_tree_len_cnt[i];
while (--k >= 0)
context->enc_len[*context->enc_tree_sortptr++] = (byte) i;
}
}
static void __inline downheap(t_encoder_context *context, short i)
/* priority queue; send i-th entry down heap */
{
short j, k;
k = context->enc_tree_heap[i];
while ((j = (i<<1)) <= context->enc_tree_heapsize)
{
if (j < context->enc_tree_heapsize &&
context->enc_tree_freq[context->enc_tree_heap[j]] > context->enc_tree_freq[context->enc_tree_heap[j + 1]])
j++;
if (context->enc_tree_freq[k] <= context->enc_tree_freq[context->enc_tree_heap[j]])
break;
context->enc_tree_heap[i] = context->enc_tree_heap[j];
i = j;
}
context->enc_tree_heap[i] = k;
}
static void make_code(t_encoder_context *context, int n, char len[], ushort code[])
{
int i;
ushort start[18];
start[1] = 0;
for (i = 1; i <= 16; i++)
start[i + 1] = (start[i] + context->enc_tree_len_cnt[i]) << 1;
for (i = 0; i < n; i++)
{
code[i] = start[len[i]]++;
}
}
void make_tree(
t_encoder_context *context,
int nparm,
ushort *freqparm,
byte *lenparm,
ushort *codeparm,
bool make_codes /* for estimations, we only want the lengths */
)
{
short i, avail;
REDO_TREE:
context->enc_tree_n = nparm;
context->enc_tree_freq = freqparm;
context->enc_len = lenparm;
avail = (short)context->enc_tree_n;
context->enc_depth = 0;
context->enc_tree_heapsize = 0;
context->enc_tree_heap[1] = 0;
for (i = 0; i < nparm; i++)
{
context->enc_len[i] = 0;
if (freqparm[i])
context->enc_tree_heap[++context->enc_tree_heapsize] = i;
}
if (context->enc_tree_heapsize < 2)
{
if (!context->enc_tree_heapsize)
{
codeparm[context->enc_tree_heap[1]] = 0;
return;
}
if (!context->enc_tree_heap[1])
freqparm[1] = 1;
else
freqparm[0] = 1;
goto REDO_TREE;
}
make_tree2(context, avail, freqparm, codeparm);
if (make_codes)
make_code(context, nparm, lenparm, codeparm);
}
static void make_tree2(
t_encoder_context *context,
short avail,
ushort freqparm[],
ushort codeparm[]
)
{
short i, j, k;
for (i = context->enc_tree_heapsize >> 1; i >= 1; i--)
downheap(context, i); /* make priority queue */
context->enc_tree_sortptr = codeparm;
do
{ /* while queue has at least two entries */
i = context->enc_tree_heap[1]; /* take out least-freq entry */
if (i < context->enc_tree_n)
*context->enc_tree_sortptr++ = i;
context->enc_tree_heap[1] = context->enc_tree_heap[context->enc_tree_heapsize--];
downheap(context, 1);
j = context->enc_tree_heap[1]; /* next least-freq entry */
if (j < context->enc_tree_n)
*context->enc_tree_sortptr++ = j;
k = avail++; /* generate new node */
freqparm[k] = freqparm[i] + freqparm[j];
context->enc_tree_heap[1] = k;
downheap(context, 1); /* put into queue */
context->enc_tree_leftright[k*2] = i;
context->enc_tree_leftright[k*2+1] = j;
} while (context->enc_tree_heapsize > 1);
context->enc_tree_sortptr = codeparm;
make_len(context, k);
}