| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * drivers/amlogic/media/enhancement/amvecm/util/huffman.c |
| * |
| * Copyright (C) 2017 Amlogic, Inc. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| * more details. |
| * |
| */ |
| |
| #include "huffman.h" |
| |
| struct _huffman_node { |
| struct _huffman_node *lr[2]; |
| struct _huffman_node *qprev, *qnext; |
| unsigned long prob; |
| unsigned long c; |
| }; |
| |
| struct _huffman_encode_table { |
| unsigned long code; |
| unsigned long bits; |
| }; |
| |
| static void _huffman_write_tree_make_table(unsigned char *out, |
| unsigned long *outbitctr, |
| unsigned long outlen, |
| struct _huffman_encode_table *et, |
| unsigned long code, |
| unsigned int bits, |
| struct _huffman_node *t) |
| { |
| struct _huffman_encode_table *eti; |
| unsigned int i; |
| unsigned long byte_index; |
| |
| byte_index = (*outbitctr)++ >> 3; |
| byte_index *= (byte_index < outlen); |
| if (t->lr[0]) { |
| out[byte_index] <<= 1; |
| _huffman_write_tree_make_table(out, |
| outbitctr, |
| outlen, |
| et, |
| code, |
| bits + 1, |
| t->lr[0]); |
| _huffman_write_tree_make_table(out, |
| outbitctr, |
| outlen, |
| et, |
| code | (1 << bits), |
| bits + 1, |
| t->lr[1]); |
| } else { |
| out[byte_index] = (out[byte_index] << 1) | 1; |
| for (i = 0; i < 9; ++i) { |
| byte_index = (*outbitctr)++ >> 3; |
| if (byte_index >= outlen) |
| return; |
| out[byte_index] = |
| (out[byte_index] << 1) | |
| ((unsigned char)((t->c >> i) & 1)); |
| } |
| eti = &et[t->c]; |
| eti->code = code; |
| eti->bits = bits; |
| } |
| } |
| |
| static struct _huffman_node *_huffman_read_tree(const unsigned char *in, |
| unsigned long *inbitctr, |
| unsigned long inlen, |
| unsigned char **heapptr, |
| unsigned char *heapend) |
| { |
| struct _huffman_node *n; |
| unsigned int i; |
| unsigned long byte_index; |
| unsigned char tmp; |
| |
| n = (struct _huffman_node *)(*heapptr); |
| *heapptr += sizeof(struct _huffman_node); |
| if (*heapptr > heapend) |
| return (struct _huffman_node *)0; |
| |
| byte_index = *inbitctr >> 3; |
| byte_index *= (byte_index < inlen); |
| if (((in[byte_index] >> (~((*inbitctr)++) & 7)) & 1)) { |
| n->lr[0] = (struct _huffman_node *)0; |
| n->lr[1] = (struct _huffman_node *)0; |
| n->c = 0; |
| for (i = 0; i < 9; ++i) { |
| byte_index = *inbitctr >> 3; |
| if (byte_index >= inlen) |
| return (struct _huffman_node *)0; |
| tmp = in[byte_index] >> (~((*inbitctr)++) & 7); |
| n->c |= |
| (((unsigned int)(tmp)) & 1) << i; |
| } |
| } else { |
| n->lr[0] = _huffman_read_tree(in, inbitctr, inlen, |
| heapptr, heapend); |
| n->lr[1] = _huffman_read_tree(in, inbitctr, inlen, |
| heapptr, heapend); |
| if (!(n->lr[0] && n->lr[1])) |
| return (struct _huffman_node *)0; |
| } |
| |
| return n; |
| } |
| |
| unsigned long huffman_compress(const unsigned char *in, |
| unsigned long inlen, |
| unsigned char *out, |
| unsigned long outlen, |
| void *huffheap) |
| { |
| struct _huffman_encode_table *et, *eti; |
| struct _huffman_node *t, *n; |
| struct _huffman_node *pair[2]; |
| unsigned char *heapptr = (unsigned char *)huffheap; |
| unsigned long i, code, byte_index, outbitctr; |
| unsigned int bits, b; |
| unsigned long *counts, lowest_prob, total_symbols; |
| |
| counts = (unsigned long *)heapptr; |
| heapptr += (sizeof(unsigned long) * 257); |
| for (i = 0; i < 256; ++i) |
| counts[i] = 0; |
| counts[256] = (inlen + 1); /* one stop code at end */ |
| for (i = 0; i < inlen; ++i) |
| counts[(unsigned long)in[i]] += 1; |
| t = (struct _huffman_node *)0; |
| total_symbols = (inlen + 1); |
| for (i = 0; i <= 256; ++i) { |
| if (counts[i] > 0) { |
| n = (struct _huffman_node *)heapptr; |
| heapptr += sizeof(struct _huffman_node); |
| if (t) |
| t->qprev = n; |
| n->qprev = (struct _huffman_node *)0; |
| n->qnext = t; |
| n->lr[0] = (struct _huffman_node *)0; |
| n->lr[1] = (struct _huffman_node *)0; |
| n->prob = counts[i] * 1000 / total_symbols; |
| n->c = (unsigned int)i; |
| t = n; |
| } |
| } |
| while (t->qnext) { |
| for (i = 0; i < 2; ++i) { |
| lowest_prob = 1000; |
| pair[i] = (struct _huffman_node *)0; |
| n = t; |
| while (n) { |
| if (n->prob <= lowest_prob) { |
| lowest_prob = n->prob; |
| pair[i] = n; |
| } |
| n = n->qnext; |
| } |
| if (pair[i]->qprev) |
| pair[i]->qprev->qnext = pair[i]->qnext; |
| else |
| t = pair[i]->qnext; |
| if (pair[i]->qnext) |
| pair[i]->qnext->qprev = pair[i]->qprev; |
| } |
| n = (struct _huffman_node *)heapptr; |
| heapptr += sizeof(struct _huffman_node); |
| n->lr[0] = pair[0]; |
| n->lr[1] = pair[1]; |
| n->prob = pair[0]->prob + pair[1]->prob; |
| if (t) |
| t->qprev = n; |
| n->qprev = (struct _huffman_node *)0; |
| n->qnext = t; |
| t = n; |
| } |
| et = (struct _huffman_encode_table *)heapptr; |
| heapptr += (sizeof(struct _huffman_encode_table) * 257); |
| outbitctr = 0; |
| _huffman_write_tree_make_table(out, &outbitctr, |
| outlen, et, |
| 0, 0, t); |
| for (i = 0; i < inlen; ++i) { |
| eti = &(et[(unsigned long)in[i]]); |
| code = eti->code; |
| bits = eti->bits; |
| for (b = 0; b < bits; ++b) { |
| byte_index = outbitctr++ >> 3; |
| if (byte_index >= outlen) |
| return 0; |
| out[byte_index] = |
| (out[byte_index] << 1) | (unsigned char)(code & 1); |
| code >>= 1; |
| } |
| } |
| code = et[256].code; |
| bits = et[256].bits; |
| for (b = 0; b < bits; ++b) { |
| byte_index = outbitctr++ >> 3; |
| if (byte_index >= outlen) |
| return 0; |
| out[byte_index] = |
| (out[byte_index] << 1) | (unsigned char)(code & 1); |
| code >>= 1; |
| } |
| if (outbitctr > (outlen << 3)) { |
| return 0; |
| } else if ((outbitctr & 7)) { |
| out[i = (outbitctr >> 3)] <<= 8 - (outbitctr & 7); |
| return (i + 1); |
| } else { |
| return (outbitctr >> 3); |
| } |
| } |
| |
| unsigned long huffman_decompress(const unsigned char *in, |
| unsigned long inlen, |
| unsigned char *out, |
| unsigned long outlen, |
| void *huffheap) |
| { |
| struct _huffman_node *t, *n; |
| unsigned char *heapptr = (unsigned char *)huffheap, tmp; |
| unsigned long inbitctr, outptr, byte_index = 0; |
| |
| inbitctr = 0; |
| t = _huffman_read_tree(in, &inbitctr, inlen, &heapptr, |
| heapptr + HUFFHEAP_SIZE); |
| if (!t) |
| return 0; |
| outptr = 0; |
| for (;;) { |
| n = t; |
| while (n->lr[0]) { |
| byte_index = inbitctr >> 3; |
| if (byte_index >= inlen) |
| return 0; |
| tmp = in[byte_index] >> (~(inbitctr++) & 7); |
| n = |
| n->lr[((unsigned long)(tmp)) & 1]; |
| } |
| if (n->c == 256) |
| return outptr; |
| if (outptr == outlen) |
| return 0; |
| out[outptr++] = (unsigned char)n->c; |
| } |
| } |
| |