/**
 
        libsmacker - A C library for decoding .smk Smacker Video files
 
        Copyright (C) 2012-2017 Greg Kennedy
 
 
 
        See smacker.h for more information.
 
 
 
        smk_hufftree.c
 
                Implementation of Smacker Huffman coding trees.
 
*/
 
 
 
#include "smk_hufftree.h"
 
 
 
/* malloc and friends */
 
#include "smk_malloc.h"
 
 
 
/**
 
        8-bit Tree node structure.
 
        If b0 is non-null, this is a branch, and b1 from the union should be used.
 
        If b0 is null, this is a leaf, and val / escape code from union should be used.
 
*/
 
struct smk_huff8_t
 
{
 
        struct smk_huff8_t* b0;
 
        union
 
        {
 
                struct smk_huff8_t* b1;
 
                struct
 
                {
 
                        unsigned short value;
 
                        unsigned char escapecode;
 
                } leaf;
 
        } u;
 
};
 
 
 
/**
 
        16-bit Tree root struct: holds a huff8_t structure,
 
        as well as a cache of three 16-bit values.
 
*/
 
struct smk_huff16_t
 
{
 
        struct smk_huff8_t* t;
 
        unsigned short cache[3];
 
};
 
 
 
/*********************** 8-BIT HUFF-TREE FUNCTIONS ***********************/
 
/** safe build with built-in error jump */
 
#define smk_huff8_build_rec(bs,p) \
 
