214 lines
4.5 KiB
C
214 lines
4.5 KiB
C
|
/*
|
||
|
* 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);
|
||
|
}
|