LZ77源码阅读笔记
来源:互联网 发布:三星scx3401扫描软件 编辑:程序博客网 时间:2024/05/28 15:35
/*********************************************************************
*
* Project description:
* Lz77 compression/decompression algorithm.
*
*********************************************************************/
#include <windows.h>
#include <conio.h>
#include <stdio.h>
#include <assert.h>
#define OFFSET_CODING_LENGTH (10)
#define MAX_WND_SIZE 1024
//#define MAX_WND_SIZE (1<<OFFSET_CODING_LENGTH)
#define OFFSET_MASK_CODE (MAX_WND_SIZE-1)
const ULONG m=3; //m是Golomb编码使用的常数
UCHAR __buffer1__[0x200000];
UCHAR __buffer2__[0x200000];
void
Write1ToBitStream(
PUCHAR pBuffer,
ULONG ulBitOffset
)
{
ULONG ulByteBoundary;
ULONG ulOffsetInByte;
//计算ulBitOffset右移3位,即判断所给定的位偏移,相对pBuffer来说是第几个字符的地址,每个字符占8个bit
//ulBitOffset与0x00000111做与运算可以知道要在所指向的那个字符的第几个位置置1
ulByteBoundary = ulBitOffset>>3
ulOffsetInByte = ulBitOffset&7;
*(pBuffer+ulByteBoundary) |= (1<<ulOffsetInByte);
}
读取的过程正好和写过程是对称:
ULONG
ReadBitFromBitStream(
PUCHAR pBuffer,
ULONG ulBitOffset
)
{
ULONG ulByteBoundary;
ULONG ulOffsetInByte;
//首先计算字符位置和字符内偏移量
ulByteBoundary = ulBitOffset>>3 ;
ulOffsetInByte = ulBitOffset&7;
//然后通过偏移与0x00000001与运算就知道所给定的偏移位置上是0还是1
return ((*(PULONG)(pBuffer+ulByteBoundary))>>ulOffsetInByte)&1 ;
}
Golomb编码:
先看一下Golomb编码的规范:
Golomb 编码。假设对正整数 x 进行 Golomb 编码,选择参数 m,令
b = 2^m
q = INT((x - 1)/b)
r = x - q^b - 1
则 x 可以被编码为两部分,第一部分是由 q 个 1 加 1 个 0 组成,第二部分为 m 位二进制数,其值为 r。我们将 m = 0, 1, 2, 3 时的 Golomb 编码表列出:
值 x m = 0 m = 1 m = 2 m = 3
-------------------------------------------------------------
1 0 0 0 0 00 0 000
2 10 0 1 0 01 0 001
3 110 10 0 0 10 0 010
4 1110 10 1 0 11 0 011
5 11110 110 0 10 00 0 100
6 111110 110 1 10 01 0 101
7 1111110 1110 0 10 10 0 110
8 11111110 1110 1 10 11 0 111
9 111111110 11110 0 110 00 10 000
从表中我们可以看出,Golomb 编码不但符合前缀编码的规律,而且可以用较少的位表示
较小的 x 值,而用较长的位表示较大的 x 值。这样,如果 x 的取值倾向于比较小的数值,Golomb 编码就可以有效地节省空间。当然,根据 x 的分布规律不同,我们可以选取不同的 m 值以达到最好的压缩效果。
对我们上面讨论的三元组 len 值,我们可以采用 Golomb 方式编码。上面的讨论中 len 可能取 0,我们只需用 len + 1 的 Golomb 编码即可。至于参数 m 的选择,一般经验是取 3 或 4 即可。
ULONG WINAPI
WriteGolombCode(
ULONG x,
PUCHAR pBuffer,
ULONG ulBitOffset
)
{
ULONG q, r;
int i;
q = (x-1)>>m;
r = x-(q<<m)-1;
//首先写q个1
for(i=0; (ULONG)i<q; i++, ulBitOffset++)
{
Write1ToBitStream(pBuffer, ulBitOffset);
}
//q个1和m位r的2进制编码间用0隔开
Write0ToBitStream(pBuffer, ulBitOffset);
ulBitOffset++;
//m位r的2进制编码
for(i=0; i<m; i++, ulBitOffset++)
{
if( (r>>i)&1 )
{
Write1ToBitStream(pBuffer, ulBitOffset);
}
else
{
Write0ToBitStream(pBuffer, ulBitOffset);
}
}
//返回GolombCode长度,这个长度是解码时候计算偏移的增量有用
return m+q+1;
}
然后看写入Bit的操作WriteBits,用来在指定的Buff偏移位置写入一串bits:
void
WriteBits(
PUCHAR pDataBuffer,
ULONG ulOffsetToWrite,
ULONG ulBits,
ULONG ulBitLength
)
{
ULONG ulDwordsOffset;
ULONG ulBitsOffset, ulBitsRemained;
ulDwordsOffset = ulOffsetToWrite>>5;
ulBitsOffset = ulOffsetToWrite&31;
ulBitsRemained = 32 - ulBitsOffset;
if( 0==ulBitsOffset )
{
*((PULONG)pDataBuffer+ulDwordsOffset) = ulBits;
}
else if( ulBitsRemained>=ulBitLength )
{
*((PULONG)pDataBuffer+ulDwordsOffset) |= (ulBits<<ulBitsOffset);
}
else
{
*((PULONG)pDataBuffer+ulDwordsOffset) |= (ulBits<<ulBitsOffset);
*((PULONG)pDataBuffer+ulDwordsOffset+1) = ulBits>>ulBitsRemained;
}
}
看起来有点绕,不过通过示意图就很明了了:
下面就是LZ77压缩的主体函数:
void
lz77compress(
PUCHAR pDataBuffer,
ULONG ulDataLength,
PUCHAR pOutputBuffer,
PULONG pulNumberOfBits
)
{
LONG iSlideWindowPtr;
ULONG ulBytesCoded;
ULONG ulMaxlength;
PUCHAR pSlideWindowPtr;
PUCHAR pUnprocessedDataPtr;
ULONG offset;
ULONG length;
ULONG ulCodingLength;
ULONG ulBitOffset;
UCHAR cc;
int i;
//滑动窗口的初始长度是-MAX_WND_SIZE,因为现在还没有字符被编码,所以滑动窗口的最右端就是编码字串的最前端。
iSlideWindowPtr = -MAX_WND_SIZE;
pSlideWindowPtr = NULL;
ulBitOffset = 0;
ulBytesCoded = 0;
while( ulBytesCoded<ulDataLength )
{
//当iSildeWindowPtr>=0时,已编码的字串长度已经大于等于滑动窗的长度
if( iSlideWindowPtr>=0 )
{
pSlideWindowPtr = pDataBuffer+iSlideWindowPtr;
ulMaxlength = MAX_WND_SIZE;
}
else if( iSlideWindowPtr>=-MAX_WND_SIZE )
{
pSlideWindowPtr = pDataBuffer;
ulMaxlength = MAX_WND_SIZE + iSlideWindowPtr;
}
else
{
pSlideWindowPtr = NULL;
ulMaxlength = 0;
}
pUnprocessedDataPtr = pDataBuffer + ulBytesCoded;
if( ulMaxlength>ulDataLength-ulBytesCoded )
{
ulMaxlength = ulDataLength-ulBytesCoded;
}
//在已编码的串中搜索待编码字串的最长组合,并记录长度、偏移
FindLongestSubstring(
pSlideWindowPtr,
pUnprocessedDataPtr,
ulMaxlength,
&offset,
&length
);
assert( length<=MAX_WND_SIZE );
assert( offset<MAX_WND_SIZE );
if(length>1)
{
//如果匹配长度大于1,将首位置1,然后写入偏移的位置,因为搜索窗口长度是10位的,所以这个偏移按照10位的2进制编码写入。最后写入Golomb编码。
Write1ToBitStream(pOutputBuffer, ulBitOffset);
ulBitOffset++;
for(i=0; i<OFFSET_CODING_LENGTH; i++, ulBitOffset++)
{
if( (offset>>i)&1 )
{
Write1ToBitStream(pOutputBuffer, ulBitOffset);
}
else
{
Write0ToBitStream(pOutputBuffer, ulBitOffset);
}
}
ulCodingLength = WriteGolombCode(length, pOutputBuffer, ulBitOffset);
ulBitOffset += ulCodingLength;
iSlideWindowPtr += length;
ulBytesCoded += length;
}
else
{
//如果匹配长度小于等于1,将首位置0,然后写入对应字符的8位2进制编码
Write0ToBitStream(pOutputBuffer, ulBitOffset);
ulBitOffset++;
cc = (*pUnprocessedDataPtr);
for(i=0; i<8; i++, ulBitOffset++)
{
if( (cc>>i)&1 )
{
Write1ToBitStream(pOutputBuffer, ulBitOffset);
}
else
{
Write0ToBitStream(pOutputBuffer, ulBitOffset);
}
}
iSlideWindowPtr++;
ulBytesCoded++;
}
}
if( ulBytesCoded!=ulDataLength )
{
assert(ulBytesCoded==ulDataLength);
}
*pulNumberOfBits = ulBitOffset;
}
LZ77的解码过程是上面的逆过程就不敷述了。
- LZ77源码阅读笔记
- LZ77
- Boa 源码阅读笔记
- VNC源码阅读笔记
- osqa源码阅读笔记
- VNC源码阅读笔记
- MPlayer 源码阅读笔记
- PyEmu源码阅读笔记
- JSONModel源码阅读笔记
- JSONModel源码阅读笔记
- HSF源码阅读笔记
- memcached 源码阅读笔记
- ALLJOYN源码阅读笔记
- netmap源码阅读笔记
- ArrayList源码阅读笔记
- Flask 源码阅读笔记
- memcached 源码阅读笔记
- NASM源码阅读笔记
- 内核态进程管理器Intercessor和实现细节
- 简单认识Anti-RootKit
- C++如何使用cscope
- 数组越界之后
- Jakarta Struts 项目的介绍和它的支持组件
- LZ77源码阅读笔记
- ECC的代码实现
- 一道题目
- The Two Types of Programmers
- 一道指针内存题目的三个改法
- 悬浮窗 & 动态菜单 VC++
- php eval函数用法
- PHP效率优化
- javascript小技巧&&JavaScript[对象.属性]集锦 [转载]