{ \
 
        if (!(p = _smk_huff8_build_rec(bs))) \
 
        { \
 
                fprintf(stderr, "libsmacker::smk_huff8_build_rec(" #bs ", " #p ") - ERROR (file: %s, line: %lu)\n", __FILE__, (unsigned long)__LINE__); \
 
                goto error; \
 
        } \
 
}
 
/** Recursive tree-building function. */
 
static struct smk_huff8_t* _smk_huff8_build_rec(struct smk_bit_t* bs)
 
{
 
        struct smk_huff8_t* ret = NULL;
 
        char bit;
 
 
 
        /* sanity check - removed: bs cannot be null, because it was checked at smk_huff8_build below */
 
        /* smk_assert(bs); */
 
 
 
        /* Read the bit */
 
        smk_bs_read_1(bs, bit);
 
 
 
        /* Malloc a structure. */
 
        smk_malloc(ret, sizeof(struct smk_huff8_t));
 
 
 
        if (bit)
 
        {
 
                /* Bit set: this forms a Branch node. */
 
                /* Recursively attempt to build the Left branch. */
 
                smk_huff8_build_rec(bs, ret->b0);
 
 
 
                /* Everything is still OK: attempt to build the Right branch. */
 
                smk_huff8_build_rec(bs, ret->u.b1);
 
 
 
                /* return branch pointer here */
 
                return ret;
 
        }
 
 
 
        /* Bit unset signifies a Leaf node. */
 
        /* Attempt to read value */
 
        smk_bs_read_8(bs, ret->u.leaf.value);
 
 
 
        /* smk_malloc sets entries to 0 by default */
 
        /* ret->b0 = NULL; */
 
        ret->u.leaf.escapecode = 0xFF;
 
 
 
        return ret;
 
 
 
error:
 
        /* In case of error, undo the subtree we were building, and return NULL. */
 
        smk_huff8_free(ret);
 
        return NULL;
 
}
 
 
 
/* Look up an 8-bit value from a basic huff tree.
 
        Return -1 on error. */
 
short _smk_huff8_lookup(struct smk_bit_t* bs, const struct smk_huff8_t* t)
 
{
 
        char bit;
 
 
 
        /* sanity check */
 
        smk_assert(bs);
 
        smk_assert(t);
 
 
 
        if (!t->b0)
 
        {
 
                /* Reached a Leaf node. Return its value. */
 
                return t->u.leaf.value;
 
        }
 
 
 
        /* Read the next bit from bitstream to determine path */
 
        smk_bs_read_1(bs, bit);
 
 
 
        if (bit)
 
        {
 
                /* get_bit returned Set, follow Right branch. */
 
                return _smk_huff8_lookup(bs, t->u.b1);
 
        }
 
 
 
        /* follow Left branch */
 
        return _smk_huff8_lookup(bs, t->b0);
 
 
 
error:
 
        return -1;
 
}
 
 
 
/**
 
        Entry point for huff8 build. Basically just checks the start/end tags
 
        and calls smk_huff8_build_rec recursive function.
 
*/
 
struct smk_huff8_t* _smk_huff8_build(struct smk_bit_t* bs)
 
{
 
        struct smk_huff8_t* ret = NULL;
 
        char bit;
 
 
 
        /* sanity check */
 
        smk_assert(bs);
 
 
 
        /* Smacker huff trees begin with a set-bit. */
 
        smk_bs_read_1(bs, bit);
 
 
 
        if (!bit)
 
        {
 
                /* Got a bit, but it was not 1. In theory, there could be a smk file
 
                        without this particular tree. */
 
                fputs("libsmacker::_smk_huff8_build(bs) - Warning: initial get_bit returned 0\n", stderr
);  
                goto error;
 
        }
 
 
 
        /* Begin parsing the tree data. */
 
        smk_huff8_build_rec(bs, ret);
 
 
 
        /* huff trees end with an unset-bit */
 
        smk_bs_read_1(bs, bit);
 
 
 
        if (bit)
 
        {
 
                fputs("libsmacker::_smk_huff8_build(bs) - ERROR: final get_bit returned 1\n", stderr
);  
                goto error;
 
        }
 
 
 
        return ret;
 
 
 
error:
 
        smk_huff8_free(ret);
 
        return NULL;
 
}
 
 
 
/* function to recursively delete a huffman tree */
 
void smk_huff8_free(struct smk_huff8_t* t)
 
{
 
        /* Sanity check: do not double-free */
 
        smk_assert(t);
 
 
 
        /* If this is not a leaf node, free child trees first */
 
        if (t->b0)
 
        {
 
                smk_huff8_free(t->b0);
 
                smk_huff8_free(t->u.b1);
 
        }
 
 
 
        /* Safe-delete tree node. */
 
        smk_free(t);
 
 
 
error: ;
 
}
 
 
 
/*********************** 16-BIT HUFF-TREE FUNCTIONS ***********************/
 
/* safe bigtree build with built-in error jump */
 
#define smk_huff16_build_rec(bs,cache,low8,hi8,p) \
 
{ \
 
        if (!(p = _smk_huff16_build_rec(bs, cache, low8, hi8))) \
 
        { \
 
                fprintf(stderr, "libsmacker::smk_huff16_build_rec(" #bs ", " #cache ", " #low8 ", " #hi8 ", " #p ") - ERROR (file: %s, line: %lu)\n", __FILE__, (unsigned long)__LINE__); \
 
                goto error; \
 
        } \
 
}
 
/* Recursively builds a Big tree. */
 
static struct smk_huff8_t* _smk_huff16_build_rec(struct smk_bit_t* bs, const unsigned short cache[3], const struct smk_huff8_t* low8, const struct smk_huff8_t* hi8)
 
{
 
        struct smk_huff8_t* ret = NULL;
 
 
 
        char bit;
 
        short lowval;
 
 
 
        /* sanity check - removed: these cannot be null, because they were checked at smk_huff16_build below */
 
        /* smk_assert(bs);
 
        smk_assert(cache);
 
        smk_assert(low8);
 
        smk_assert(hi8); */
 
 
 
        /* Get the first bit */
 
        smk_bs_read_1(bs, bit);
 
 
 
        /* Malloc a structure. */
 
        smk_malloc(ret, sizeof(struct smk_huff8_t));
 
 
 
        if (bit)
 
        {
 
                /* Recursively attempt to build the Left branch. */
 
                smk_huff16_build_rec(bs, cache, low8, hi8, ret->b0);
 
 
 
                /* Recursively attempt to build the Left branch. */
 
                smk_huff16_build_rec(bs, cache, low8, hi8, ret->u.b1);
 
 
 
                /* return branch pointer here */
 
                return ret;
 
        }
 
 
 
        /* Bit unset signifies a Leaf node. */
 
        smk_huff8_lookup(bs, low8, lowval);
 
        smk_huff8_lookup(bs, hi8, ret->u.leaf.value);
 
 
 
        /* Looks OK: we got low and hi values. Return a new LEAF */
 
        /* ret->b0 = NULL; */
 
        ret->u.leaf.value = lowval | (ret->u.leaf.value << 8);
 
 
 
        /* Last: when building the tree, some Values may correspond to cache positions.
 
                Identify these values and set the Escape code byte accordingly. */
 
        if (ret->u.leaf.value == cache[0])
 
        {
 
                ret->u.leaf.escapecode = 0;
 
        }
 
        else if (ret->u.leaf.value == cache[1])
 
        {
 
                ret->u.leaf.escapecode = 1;
 
        }
 
        else if (ret->u.leaf.value == cache[2])
 
        {
 
                ret->u.leaf.escapecode = 2;
 
        }
 
        else
 
        {
 
                ret->u.leaf.escapecode = 0xFF;
 
        }
 
 
 
        return ret;
 
 
 
error:
 
        smk_huff8_free(ret);
 
        return NULL;
 
}
 
 
 
/* Entry point for building a big 16-bit tree. */
 
struct smk_huff16_t* _smk_huff16_build(struct smk_bit_t* bs)
 
{
 
        struct smk_huff16_t* big = NULL;
 
 
 
        struct smk_huff8_t* low8 = NULL;
 
        struct smk_huff8_t* hi8 = NULL;
 
 
 
        short lowval;
 
 
 
        char bit;
 
        unsigned char i;
 
 
 
        /* sanity check */
 
        smk_assert(bs);
 
 
 
        /* Smacker huff trees begin with a set-bit. */
 
        smk_bs_read_1(bs, bit);
 
 
 
        if (!bit)
 
        {
 
                fputs("libsmacker::smk_huff16_build(bs) - ERROR: initial get_bit returned 0\n", stderr
);  
                goto error;
 
        }
 
 
 
        /* build low-8-bits tree */
 
        smk_huff8_build(bs, low8);
 
        /* build hi-8-bits tree */
 
        smk_huff8_build(bs, hi8);
 
 
 
        /* Everything looks OK so far. Time to malloc structure. */
 
        smk_malloc(big, sizeof(struct smk_huff16_t));
 
 
 
        /* Init the escape code cache. */
 
        for (i = 0; i < 3; i ++)
 
        {
 
                smk_bs_read_8(bs, lowval);
 
                smk_bs_read_8(bs, big->cache[i]);
 
                big->cache[i] = lowval | (big->cache[i] << 8);
 
        }
 
 
 
        /* Finally, call recursive function to retrieve the Bigtree. */
 
        smk_huff16_build_rec(bs, big->cache, low8, hi8, big->t);
 
 
 
        /* Done with 8-bit hufftrees, free them. */
 
        smk_huff8_free(hi8);
 
        smk_huff8_free(low8);
 
 
 
        /* Check final end tag. */
 
        smk_bs_read_1(bs, bit);
 
 
 
        if (bit)
 
        {
 
                fputs("libsmacker::smk_huff16_build(bs) - ERROR: final get_bit returned 1\n", stderr
);  
                goto error;
 
        }
 
 
 
        return big;
 
 
 
error:
 
        smk_huff16_free(big);
 
        smk_huff8_free(hi8);
 
        smk_huff8_free(low8);
 
        return NULL;
 
}
 
 
 
static int _smk_huff16_lookup_rec(struct smk_bit_t* bs, unsigned short cache[3], const struct smk_huff8_t* t)
 
{
 
        unsigned short val;
 
        char bit;
 
 
 
        /* sanity check */
 
        /* smk_assert(bs);
 
        smk_assert(cache);
 
        smk_assert(t); */
 
 
 
        /* Reached a Leaf node */
 
        if (!t->b0)
 
        {
 
                if (t->u.leaf.escapecode != 0xFF)
 
                {
 
                        /* Found escape code. Retrieve value from Cache. */
 
                        val = cache[t->u.leaf.escapecode];
 
                }
 
                else
 
                {
 
                        /* Use value directly. */
 
                        val = t->u.leaf.value;
 
                }
 
 
 
                if (cache[0] != val)
 
                {
 
                        /* Update the cache, by moving val to the front of the queue,
 
                                if it isn't already there. */
 
                        cache[2] = cache[1];
 
                        cache[1] = cache[0];
 
                        cache[0] = val;
 
                }
 
 
 
                return val;
 
        }
 
 
 
        /* Read the next bit from bitstream to determine path */
 
        smk_bs_read_1(bs, bit);
 
 
 
        if (bit)
 
        {
 
                /* get_bit returned Set, follow Right branch. */
 
                return _smk_huff16_lookup_rec(bs, cache, t->u.b1);
 
        }
 
 
 
        return _smk_huff16_lookup_rec(bs, cache, t->b0);
 
 
 
error:
 
        return -1;
 
}
 
 
 
/* Convenience call-out for recursive bigtree lookup function */
 
long _smk_huff16_lookup(struct smk_bit_t* bs, struct smk_huff16_t* big)
 
{
 
        /* sanity check */
 
        smk_assert(bs);
 
        smk_assert(big);
 
 
 
        return _smk_huff16_lookup_rec(bs, big->cache, big->t);
 
 
 
error:
 
        return -1;
 
}
 
 
 
/* Resets a Big hufftree cache */
 
void smk_huff16_reset(struct smk_huff16_t* big)
 
{
 
        /* sanity check */
 
        smk_assert(big);
 
 
 
        big->cache[0] = 0;
 
        big->cache[1] = 0;
 
        big->cache[2] = 0;
 
 
 
error: ;
 
}
 
 
 
/* delete a (big) huffman tree */
 
void smk_huff16_free(struct smk_huff16_t* big)
 
{
 
        /* Sanity check: do not double-free */
 
        smk_assert(big);
 
 
 
        /* free the subtree */
 
        if (big->t)
 
                smk_huff8_free(big->t);
 
 
 
        /* free the bigtree */
 
        smk_free(big);
 
 
 
error: ;
 
};