www.pudn.com > ntshell.rar > lz77.c


#ifdef WIN32 
#include  
#define malloc(s) HeapAlloc(GetProcessHeap(), 0, s) 
#define free(p) HeapFree(GetProcessHeap(), 0, p) 
#else 
#include  
#include  
#endif 
//--------------------------------------------------------------------------- 
 
#define MAXBITS			15 
#define MINOFFSET		0x01 
#define MINMATCH		0x03 
#define MAXMATCH		((1 << 24) + MINMATCH) 
#define MAXWND			(1 << MAXBITS) 
#define NIL				0xffff 
#define M				3 
#define MAX(a, b)		((a) > (b) ? (a) : (b)) 
#define MIN(a, b)		((a) < (b) ? (a) : (b)) 
//--------------------------------------------------------------------------- 
 
typedef unsigned char UCHAR; 
typedef unsigned short USHORT; 
typedef unsigned long ULONG; 
 
typedef struct _LZ77_MATCHINFO 
{ 
	ULONG len; 
	ULONG off; 
} LZ77_MATCHINFO; 
 
typedef struct _LZ77_RUNSTATE 
{ 
	ULONG wsize; 
	UCHAR *pwnd; 
	ULONG confine; 
	USHORT *head; 
	USHORT *prev; 
	ULONG nice; 
} LZ77_RUNSTATE; 
 
typedef struct _LZ77_IOSTATE 
{ 
	UCHAR *pbuf; 
	ULONG bytenum; 
	UCHAR bitnum; 
	UCHAR codelen; 
} LZ77_INPUTS, LZ77_OUTPUTS; 
//--------------------------------------------------------------------------- 
 
static UCHAR log2(ULONG n) 
{ 
	UCHAR c, i; 
 
	if (n > 0xffff) 
	{ 
		for (i = 16; n > ((ULONG)-1 >> (sizeof(ULONG) * 8 - i)); i++); 
		return i; 
	} 
 
	if (n & 0xff00) 
	{ 
		if (n & 0xf000) 
		{ 
			if (n & 0xc000) 
			{ 
				if (n & 0x8000) 
				{ 
					c = 16; 
				} 
				else 
				{ 
					c = 15; 
				} 
			} 
			else 
			{ 
				if (n & 0x2000) 
				{ 
					c = 14; 
				} 
				else 
				{ 
					c = 13; 
				} 
			} 
		} 
		else 
		{ 
			if (n & 0x0c00) 
			{ 
				if (n & 0x0800) 
				{ 
					c = 12; 
				} 
				else 
				{ 
					c = 11; 
				} 
			} 
			else 
			{ 
				if (n & 0x0200) 
				{ 
					c = 10; 
				} 
				else 
				{ 
					c = 9; 
				} 
			} 
		} 
	} 
	else 
	{ 
		if (n & 0x00f0) 
		{ 
			if (n & 0x00c0) 
			{ 
				if (n & 0x0080) 
				{ 
					c = 8; 
				} 
				else 
				{ 
					c = 7; 
				} 
			} 
			else 
			{ 
				if (n & 0x0020) 
				{ 
					c = 6; 
				} 
				else 
				{ 
					c = 5; 
				} 
			} 
		} 
		else 
		{ 
			if (n & 0x000c) 
			{ 
				if (n & 0x0008) 
				{ 
					c = 4; 
				} 
				else 
				{ 
					c = 3; 
				} 
			} 
			else 
			{ 
				if (n & 0x0002) 
				{ 
					c = 2; 
				} 
				else 
				{ 
					c = 1; 
				} 
			} 
		} 
	} 
 
	return c; 
} 
//--------------------------------------------------------------------------- 
 
static void PutBits(LZ77_OUTPUTS *out, ULONG v, int num) 
{ 
	UCHAR *s = out->pbuf + out->bytenum; 
	ULONG i = 0; 
	ULONG temp = v & ~(-1 << num); 
 
	do 
	{ 
		s[i] &= ~(-1 << out->bitnum); 
		s[i] |= (UCHAR)(temp << out->bitnum); 
		s[i + 1] = (UCHAR)(temp >> (8 - out->bitnum)); 
		temp >>= 8; 
	} while ((++i << 3) < (ULONG)num); 
 
	out->bitnum += (UCHAR)num; 
	out->bytenum += out->bitnum >> 3; 
	out->bitnum &= 7; 
} 
//--------------------------------------------------------------------------- 
 
