|
|
/*
* 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); }
|