www.pudn.com > LZ77code.zip > Lz77.cpp
////////////////////////////// // LZ77.CPP ////////////////////////////// #include#include #include #include #include "lz77.h" ///////////////////////////////////////////////////////// // 取log2(n)的upper_bound int CCompress::UpperLog2(int n) { int i = 0; if (n > 0) { int m = 1; while(1) { if (m >= n) return i; m <<= 1; i++; } } else return -1; } // UpperLog2 ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// // 取log2(n)的lower_bound int CCompress::LowerLog2(int n) { int i = 0; if (n > 0) { int m = 1; while(1) { if (m == n) return i; if (m > n) return i - 1; m <<= 1; i++; } } else return -1; } // LowerLog2 ///////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// // 将位指针*piByte(字节偏移), *piBit(字节内位偏移)后移num位 void CCompress::MovePos(int* piByte, int* piBit, int num) { num += (*piBit); (*piByte) += num / 8; (*piBit) = num % 8; } // MovePos //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// // 得到字节byte第pos位的值 // pos顺序为高位起从0记数(左起) BYTE CCompress::GetBit(BYTE byte, int pos) { int j = 1; j <<= 7 - pos; if (byte & j) return 1; else return 0; } // GetBit ///////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////// // 设置byte的第iBit位为aBit // iBit顺序为高位起从0记数(左起) void CCompress::SetBit(BYTE* byte, int iBit, BYTE aBit) { if (aBit) (*byte) |= (1 << (7 - iBit)); else (*byte) &= ~(1 << (7 - iBit)); } // SetBit ////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////// // 将DWORD值从高位字节到低位字节排列 void CCompress::InvertDWord(DWORD* pDW) { union UDWORD{ DWORD dw; BYTE b[4]; }; UDWORD* pUDW = (UDWORD*)pDW; BYTE b; b = pUDW->b[0]; pUDW->b[0] = pUDW->b[3]; pUDW->b[3] = b; b = pUDW->b[1]; pUDW->b[1] = pUDW->b[2]; pUDW->b[2] = b; } // InvertDWord ////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////// // CopyBits : 复制内存中的位流 // memDest - 目标数据区 // nDestPos - 目标数据区第一个字节中的起始位 // memSrc - 源数据区 // nSrcPos - 源数据区第一个字节的中起始位 // nBits - 要复制的位数 // 说明: // 起始位的表示约定为从字节的高位至低位(由左至右) // 依次为 0,1,... , 7 // 要复制的两块数据区不能有重合 void CCompress::CopyBits(BYTE* memDest, int nDestPos, BYTE* memSrc, int nSrcPos, int nBits) { int iByteDest = 0, iBitDest; int iByteSrc = 0, iBitSrc = nSrcPos; int nBitsToFill, nBitsCanFill; while (nBits > 0) { // 计算要在目标区当前字节填充的位数 nBitsToFill = min(nBits, iByteDest ? 8 : 8 - nDestPos); // 目标区当前字节要填充的起始位 iBitDest = iByteDest ? 0 : nDestPos; // 计算可以一次从源数据区中复制的位数 nBitsCanFill = min(nBitsToFill, 8 - iBitSrc); // 字节内复制 CopyBitsInAByte(memDest + iByteDest, iBitDest, memSrc + iByteSrc, iBitSrc, nBitsCanFill); // 如果还没有复制完 nBitsToFill 个 if (nBitsToFill > nBitsCanFill) { iByteSrc++; iBitSrc = 0; iBitDest += nBitsCanFill; CopyBitsInAByte(memDest + iByteDest, iBitDest, memSrc + iByteSrc, iBitSrc, nBitsToFill - nBitsCanFill); iBitSrc += nBitsToFill - nBitsCanFill; } else { iBitSrc += nBitsCanFill; if (iBitSrc >= 8) { iByteSrc++; iBitSrc = 0; } } nBits -= nBitsToFill; // 已经填充了nBitsToFill位 iByteDest++; } } // CopyBits ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// // CopyBitsInAByte : 在一个字节范围内复制位流 // 参数含义同 CopyBits 的参数 // 说明: // 此函数由 CopyBits 调用,不做错误检查,即 // 假定要复制的位都在一个字节范围内 void CCompress::CopyBitsInAByte(BYTE* memDest, int nDestPos, BYTE* memSrc, int nSrcPos, int nBits) { BYTE b1, b2; b1 = *memSrc; b1 <<= nSrcPos; b1 >>= 8 - nBits; // 将不用复制的位清0 b1 <<= 8 - nBits - nDestPos; // 将源和目的字节对齐 *memDest |= b1; // 复制值为1的位 b2 = 0xff; b2 <<= 8 - nDestPos; // 将不用复制的位置1 b1 |= b2; b2 = 0xff; b2 >>= nDestPos + nBits; b1 |= b2; *memDest &= b1; // 复制值为0的位 } // CopyBitsInAByte ///////////////////////////////////////////////////////// //------------------------------------------------------------------ CCompressLZ77::CCompressLZ77() { SortHeap = new struct STIDXNODE[_MAX_WINDOW_SIZE]; } CCompressLZ77::~CCompressLZ77() { delete[] SortHeap; } // 初始化索引表,释放上次压缩用的空间 void CCompressLZ77::_InitSortTable() { memset(SortTable, 0, sizeof(WORD) * 65536); nWndSize = 0; HeapPos = 1; } // 向索引中添加一个2字节串 void CCompressLZ77::_InsertIndexItem(int off) { WORD q; BYTE ch1, ch2; ch1 = pWnd[off]; ch2 = pWnd[off + 1]; if (ch1 != ch2) { // 新建节点 q = HeapPos; HeapPos++; SortHeap[q].off = off; SortHeap[q].next = SortTable[ch1 * 256 + ch2]; SortTable[ch1 * 256 + ch2] = q; } else { // 对重复2字节串 // 因为没有虚拟偏移也没有删除操作,只要比较第一个节点 // 是否和 off 相连接即可 q = SortTable[ch1 * 256 + ch2]; if (q != 0 && off == SortHeap[q].off2 + 1) { // 节点合并 SortHeap[q].off2 = off; } else { // 新建节点 q = HeapPos; HeapPos++; SortHeap[q].off = off; SortHeap[q].off2 = off; SortHeap[q].next = SortTable[ch1 * 256 + ch2]; SortTable[ch1 * 256 + ch2] = q; } } } ////////////////////////////////////////// // 将窗口向右滑动n个字节 void CCompressLZ77::_ScrollWindow(int n) { for (int i = 0; i < n; i++) { nWndSize++; if (nWndSize > 1) _InsertIndexItem(nWndSize - 2); } } /////////////////////////////////////////////////////////// // 得到已经匹配了2个字节的窗口位置offset // 共能匹配多少个字节 int CCompressLZ77::_GetSameLen(BYTE* src, int srclen, int nSeekStart, int offset) { int i = 2; // 已经匹配了2个字节 int maxsame = min(srclen - nSeekStart, nWndSize - offset); while (i < maxsame && src[nSeekStart + i] == pWnd[offset + i]) i++; _ASSERT(nSeekStart + i <= srclen && offset + i <= nWndSize); return i; } /////////////////////////////////////////////////////////// // 在滑动窗口中查找术语 // nSeekStart - 从何处开始匹配 // offset, len - 用于接收结果,表示在滑动窗口内的偏移和长度 // 返回值- 是否查到长度为2或2以上的匹配字节串 BOOL CCompressLZ77::_SeekPhase(BYTE* src, int srclen, int nSeekStart, int* offset, int* len) { int j, m, n; if (nSeekStart < srclen - 1) { BYTE ch1, ch2; ch1 = src[nSeekStart]; ch2 = src[nSeekStart + 1]; WORD p; p = SortTable[ch1 * 256 + ch2]; if (p != 0) { m = 2; n = SortHeap[p].off; while (p != 0) { j = _GetSameLen(src, srclen, nSeekStart, SortHeap[p].off); if ( j > m ) { m = j; n = SortHeap[p].off; } p = SortHeap[p].next; } (*offset) = n; (*len) = m; return TRUE; } } return FALSE; } //////////////////////////////////////// // 输出压缩码 // code - 要输出的数 // bits - 要输出的位数(对isGamma=TRUE时无效) // isGamma - 是否输出为γ编码 void CCompressLZ77::_OutCode(BYTE* dest, DWORD code, int bits, BOOL isGamma) { if ( isGamma ) { BYTE* pb; DWORD out; // 计算输出位数 int GammaCode = (int)code - 1; int q = LowerLog2(GammaCode); if (q > 0) { out = 0xffff; pb = (BYTE*)&out; // 输出q个1 CopyBits(dest + CurByte, CurBit, pb, 0, q); MovePos(&CurByte, &CurBit, q); } // 输出一个0 out = 0; pb = (BYTE*)&out; CopyBits(dest + CurByte, CurBit, pb + 3, 7, 1); MovePos(&CurByte, &CurBit, 1); if (q > 0) { // 输出余数, q位 int sh = 1; sh <<= q; out = GammaCode - sh; pb = (BYTE*)&out; InvertDWord(&out); CopyBits(dest + CurByte, CurBit, pb + (32 - q) / 8, (32 - q) % 8, q); MovePos(&CurByte, &CurBit, q); } } else { DWORD dw = (DWORD)code; BYTE* pb = (BYTE*)&dw; InvertDWord(&dw); CopyBits(dest + CurByte, CurBit, pb + (32 - bits) / 8, (32 - bits) % 8, bits); MovePos(&CurByte, &CurBit, bits); } } ///////////////////////////////////////////// // 压缩一段字节流 // src - 源数据区 // srclen - 源数据区字节长度 // dest - 压缩数据区,调用前分配srclen+5字节内存 // 返回值 > 0 压缩数据长度 // 返回值 = 0 数据无法压缩 // 返回值 < 0 压缩中异常错误 int CCompressLZ77::Compress(BYTE* src, int srclen, BYTE* dest) { int i; CurByte = 0; CurBit = 0; int off, len; if (srclen > 65536) return -1; pWnd = src; _InitSortTable(); for (i = 0; i < srclen; i++) { if (CurByte >= srclen) return 0; if (_SeekPhase(src, srclen, i, &off, &len)) { // 输出匹配术语 flag(1bit) + len(γ编码) + offset(最大16bit) _OutCode(dest, 1, 1, FALSE); _OutCode(dest, len, 0, TRUE); // 在窗口不满64k大小时,不需要16位存储偏移 _OutCode(dest, off, UpperLog2(nWndSize), FALSE); _ScrollWindow(len); i += len - 1; } else { // 输出单个非匹配字符 0(1bit) + char(8bit) _OutCode(dest, 0, 1, FALSE); _OutCode(dest, (DWORD)(src[i]), 8, FALSE); _ScrollWindow(1); } } int destlen = CurByte + ((CurBit) ? 1 : 0); if (destlen >= srclen) return 0; return destlen; } ///////////////////////////////////////////// // 解压缩一段字节流 // src - 接收原始数据的内存区 // srclen - 源数据区字节长度 // dest - 压缩数据区 // 返回值 - 成功与否 BOOL CCompressLZ77::Decompress(BYTE* src, int srclen, BYTE* dest) { int i; CurByte = 0; CurBit = 0; pWnd = src; // 初始化窗口 nWndSize = 0; if (srclen > 65536) return FALSE; for (i = 0; i < srclen; i++) { BYTE b = GetBit(dest[CurByte], CurBit); MovePos(&CurByte, &CurBit, 1); if (b == 0) // 单个字符 { CopyBits(src + i, 0, dest + CurByte, CurBit, 8); MovePos(&CurByte, &CurBit, 8); nWndSize++; } else // 窗口内的术语 { int q = -1; while (b != 0) { q++; b = GetBit(dest[CurByte], CurBit); MovePos(&CurByte, &CurBit, 1); } int len, off; DWORD dw = 0; BYTE* pb; if (q > 0) { pb = (BYTE*)&dw; CopyBits(pb + (32 - q) / 8, (32 - q) % 8, dest + CurByte, CurBit, q); MovePos(&CurByte, &CurBit, q); InvertDWord(&dw); len = 1; len <<= q; len += dw; len += 1; } else len = 2; // 在窗口不满64k大小时,不需要16位存储偏移 dw = 0; pb = (BYTE*)&dw; int bits = UpperLog2(nWndSize); CopyBits(pb + (32 - bits) / 8, (32 - bits) % 8, dest + CurByte, CurBit, bits); MovePos(&CurByte, &CurBit, bits); InvertDWord(&dw); off = (int)dw; // 输出术语 for (int j = 0; j < len; j++) { _ASSERT(i + j < srclen); _ASSERT(off + j < _MAX_WINDOW_SIZE); src[i + j] = pWnd[off + j]; } nWndSize += len; i += len - 1; } // 滑动窗口 if (nWndSize > _MAX_WINDOW_SIZE) { pWnd += nWndSize - _MAX_WINDOW_SIZE; nWndSize = _MAX_WINDOW_SIZE; } } return TRUE; }