www.pudn.com > Compression.rar > Huffman.cpp


#include "Huffman.h"
#include 
#include "../Fatal/Fatal.h"

#define MAX_LENGTH		9		// bits in symbol it can`t be coded
#define ESC_END			4		// bits in symbol to start code from it
#define ESC_WEIDTH		8		// weight of ESC symbol is source_length/ESC_WEIDTH
#define ESC_LENGTH		4		// bits for count not coded symbols
#define MAX_NOT_CODED	16		// 2 pow ESC_LENGTH
#define MAX_TABLE		256		// maximum number of symbols in table
#define ESC				256		// ESC symbol always 256

CHuffman::CHuffman()
{
	memset(&nill, 0, sizeof(nill));
	nill.next = nill.prev = &nill;
	memset(table, 0, (ESC+1)*sizeof(tagTable));
}
CHuffman::~CHuffman()
{
	FreeTable();
}
void CHuffman::ListInit(DWORD *w)		// init by list of ESC+1 weights
{
	int i;
	tagNode *node;
	for (i = 0; i < ESC+1; i++)
	{
		node = new tagNode;
		memset(node, 0, sizeof(tagNode));
		node->key = i;
		ListAddSorted(w[i], node);
	}
}
void CHuffman::ListAddSorted(int weight, CHuffman::tagNode *node)
{
	tagList *cur, *cur1;
	cur1 = new tagList;
	memset(cur1, 0, sizeof(tagList));
	cur1->weight = weight;
	cur1->node = node;
	for (cur = nill.next; cur != &nill; cur = cur->next)
		if (cur->weight >= weight) break;
		cur1->next = cur;
		cur1->prev = cur->prev;
		cur1->prev->next = cur1;
		cur1->next->prev = cur1;
}
void CHuffman::ListDelete(CHuffman::tagList *cur)
{
	if (cur == &nill) return;
	cur->next->prev = cur->prev;
	cur->prev->next = cur->next;
	delete cur;
}
CHuffman::tagList *CHuffman::ListExtractMin()
{
	tagList *cur = nill.next;
	if (nill.next == &nill) return NULL;
	cur->next->prev = cur->prev;
	cur->prev->next = cur->next;
	return cur;
}
int CHuffman::ListIsLast()
{
	return (nill.next->next == &nill);
}
int CHuffman::ListIsEmpty()
{
	return (nill.next == &nill);
}

