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