static ULONG GetBits(LZ77_INPUTS *in, int num) 
{ 
	UCHAR *s = in->pbuf + in->bytenum; 
	ULONG i = 0, v = 0; 
 
	do 
	{ 
		v |= (s[i] >> in->bitnum) << (i << 3); 
		v |= (s[i + 1] << (8 - in->bitnum)) << (i << 3); 
	} while ((++i << 3) < (ULONG)num); 
 
	in->bitnum += (UCHAR)num; 
	in->bytenum += in->bitnum >> 3; 
	in->bitnum &= 7; 
 
	return v & ~(-1 << num); 
} 
//--------------------------------------------------------------------------- 
 
static void insert(LZ77_RUNSTATE *rs, ULONG at, ULONG len) 
{ 
	ULONG ins_h, ins_t; 
 
	if (len == 1) 
	{ 
		ins_h = *(USHORT *)(rs->pwnd + at); 
		rs->prev[at] = rs->head[ins_h]; 
		rs->head[ins_h] = (USHORT)at; 
		return; 
	} 
 
	if ((at + len) < MAXWND) 
	{ 
		ins_t = -1; 
		len += at--; 
 
		while (++at != len) 
		{ 
			ins_h = *(USHORT *)(rs->pwnd + at); 
			if ((ins_t - rs->head[ins_h]) <= 2) 
				continue; 
			ins_t = at; 
			rs->prev[at] = rs->head[ins_h]; 
			rs->head[ins_h] = (USHORT)at; 
		} 
	} 
} 
//--------------------------------------------------------------------------- 
 
#define CHARBITS1 4 
#define CHARBITS2 7 
#define CHARBITS3 16 
#define CHARNUMS0 7 
#define CHARNUMS1 ((1 << CHARBITS1) - 1 + (CHARNUMS0 + 1) - 2) 
#define CHARNUMS2 ((1 << CHARBITS2) - 1 + (CHARNUMS1 + 1)) 
#define CHARNUMS3 ((1 << CHARBITS3) - 1 + (CHARNUMS2 + 1)) 
//--------------------------------------------------------------------------- 
//标志位定义: 
//长度:1,值:0,表示后面有一字节未压缩数据 
//长度:2,值:10,表示后面有一个匹配(变长偏移+变长长度) 
//长度:3,值:110,表示后面有一个匹配(7位偏移+1位长度,偏移为128时表示压缩结束) 
//长度:3,值:111,表示后面有多个未压缩字节 
//--------------------------------------------------------------------------- 
 