int CHuffman::RecTableFillBits(tagNode *node, int level)
{
	int left, right;
	int p, o;
	tagNode *cur;
	if (level > MAX_LENGTH) return 1;
	if (node == NULL) return 0;
	left = RecTableFillBits(node->left, level+1);
	right = RecTableFillBits(node->right, level+1);
	if (!left && !right)
	{
		table[node->key].count = (BYTE)level;
		table[node->key].buf = new BYTE[level/8 + 1];
		memset(table[node->key].buf, 0, level/8 + 1);
		p = level/8;
		o = level%8;
		// bits are inserted in reverce order
		for (cur = node; cur != NULL; cur = cur->up)
			if (cur->up != NULL)
			{
				if ((--o) < 0)
				{
					o = 7;
					p--;
				}
				if (p < 0) return 1;
				
				if (cur->up->right == cur)
					table[node->key].buf[p] |= 1<= 8)
	{
		p++;
		o = 0;
	}
	if (clean && !o) strm[p] = 0;
	strm[p] |= bit<<(o++);
}
void CHuffman::StreamToBit(int &bit, BYTE *strm, int &p, int &o)
{
	if (o >= 8)
	{
		p++;
		o = 0;
	}
	bit = (strm[p] & 1<<(o++))?1:0;
}
void CHuffman::StreamToStream(BYTE *target, BYTE *source, int bit_len, int &sp, int &so, int &tp, int &to, bool clean)
{
	int i, bit;
	for (i = 0; i < bit_len; i++)
	{
		StreamToBit(bit, source, sp, so);
		BitToStream(bit, target, tp, to, clean);
	}
}

void CHuffman::FreeList()
{
	while (!ListIsEmpty())
	{
		FreeNode(nill.next->node);
		ListDelete(nill.next);
	}
}
void CHuffman::FreeNode(tagNode *node)
{
	if (node == NULL) return;
	FreeNode(node->left);
	FreeNode(node->right);
	delete node;
}
void CHuffman::FreeTable()
{
	int i;
	for (i = 0; i < ESC+1; i++)
		if (table[i].buf != NULL)
			delete table[i].buf;
		memset(table, 0, (ESC+1)*sizeof(table[0]));
}

void CHuffman::InitTable(BYTE *buf, long size)
{
	DWORD w[ESC+1];
	int i;
	tagList *l1, *l2;
	tagNode *node = NULL;
	
	FreeTable();
	
	memset(w, 0, (ESC+1)*sizeof(DWORD));
	for (i = 0; i < size; i++)
		w[buf[i]]++;
	w[ESC] = size/ESC_WEIDTH;
	if (!w[ESC])
		w[ESC]++;
	
	ListInit(w);
	
	while (!ListIsLast())
	{
		l1 = ListExtractMin();
		l2 = ListExtractMin();
		node = new tagNode;
		memset(node, 0, sizeof(tagNode));
		node->left = l1->node;
		node->right = l2->node;
		l1->node->up = node;
		l2->node->up = node;
		ListAddSorted(l1->weight + l2->weight, node);
		delete l1;
		delete l2;
	}
	
	if (ListIsEmpty())
		FatalError("Error InitTable");
	
	RecTableFillBits(node);
	FreeList();
	
	if (!table[ESC].count)
		FatalError("ESC symbol is too long and concatinated to zero");
}
int CHuffman::GetTableLength()
{
	int i, bit_len = 0;
	bit_len += 8;		// place for sizeof table
	bit_len += sizeof(BYTE)*8 + table[ESC].count;
	for (i = 0; i < ESC; i++)
		if (table[i].count)
			bit_len += sizeof(BYTE)*8*2 + table[i].count;
		return bit_len/8 + 1;
}
int CHuffman::GetTable(BYTE *buf)
{
	int sp, tp, so, to;
	int i, bit_len = 0;
	BYTE len_tab = 0;
	tp = to = 0;
	bit_len += sizeof(BYTE)*8;
	tp++;			// one byte for table length
	
	sp = so = 0;
	StreamToStream(buf, &table[ESC].count, sizeof(BYTE)*8, sp, so, tp, to);
	sp = so = 0;
	StreamToStream(buf, table[ESC].buf, table[ESC].count, sp, so, tp, to);
	bit_len += sizeof(BYTE)*8 + table[ESC].count;
	
	for (i = 0; i < ESC; i++)
		if (table[i].count)
		{
			sp = so = 0;
			StreamToStream(buf, (BYTE*)&i, sizeof(BYTE)*8, sp, so, tp, to);
			sp = so = 0;
			StreamToStream(buf, &table[i].count, sizeof(BYTE)*8, sp, so, tp, to);
			sp = so = 0;
			StreamToStream(buf, table[i].buf, table[i].count, sp, so, tp, to);
			bit_len += sizeof(BYTE)*8*2 + table[i].count;
			len_tab++;
		}
		sp = so = 0;
		tp = to = 0;
		StreamToStream(buf, &len_tab, sizeof(BYTE)*8, sp, so, tp, to);
		
		return bit_len/8 + 1;
}
int CHuffman::SetTable(BYTE *buf)
{
	int sp, tp, so, to;
	int i;
	BYTE tab_len, key;
	
	FreeTable();
	
	sp = so = 0;
	tp = to = 0;
	StreamToStream(&tab_len, buf, sizeof(BYTE)*8, sp, so, tp, to);
	
	tp = to = 0;
	StreamToStream(&table[ESC].count, buf, sizeof(BYTE)*8, sp, so, tp, to);
	table[ESC].buf = new BYTE[table[ESC].count/8 + 1];
	memset(table[ESC].buf, 0, table[ESC].count/8 + 1);
	tp = to = 0;
	StreamToStream(table[ESC].buf, buf, table[ESC].count, sp, so, tp, to);
	
	for (i = 0; i < tab_len; i++)
	{
		tp = to = 0;
		StreamToStream(&key, buf, sizeof(BYTE)*8, sp, so, tp, to);
		tp = to = 0;
		StreamToStream(&table[key].count, buf, sizeof(BYTE)*8, sp, so, tp, to);
		table[key].buf = new BYTE[table[key].count/8 + 1];
		memset(table[key].buf, 0, table[key].count/8 + 1);
		tp = to = 0;
		StreamToStream(table[key].buf, buf, table[key].count, sp, so, tp, to);
	}
	// size must be equal to sp+1
	return sp+1+((so >= 8)?1:0);
}
int CHuffman::FindKey(BYTE *buf)
{
	int i, j, k;
	BYTE last;
	int ok;
	for (i = 0; i < ESC+1; i++)
		if (table[i].count)
		{
			ok = 1;
			for (j = 0; j < table[i].count/8+1; j++)
				if (j == table[i].count/8)
				{
					if (!(table[i].count%8)) return i;
					last = 0;
					for (k = 0; k < table[i].count%8; k++)
						last |= buf[j] & (1< ESC_END)) && (count < MAX_NOT_CODED-1))
			{
				sp = so = 0;
				StreamToStream(target, &source[i], sizeof(BYTE)*8, sp, so, tp, to);
				count++;
			} else
			{
				status = 1;
				tsp = tso = 0;
				sp = so = 0;
				StreamToStream(target, (BYTE*)&count, ESC_LENGTH, tsp, tso, ep, eo, false);
				if (count < MAX_NOT_CODED-1)
					StreamToStream(target, table[source[i]].buf, table[source[i]].count, sp, so, tp, to);
				else i--;
			}
		}
		if (tp > slen)
		{
			tlen = 0;
			return;
		}
		OnStep();
	}
	if (!status)
	{
		tsp = tso = 0;
		StreamToStream(target, (BYTE*)&count, ESC_LENGTH, tsp, tso, ep, eo, false);
	}
	tlen = tp+1 + sizeof(DWORD);
	target -= sizeof(DWORD);
	((DWORD*)target)[0] = slen;
}
long CHuffman::DecodeHuffman(BYTE *target, long &tlen, BYTE *source, long slen)
{
	int sp, so, tp, to, tsp, tso, ttp, tto;
	int i, len_buf;
	int status = 1, count = 0;
	int key;
	BYTE buf[MAX_LENGTH/8 + 1];
	sp = so = 0;
	len_buf = MAX_LENGTH;
	tlen = ((DWORD*)source)[0];
	source += sizeof(DWORD);
	for (i = 0; i < tlen; i++)
	{
		if (slen - sp < len_buf/8 + 1)
			len_buf = (slen - sp)*8;
		if (status)
		{
			tsp = sp; tso = so;
			ttp = tto = 0;
			StreamToStream(buf, source, len_buf, tsp, tso, ttp, tto);
			key = FindKey(buf);
			if (key != ESC)
			{
				tp = to = 0;
				StreamToStream(buf, source, table[key].count, sp, so, tp, to);
				target[i] = (BYTE)key;
			} else
			{
				count = 0;
				tp = to = 0;
				StreamToStream(buf, source, table[ESC].count, sp, so, tp, to);
				tp = to = 0;
				StreamToStream((BYTE*)&count, source, ESC_LENGTH, sp, so, tp, to);
				status = 0;
				if (!count) FatalError("Error in encoder. Can`t be zero size sequence\r\n"
					"i = %d, sp = %d, so = %d", i, sp, so);
			}
		}
		if (!status)
		{
			tp = to = 0;
			StreamToStream(&target[i], source, sizeof(BYTE)*8, sp, so, tp, to);
			if (!(--count))
				status = 1;
		}
		OnStep();
	}
	return sp + 1 + sizeof(DWORD);
}
void CHuffman::Encode(BYTE *target, long &tlen, BYTE *source, long slen)
{
	long hlen;
	InitTable(source, slen);
	hlen = GetTable(target);
	EncodeHuffman(target + hlen, tlen, source, slen);
	if (tlen) tlen += hlen;
}
long CHuffman::Decode(BYTE *target, long &tlen, BYTE *source, long slen)
{
	long hlen;
	hlen = SetTable(source); 
 	return hlen + DecodeHuffman(target, tlen, source + hlen, slen - hlen); 
	
}
long CHuffman::GetMaxEncoded(long len)
{
	return 257*(2+32) + len + 256;
}
long CHuffman::GetMaxDecoded(BYTE *source)
{
	long tab_len = SetTable(source);
	return ((DWORD*)(source + tab_len))[0];
}