解压缩过程在一个循环中执行,此循环会持续迭代执行直到所有的符号处理完。使用ipos来保存向压缩数据中写入的当前位,并用opos来保存写入恢复数据缓冲区中当前字节。在循环的每次迭代过程中,首先从压缩数据读取一位来确定要解码的标记类型。
在解析一个标记时,如果读取的首位是1,说明遇到了一个短语标记。此时读取它的每个成员,查找滑动窗口中的短语,然后将短语写入恢复数据缓冲区中。当查找每个短语时,调用网络函数ntohl来保证窗口中的偏移量和长度的字节顺序是与操作系统匹配的。这个转换过程是必要的,因为从压缩数据中读取出来的偏移量和长度是大端格式的。在数据被拷贝到滑动窗口之前,前向缓冲区被用做一个临时转换区来保存数据。最后,写入该标记编码的匹配的符号。如果读取的标记的首位是0,说明遇到了一个符号标记。在这种情况下,将该标记编码的匹配符号写入恢复数据缓冲区中。
一旦将解码的数据写入恢复数据的缓冲区中,就调整滑动窗口。要将数据通过滑动窗口,将数据从右边滑入窗口,从左边滑出窗口。移动的字节数与从标记中解码的字符数相等。
lz77_uncompress的时间复杂度为O(n),其中n是原始数据中符号的个数。
示例:LZ77的实现文件
(示例所需要的头文件信息请查阅这篇文章:数据压缩的重要组成部分--位操作)
/*lz77.c*/
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include "bit.h"
#include "compress.h"
/*compare_win 确定前向缓冲区中与滑动窗口中匹配的最长短语*/
static int compare_win(const unsigned char *window, const unsigned char *buffer,
int *offset, unsigned char *next)
{
int match,longest,i,j,k;
/*初始化偏移量*/
*offset = 0;
/*如果没有找到匹配,准备在前向缓冲区中返回0和下一个字符*/
longest = 0;
*next = buffer[0];
/*在前向缓冲区和滑动窗口中寻找最佳匹配*/
for(k=0; k<LZ77_WINDOW_SIZE; k++)
{
i = k;
j = 0;
match = 0;
/*确定滑动窗口中k个偏移量匹配的符号数*/
while(i<LZ77_WINDOW_SIZE && j<LZ77_BUFFER_SIZE - 1)
{
if(window[i] != buffer[j])
break;
match++;
i++;
j++;
}
/*跟踪最佳匹配的偏移、长度和下一个符号*/
if(match > longest)
{
*offset = k;
longest = match;
*next = buffer[j];
}
}
return longest;
}
/*lz77_compress 使用lz77算法压缩数据*/
int lz77_compress(const unsigned char *original,unsigned char **compressed,int size)
{
unsigned char window[LZ77_WINDOW_SIZE],
buffer[LZ77_BUFFER_SIZE],
*comp,
*temp,
next;
int offset,
length,
remaining,
hsize,
ipos,
opos,
tpos,
i;
/*使指向压缩数据的指针暂时无效*/
*compressed = NULL;