static void outcodec(LZ77_OUTPUTS *out, UCHAR *buffer, ULONG length) 
{ 
	ULONG i, temp; 
 
	if (length <= CHARNUMS0) 
	{ 
		for (i = 0; i < length; i++) 
		{ 
			//逐字符输出,额外输出位(length) 
			temp = 0x00 | (buffer[i] << 1); 
			PutBits(out, temp, 1 + 8);			//标志位(1),数据位(8) 
		} 
	} 
	else 
	{ 
		if (length <= CHARNUMS1) 
		{ 
			//输出(0-13)表示有(0-13)+8个连续字符未压缩,额外输出位(7) 
			temp = 0x07 | ((length - CHARNUMS0 - 1) << 3); 
			PutBits(out, temp, 3 + CHARBITS1);	//标志位(3),数据位(4) 
		} 
		else if (length <= CHARNUMS1 * 2) 
		{ 
			//优化输出,最大额外输出位(14) 
			outcodec(out, buffer, CHARNUMS1); 
			outcodec(out, buffer + CHARNUMS1, length - CHARNUMS1); 
			return; 
		} 
		else if (length <= CHARNUMS2) 
		{ 
			//输出(14)表示未压缩字符数由后面7位决定,额外输出位(14) 
			temp = 0x07 | (14 << 3); 
			PutBits(out, temp, 3 + CHARBITS1);	//标志位(3),数据位(4) 
 
			temp = length - CHARNUMS1 - 1; 
			PutBits(out, temp, CHARBITS2);		//数据位(7) 
		} 
		else if (length <= CHARNUMS2 + CHARNUMS1) 
		{ 
			//优化输出,最大额外输出位(21) 
			outcodec(out, buffer, CHARNUMS2); 
			outcodec(out, buffer + CHARNUMS2, length - CHARNUMS2); 
			return; 
		} 
		else 
		{ 
			//输出(15)表示未压缩字符数由后面两字节决定,额外输出位(23) 
			temp = 0x07 | (15 << 3); 
			PutBits(out, temp, 3 + CHARBITS1);	//标志位(3),数据位(4) 
 
			//输出(0-65535)+18个连续字符未压缩 
			temp = length - CHARNUMS2 - 1; 
			PutBits(out, temp, CHARBITS3);		//数据位(16) 
		} 
 
		{ 
			UCHAR *s = out->pbuf + out->bytenum; 
			UCHAR x = out->bitnum; 
 
			temp = buffer[0]; 
			PutBits(out, temp, 8 - x); 
 
			//拷贝连续的未压缩字符 
			for (i = 1; i < length; i++) 
			{ 
				s[i] = buffer[i]; 
			} 
 
			temp >>= 8 - x; 
			out->bytenum += length - 1; 
			PutBits(out, temp, x); 
		} 
	} 
} 
//--------------------------------------------------------------------------- 
 
static void outcodex(LZ77_OUTPUTS *out, ULONG offset, ULONG length) 
{ 
	UCHAR i = 0; 
	ULONG temp, m, n; 
 
	switch (length) 
	{ 
//	case 1: 
//		temp = 0x03 | ((offset - MINOFFSET) << 3); 
//		PutBits(out, temp, 3 + 4);				//标志位(3),数据位(4) 
//		return; 
 
	case 3: 
		if (offset > 127) 
			break; 
 
	case 2: 
		temp = 0x03 | ((offset - MINOFFSET) << 3);	//短匹配优化 
		temp |= (length - 2) << (3 + 7); 
		PutBits(out, temp, 3 + 7 + 1);			//标志位(3),数据位(1+7) 
		return; 
	} 
 
	//写入变长匹配偏移 
	temp = 0x01 | ((offset - MINOFFSET) << 2); 
	PutBits(out, temp, 2 + out->codelen);		//标志位(2),数据位(log2(数据)) 
 
	length -= MINMATCH; 
	m = 1 << (M - 1); 
 
	//计算匹配长度最少占用多少位 
	do 
	{ 
		n = ~(-1 << i++) << M; 
		m <<= 1; 
	} while ((m + n) <= length); 
 
	//写入匹配长度位数 
	temp = ~(-1 << (i - 1)); 
	PutBits(out, temp, i); 
 
	//写入变长匹配长度 
	temp = length - n; 
	PutBits(out, temp, i + 3 - 1); 
}//*/ 
//--------------------------------------------------------------------------- 
 
