Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | pmbaty | 1 | /** |
2 | libsmacker - A C library for decoding .smk Smacker Video files |
||
3 | Copyright (C) 2012-2017 Greg Kennedy |
||
4 | |||
5 | See smacker.h for more information. |
||
6 | |||
7 | smk_hufftree.c |
||
8 | Implementation of Smacker Huffman coding trees. |
||
9 | */ |
||
10 | |||
11 | #include "smk_hufftree.h" |
||
12 | |||
13 | /* malloc and friends */ |
||
14 | #include "smk_malloc.h" |
||
15 | |||
16 | /** |
||
17 | 8-bit Tree node structure. |
||
18 | If b0 is non-null, this is a branch, and b1 from the union should be used. |
||
19 | If b0 is null, this is a leaf, and val / escape code from union should be used. |
||
20 | */ |
||
21 | struct smk_huff8_t |
||
22 | { |
||
23 | struct smk_huff8_t* b0; |
||
24 | union |
||
25 | { |
||
26 | struct smk_huff8_t* b1; |
||
27 | struct |
||
28 | { |
||
29 | unsigned short value; |
||
30 | unsigned char escapecode; |
||
31 | } leaf; |
||
32 | } u; |
||
33 | }; |
||
34 | |||
35 | /** |
||
36 | 16-bit Tree root struct: holds a huff8_t structure, |
||
37 | as well as a cache of three 16-bit values. |
||
38 | */ |
||
39 | struct smk_huff16_t |
||
40 | { |
||
41 | struct smk_huff8_t* t; |
||
42 | unsigned short cache[3]; |
||
43 | }; |
||
44 | |||
45 | /*********************** 8-BIT HUFF-TREE FUNCTIONS ***********************/ |
||
46 | /** safe build with built-in error jump */ |
||
47 | #define smk_huff8_build_rec(bs,p) \ |
||
48 | { \ |
||
49 | if (!(p = _smk_huff8_build_rec(bs))) \ |
||
50 | { \ |
||
51 | fprintf(stderr, "libsmacker::smk_huff8_build_rec(" #bs ", " #p ") - ERROR (file: %s, line: %lu)\n", __FILE__, (unsigned long)__LINE__); \ |
||
52 | goto error; \ |
||
53 | } \ |
||
54 | } |
||
55 | /** Recursive tree-building function. */ |
||
56 | static struct smk_huff8_t* _smk_huff8_build_rec(struct smk_bit_t* bs) |
||
57 | { |
||
58 | struct smk_huff8_t* ret = NULL; |
||
59 | char bit; |
||
60 | |||
61 | /* sanity check - removed: bs cannot be null, because it was checked at smk_huff8_build below */ |
||
62 | /* smk_assert(bs); */ |
||
63 | |||
64 | /* Read the bit */ |
||
65 | smk_bs_read_1(bs, bit); |
||
66 | |||
67 | /* Malloc a structure. */ |
||
68 | smk_malloc(ret, sizeof(struct smk_huff8_t)); |
||
69 | |||
70 | if (bit) |
||
71 | { |
||
72 | /* Bit set: this forms a Branch node. */ |
||
73 | /* Recursively attempt to build the Left branch. */ |
||
74 | smk_huff8_build_rec(bs, ret->b0); |
||
75 | |||
76 | /* Everything is still OK: attempt to build the Right branch. */ |
||
77 | smk_huff8_build_rec(bs, ret->u.b1); |
||
78 | |||
79 | /* return branch pointer here */ |
||
80 | return ret; |
||
81 | } |
||
82 | |||
83 | /* Bit unset signifies a Leaf node. */ |
||
84 | /* Attempt to read value */ |
||
85 | smk_bs_read_8(bs, ret->u.leaf.value); |
||
86 | |||
87 | /* smk_malloc sets entries to 0 by default */ |
||
88 | /* ret->b0 = NULL; */ |
||
89 | ret->u.leaf.escapecode = 0xFF; |
||
90 | |||
91 | return ret; |
||
92 | |||
93 | error: |
||
94 | /* In case of error, undo the subtree we were building, and return NULL. */ |
||
95 | smk_huff8_free(ret); |
||
96 | return NULL; |
||
97 | } |
||
98 | |||
99 | /* Look up an 8-bit value from a basic huff tree. |
||
100 | Return -1 on error. */ |
||
101 | short _smk_huff8_lookup(struct smk_bit_t* bs, const struct smk_huff8_t* t) |
||
102 | { |
||
103 | char bit; |
||
104 | |||
105 | /* sanity check */ |
||
106 | smk_assert(bs); |
||
107 | smk_assert(t); |
||
108 | |||
109 | if (!t->b0) |
||
110 | { |
||
111 | /* Reached a Leaf node. Return its value. */ |
||
112 | return t->u.leaf.value; |
||
113 | } |
||
114 | |||
115 | /* Read the next bit from bitstream to determine path */ |
||
116 | smk_bs_read_1(bs, bit); |
||
117 | |||
118 | if (bit) |
||
119 | { |
||
120 | /* get_bit returned Set, follow Right branch. */ |
||
121 | return _smk_huff8_lookup(bs, t->u.b1); |
||
122 | } |
||
123 | |||
124 | /* follow Left branch */ |
||
125 | return _smk_huff8_lookup(bs, t->b0); |
||
126 | |||
127 | error: |
||
128 | return -1; |
||
129 | } |
||
130 | |||
131 | /** |
||
132 | Entry point for huff8 build. Basically just checks the start/end tags |
||
133 | and calls smk_huff8_build_rec recursive function. |
||
134 | */ |
||
135 | struct smk_huff8_t* _smk_huff8_build(struct smk_bit_t* bs) |
||
136 | { |
||
137 | struct smk_huff8_t* ret = NULL; |
||
138 | char bit; |
||
139 | |||
140 | /* sanity check */ |
||
141 | smk_assert(bs); |
||
142 | |||
143 | /* Smacker huff trees begin with a set-bit. */ |
||
144 | smk_bs_read_1(bs, bit); |
||
145 | |||
146 | if (!bit) |
||
147 | { |
||
148 | /* Got a bit, but it was not 1. In theory, there could be a smk file |
||
149 | without this particular tree. */ |
||
150 | fputs("libsmacker::_smk_huff8_build(bs) - Warning: initial get_bit returned 0\n", stderr); |
||
151 | goto error; |
||
152 | } |
||
153 | |||
154 | /* Begin parsing the tree data. */ |
||
155 | smk_huff8_build_rec(bs, ret); |
||
156 | |||
157 | /* huff trees end with an unset-bit */ |
||
158 | smk_bs_read_1(bs, bit); |
||
159 | |||
160 | if (bit) |
||
161 | { |
||
162 | fputs("libsmacker::_smk_huff8_build(bs) - ERROR: final get_bit returned 1\n", stderr); |
||
163 | goto error; |
||
164 | } |
||
165 | |||
166 | return ret; |
||
167 | |||
168 | error: |
||
169 | smk_huff8_free(ret); |
||
170 | return NULL; |
||
171 | } |
||
172 | |||
173 | /* function to recursively delete a huffman tree */ |
||
174 | void smk_huff8_free(struct smk_huff8_t* t) |
||
175 | { |
||
176 | /* Sanity check: do not double-free */ |
||
177 | smk_assert(t); |
||
178 | |||
179 | /* If this is not a leaf node, free child trees first */ |
||
180 | if (t->b0) |
||
181 | { |
||
182 | smk_huff8_free(t->b0); |
||
183 | smk_huff8_free(t->u.b1); |
||
184 | } |
||
185 | |||
186 | /* Safe-delete tree node. */ |
||
187 | smk_free(t); |
||
188 | |||
189 | error: ; |
||
190 | } |
||
191 | |||
192 | /*********************** 16-BIT HUFF-TREE FUNCTIONS ***********************/ |
||
193 | /* safe bigtree build with built-in error jump */ |
||
194 | #define smk_huff16_build_rec(bs,cache,low8,hi8,p) \ |
||
195 | { \ |
||
196 | if (!(p = _smk_huff16_build_rec(bs, cache, low8, hi8))) \ |
||
197 | { \ |
||
198 | fprintf(stderr, "libsmacker::smk_huff16_build_rec(" #bs ", " #cache ", " #low8 ", " #hi8 ", " #p ") - ERROR (file: %s, line: %lu)\n", __FILE__, (unsigned long)__LINE__); \ |
||
199 | goto error; \ |
||
200 | } \ |
||
201 | } |
||
202 | /* Recursively builds a Big tree. */ |
||
203 | 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) |
||
204 | { |
||
205 | struct smk_huff8_t* ret = NULL; |
||
206 | |||
207 | char bit; |
||
208 | short lowval; |
||
209 | |||
210 | /* sanity check - removed: these cannot be null, because they were checked at smk_huff16_build below */ |
||
211 | /* smk_assert(bs); |
||
212 | smk_assert(cache); |
||
213 | smk_assert(low8); |
||
214 | smk_assert(hi8); */ |
||
215 | |||
216 | /* Get the first bit */ |
||
217 | smk_bs_read_1(bs, bit); |
||
218 | |||
219 | /* Malloc a structure. */ |
||
220 | smk_malloc(ret, sizeof(struct smk_huff8_t)); |
||
221 | |||
222 | if (bit) |
||
223 | { |
||
224 | /* Recursively attempt to build the Left branch. */ |
||
225 | smk_huff16_build_rec(bs, cache, low8, hi8, ret->b0); |
||
226 | |||
227 | /* Recursively attempt to build the Left branch. */ |
||
228 | smk_huff16_build_rec(bs, cache, low8, hi8, ret->u.b1); |
||
229 | |||
230 | /* return branch pointer here */ |
||
231 | return ret; |
||
232 | } |
||
233 | |||
234 | /* Bit unset signifies a Leaf node. */ |
||
235 | smk_huff8_lookup(bs, low8, lowval); |
||
236 | smk_huff8_lookup(bs, hi8, ret->u.leaf.value); |
||
237 | |||
238 | /* Looks OK: we got low and hi values. Return a new LEAF */ |
||
239 | /* ret->b0 = NULL; */ |
||
240 | ret->u.leaf.value = lowval | (ret->u.leaf.value << 8); |
||
241 | |||
242 | /* Last: when building the tree, some Values may correspond to cache positions. |
||
243 | Identify these values and set the Escape code byte accordingly. */ |
||
244 | if (ret->u.leaf.value == cache[0]) |
||
245 | { |
||
246 | ret->u.leaf.escapecode = 0; |
||
247 | } |
||
248 | else if (ret->u.leaf.value == cache[1]) |
||
249 | { |
||
250 | ret->u.leaf.escapecode = 1; |
||
251 | } |
||
252 | else if (ret->u.leaf.value == cache[2]) |
||
253 | { |
||
254 | ret->u.leaf.escapecode = 2; |
||
255 | } |
||
256 | else |
||
257 | { |
||
258 | ret->u.leaf.escapecode = 0xFF; |
||
259 | } |
||
260 | |||
261 | return ret; |
||
262 | |||
263 | error: |
||
264 | smk_huff8_free(ret); |
||
265 | return NULL; |
||
266 | } |
||
267 | |||
268 | /* Entry point for building a big 16-bit tree. */ |
||
269 | struct smk_huff16_t* _smk_huff16_build(struct smk_bit_t* bs) |
||
270 | { |
||
271 | struct smk_huff16_t* big = NULL; |
||
272 | |||
273 | struct smk_huff8_t* low8 = NULL; |
||
274 | struct smk_huff8_t* hi8 = NULL; |
||
275 | |||
276 | short lowval; |
||
277 | |||
278 | char bit; |
||
279 | unsigned char i; |
||
280 | |||
281 | /* sanity check */ |
||
282 | smk_assert(bs); |
||
283 | |||
284 | /* Smacker huff trees begin with a set-bit. */ |
||
285 | smk_bs_read_1(bs, bit); |
||
286 | |||
287 | if (!bit) |
||
288 | { |
||
289 | fputs("libsmacker::smk_huff16_build(bs) - ERROR: initial get_bit returned 0\n", stderr); |
||
290 | goto error; |
||
291 | } |
||
292 | |||
293 | /* build low-8-bits tree */ |
||
294 | smk_huff8_build(bs, low8); |
||
295 | /* build hi-8-bits tree */ |
||
296 | smk_huff8_build(bs, hi8); |
||
297 | |||
298 | /* Everything looks OK so far. Time to malloc structure. */ |
||
299 | smk_malloc(big, sizeof(struct smk_huff16_t)); |
||
300 | |||
301 | /* Init the escape code cache. */ |
||
302 | for (i = 0; i < 3; i ++) |
||
303 | { |
||
304 | smk_bs_read_8(bs, lowval); |
||
305 | smk_bs_read_8(bs, big->cache[i]); |
||
306 | big->cache[i] = lowval | (big->cache[i] << 8); |
||
307 | } |
||
308 | |||
309 | /* Finally, call recursive function to retrieve the Bigtree. */ |
||
310 | smk_huff16_build_rec(bs, big->cache, low8, hi8, big->t); |
||
311 | |||
312 | /* Done with 8-bit hufftrees, free them. */ |
||
313 | smk_huff8_free(hi8); |
||
314 | smk_huff8_free(low8); |
||
315 | |||
316 | /* Check final end tag. */ |
||
317 | smk_bs_read_1(bs, bit); |
||
318 | |||
319 | if (bit) |
||
320 | { |
||
321 | fputs("libsmacker::smk_huff16_build(bs) - ERROR: final get_bit returned 1\n", stderr); |
||
322 | goto error; |
||
323 | } |
||
324 | |||
325 | return big; |
||
326 | |||
327 | error: |
||
328 | smk_huff16_free(big); |
||
329 | smk_huff8_free(hi8); |
||
330 | smk_huff8_free(low8); |
||
331 | return NULL; |
||
332 | } |
||
333 | |||
334 | static int _smk_huff16_lookup_rec(struct smk_bit_t* bs, unsigned short cache[3], const struct smk_huff8_t* t) |
||
335 | { |
||
336 | unsigned short val; |
||
337 | char bit; |
||
338 | |||
339 | /* sanity check */ |
||
340 | /* smk_assert(bs); |
||
341 | smk_assert(cache); |
||
342 | smk_assert(t); */ |
||
343 | |||
344 | /* Reached a Leaf node */ |
||
345 | if (!t->b0) |
||
346 | { |
||
347 | if (t->u.leaf.escapecode != 0xFF) |
||
348 | { |
||
349 | /* Found escape code. Retrieve value from Cache. */ |
||
350 | val = cache[t->u.leaf.escapecode]; |
||
351 | } |
||
352 | else |
||
353 | { |
||
354 | /* Use value directly. */ |
||
355 | val = t->u.leaf.value; |
||
356 | } |
||
357 | |||
358 | if (cache[0] != val) |
||
359 | { |
||
360 | /* Update the cache, by moving val to the front of the queue, |
||
361 | if it isn't already there. */ |
||
362 | cache[2] = cache[1]; |
||
363 | cache[1] = cache[0]; |
||
364 | cache[0] = val; |
||
365 | } |
||
366 | |||
367 | return val; |
||
368 | } |
||
369 | |||
370 | /* Read the next bit from bitstream to determine path */ |
||
371 | smk_bs_read_1(bs, bit); |
||
372 | |||
373 | if (bit) |
||
374 | { |
||
375 | /* get_bit returned Set, follow Right branch. */ |
||
376 | return _smk_huff16_lookup_rec(bs, cache, t->u.b1); |
||
377 | } |
||
378 | |||
379 | return _smk_huff16_lookup_rec(bs, cache, t->b0); |
||
380 | |||
381 | error: |
||
382 | return -1; |
||
383 | } |
||
384 | |||
385 | /* Convenience call-out for recursive bigtree lookup function */ |
||
386 | long _smk_huff16_lookup(struct smk_bit_t* bs, struct smk_huff16_t* big) |
||
387 | { |
||
388 | /* sanity check */ |
||
389 | smk_assert(bs); |
||
390 | smk_assert(big); |
||
391 | |||
392 | return _smk_huff16_lookup_rec(bs, big->cache, big->t); |
||
393 | |||
394 | error: |
||
395 | return -1; |
||
396 | } |
||
397 | |||
398 | /* Resets a Big hufftree cache */ |
||
399 | void smk_huff16_reset(struct smk_huff16_t* big) |
||
400 | { |
||
401 | /* sanity check */ |
||
402 | smk_assert(big); |
||
403 | |||
404 | big->cache[0] = 0; |
||
405 | big->cache[1] = 0; |
||
406 | big->cache[2] = 0; |
||
407 | |||
408 | error: ; |
||
409 | } |
||
410 | |||
411 | /* delete a (big) huffman tree */ |
||
412 | void smk_huff16_free(struct smk_huff16_t* big) |
||
413 | { |
||
414 | /* Sanity check: do not double-free */ |
||
415 | smk_assert(big); |
||
416 | |||
417 | /* free the subtree */ |
||
418 | if (big->t) |
||
419 | smk_huff8_free(big->t); |
||
420 | |||
421 | /* free the bigtree */ |
||
422 | smk_free(big); |
||
423 | |||
424 | error: ; |
||
425 | }; |