static int match(LZ77_RUNSTATE *rs, ULONG strat, LZ77_MATCHINFO *mi) 
{ 
	UCHAR *src, *s, *d, *c, *t; 
	USHORT index, *prev; 
	ULONG i, m = 0, n, nice, flag = 0; 
 
	src = rs->pwnd; 
	index = rs->head[*(USHORT *)(src + strat)]; 
 
	if (NIL != index) 
	{ 
		c = src + MIN(rs->confine, MAXMATCH); 
		t = src + strat; 
		m = MINMATCH - 1; 
		prev = rs->prev; 
		nice = rs->nice; 
 
		do 
		{ 
			s = t; 
			d = src + index; 
 
			while (*(USHORT *)(s += 2) == *(USHORT *)(d += 2) 
				&& *(USHORT *)(s += 2) == *(USHORT *)(d += 2) 
				&& *(USHORT *)(s += 2) == *(USHORT *)(d += 2) 
				&& *(USHORT *)(s += 2) == *(USHORT *)(d += 2) 
				&& s < c); 
			if (*s == *d) s++; 
 
			if (s >= c) 
			{ 
				m = c - t; 
				n = index; 
				break; 
			} 
			i = s - t; 
 
			if (m < i) 
			{ 
				m = i; 
				n = index; 
				if (m > nice) 
					flag = 1; 
			} 
			else if (flag) 
				break; 
			index = prev[index]; 
		} while (NIL != index); 
	} 
 
	if (MINMATCH <= m) 
	{ 
		mi->len = m; 
		mi->off = strat - n; 
		return 1; 
	} 
	else 
	{ 
		index = rs->head[*(USHORT *)(src + strat)]; 
 
		if (strat - index <= 127) 
		{ 
			mi->len = 2; 
			mi->off = strat - index; 
			return 1; 
		}/* 
		else 
		{ 
			//从前面16字节中查找1字节匹配 
			for (i = 16; i > 0; i--) 
			{ 
				if (*(src + strat - i) == *(src + strat)) 
				{ 
					mi->len = 1; 
					mi->off = i; 
					return 1; 
				} 
			} 
		}//*/ 
	} 
 
	return 0; 
} 
//--------------------------------------------------------------------------- 
 
static ULONG deflate(LZ77_RUNSTATE rs, UCHAR *dst, ULONG *inbytes) 
{ 
	LZ77_MATCHINFO mi; 
	LZ77_OUTPUTS out; 
	ULONG strstart = 0, count = 0; 
	LZ77_OUTPUTS prev_out; 
	ULONG prev_start = 0; 
 
	out.pbuf = dst; 
	out.bytenum = 0; 
	out.bitnum = 0; 
	out.codelen = 1; 
	prev_out = out; 
 
	memset(rs.head, NIL, sizeof(USHORT) * 65536); 
 
	do 
	{ 
		if (match(&rs, strstart, &mi)) 
		{ 
			if (count > 0) 
			{ 
				outcodec(&out, rs.pwnd + strstart - count, count);		//输出无匹配字符 
				count = 0; 
			} 
 
			if ((ULONG)(1 << out.codelen) < strstart) 
				out.codelen = log2(strstart - MINOFFSET); 
 
			insert(&rs, strstart, mi.len);						//更新字典记录 
			outcodex(&out, mi.off, mi.len);						//输出压缩代码 
			strstart += mi.len; 
		} 
		else 
		{ 
			insert(&rs, strstart, 1);							//更新字典记录 
			count++; 
			strstart += 1; 
		} 
 
		if (strstart - prev_start > 0x1000)						//跟踪压缩率变化 
		{ 
			if (strstart - prev_start < out.bytenum - prev_out.bytenum - 4) 
			{ 
				out = prev_out; 
				outcodec(&out, rs.pwnd + prev_start, strstart - prev_start);	//处理无法压缩的块 
			} 
 
			prev_out = out; 
			prev_start = strstart; 
		} 
	} while (strstart < rs.wsize); 
 
	if (count > 0) 
	{ 
		outcodec(&out, rs.pwnd + strstart - count, count);		//输出无匹配字符 
		count = 0; 
	} 
 
	if (strstart - prev_start < out.bytenum - prev_out.bytenum - 4) 
	{ 
		out = prev_out; 
		outcodec(&out, rs.pwnd + prev_start, strstart - prev_start);	//处理无法压缩的块 
	} 
 
	outcodex(&out, 128, 2);		//输出压缩结束标记 
 
	if (out.bitnum) 
		out.bytenum++; 
 
	*inbytes = strstart; 
 
	return out.bytenum; 
} 
//--------------------------------------------------------------------------- 
 
static ULONG inflate(UCHAR *src, UCHAR *dst, ULONG len, ULONG *inbytes) 
{ 
	ULONG offset, length; 
	UCHAR i, t; 
	UCHAR *out, *s; 
	LZ77_INPUTS in; 
 
	in.pbuf = src; 
	in.bytenum = 0; 
	in.bitnum = 0; 
	in.codelen = 1; 
	out = dst; 
 
	while (in.bytenum < len) 
	{ 
		if (!GetBits(&in, 1))			//0 ? 
		{ 
			*out++ = (UCHAR)GetBits(&in, 8); 
		} 
		else 
		{ 
			if (!GetBits(&in, 1))		//10 ? 
			{ 
				if ((1 << in.codelen) < out - dst) 
					in.codelen = log2(out - dst - MINOFFSET); 
				offset = GetBits(&in, in.codelen) + MINOFFSET; 
 
				for (i = 0; GetBits(&in, 1); i++);	//计算匹配长度位数 
				length = GetBits(&in, i + M); 
				length += (~(-1 << i) << M) + MINMATCH;		//计算匹配长度 
 
				do 
				{ 
					*out++ = *(out - offset); 
				} while (--length); 
			} 
			else 
			{ 
				if (!GetBits(&in, 1))	//110 ? 
				{ 
					offset = GetBits(&in, 7) + MINOFFSET; 
					length = GetBits(&in, 1) + 2; 
 
					if (offset == 128)	//解压结束? 
						break; 
 
					do 
					{ 
						*out++ = *(out - offset);	//解压短匹配 
					} while (--length); 
				} 
				else 
				{ 
					length = GetBits(&in, CHARBITS1); 
 
					switch (length) 
					{ 
					case 14: 
						length = GetBits(&in, CHARBITS2); 
						length += CHARNUMS1 + 1; 
						break; 
					case 15: 
						length = GetBits(&in, CHARBITS3); 
						length += CHARNUMS2 + 1; 
						break; 
					default: 
						length += CHARNUMS0 + 1; 
						break; 
					} 
 
					s = in.pbuf + in.bytenum; 
					offset = 1; 
					i = in.bitnum; 
 
					do 
					{ 
						out[offset] = s[offset];	//还原字符串 
					} while (++offset < length); 
 
					t = (UCHAR)GetBits(&in, 8 - i); 
					in.bytenum += length - 1; 
					t |= (UCHAR)(GetBits(&in, i) << (8 - i)); 
					*out = t; 
					out += length; 
				} 
			} 
		} 
	} 
 
	*inbytes = in.bytenum; 
	*inbytes += in.bitnum == 0 ? 0 : 1; 
 
	return out - dst; 
} 
//--------------------------------------------------------------------------- 
 
ULONG Lz77Compress(void *dst, void *src, ULONG len, int level) 
{ 
	LZ77_RUNSTATE rs; 
	ULONG m, n, count = 0; 
 
	if (0 == len) 
		return 0; 
 
	if (!src || !dst) 
		return -1; 
 
	switch (level) 
	{ 
	case 0: 
		rs.nice = 3; 
		break; 
	case 1: 
		rs.nice = 30; 
		break; 
	case 2: 
		rs.nice = 70; 
		break; 
	case 3: 
		rs.nice = 150; 
		break; 
	case 4: 
		rs.nice = -1; 
		break; 
	} 
 
	rs.prev = (USHORT *)malloc(sizeof(USHORT) * 65536); 
	rs.head = (USHORT *)malloc(sizeof(USHORT) * 65536); 
 
	if (!rs.prev || !rs.head) 
	{ 
		free(rs.head); 
		free(rs.prev); 
		return -1; 
	} 
 
	do 
	{ 
		rs.wsize = MIN(len, MAXWND); 
		rs.pwnd = src; 
		//rs.confine = len; 
		rs.confine = MIN(len, MAXWND); 
		n = deflate(rs, dst, &m); 
		len -= m; 
		(UCHAR *)src += m; 
		(UCHAR *)dst += n; 
		count += n; 
	} while ((int)len > 0); 
 
	free(rs.head); 
	free(rs.prev); 
 
	return count; 
} 
//--------------------------------------------------------------------------- 
 
ULONG Lz77Decompress(void *dst, void *src, ULONG len) 
{ 
	ULONG c = 0, i, o, a = 0; 
 
	do 
	{ 
		o = inflate(src, dst, len, &i); 
		if ((a += o) >= 0x88000) 
		{int c = 0;} 
		(UCHAR *)src += i; 
		(UCHAR *)dst += o; 
		len -= i; 
		c += o; 
	} while (len); 
 
	return c; 
} 
//---------------------------------------------------------------------------