β

zlib流式解压

码农日记 517 阅读

zlib实现解压的例子官方已经给出

http://www.zlib.net/zlib_how.html

最常见的解压方式就是现成从堆分配出适合大小的内存,直接向这个内存里解压,这样是不错的,一些情况下这样是非常适合的,但是如果文件很大,需要实现一个流式解压的功能,比如文件非常大,需要向文件系统里写文件。

实现的效果如下:

1
2
3
4
5
6
7
8
9
FILE * file = fopen ( "text.txt" , "wb+" );
InflateStream inflateStream( "dest.file" );
char buffer[1024];
while (!inflateStream.Eof())
{
int bytes = inflateStream.Inflate(buffer,1024);
fwrite (buffer,1,bytes,file);
}
fclose (file);

为了方便扩展,定义一个解压前的数据流式读取接口

1
2
3
4
5
6
7
8
struct IIStream
{
virtual size_t GetLength() = 0;
virtual size_t Read( size_t size_,unsigned char * buff_out_) = 0;
virtual bool Eof() = 0;
virtual bool Valid() = 0;
virtual void Release() = 0;
};

针对FILE读取实现一个流式读取接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
struct StdFileStream: public IIStream
{
FILE * m_file;
unsigned long m_iLen;
StdFileStream( const char * szPath)
{
m_file = fopen (szPath, "rb+" );
if (m_file)
{
m_iLen = ftell (m_file);
fseek (m_file,0,SEEK_END);
m_iLen = ftell (m_file) - m_iLen;
fseek (m_file,0,SEEK_SET);
}
else
{
m_iLen = 0;
}
}
bool Valid()
{
return m_iLen > 0;
}
size_t GetLength()
{
return m_iLen;
}
size_t Read( size_t size_,unsigned char * buff_out_)
{
return fread (buff_out_,1,size_,m_file);
}
bool Eof()
{
return feof (m_file);
}
void Release()
{
if (m_file)
fclose (m_file);
delete this ;
}
};
IIStream * CreateStdFileStream( const char * szPath )
{
return new StdFileStream(szPath);
}

为方便扩展利用内存池再提供一个内存管理接口

1
2
3
4
5
6
7
8
9
void * AllocateMem( size_t size_)
{
return malloc (size_);
}
void RecycleMem( void * p)
{
free (p);
}

解压流的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
struct InflateStream
{
z_stream        m_stream;
IIStream*       m_origStream;
int m_last_inflate_;
unsigned char *      m_indeflateBuff;
unsigned char *      m_origBuff;
InflateStream( const void * _stream_in)
{
m_origStream = CreateStdFileStream(( const char *)_stream_in);
m_indeflateBuff = (unsigned char *)AllocateMem(CHUNK_SIZE);
m_origBuff = (unsigned char *)AllocateMem(CHUNK_SIZE);
// 初始化z_stream结构体
memset (&m_stream,0, sizeof (m_stream));
inflateInit(&m_stream);
m_last_inflate_ = Z_OK;
}
~InflateStream()
{
m_origStream->Release();
RecycleMem(m_indeflateBuff);
RecycleMem(m_origBuff);
}
size_t Inflate__(unsigned char * _buff_out, size_t _block_size)
{
m_stream.next_out = _buff_out;
m_stream.avail_out = _block_size;
m_last_inflate_ = inflate(&m_stream,Z_NO_FLUSH);
/*
#define Z_OK            0
#define Z_STREAM_END    1
#define Z_NEED_DICT     2
#define Z_ERRNO        (-1)
#define Z_STREAM_ERROR (-2)
#define Z_DATA_ERROR   (-3)
#define Z_MEM_ERROR    (-4)
#define Z_BUF_ERROR    (-5)
#define Z_VERSION_ERROR (-6)
*/
if (m_last_inflate_ > Z_STREAM_END)
{
return 0;
}
return _block_size - m_stream.avail_out;
}
size_t Inflate( char * buff_out, size_t size_)
{
int inflate_bytes = 0;
static int i = 0;
do
{
int orig_bytes = 0;
if (!m_stream.avail_in)
{
orig_bytes = m_origStream->Read(CHUNK_SIZE,m_origBuff);
m_stream.avail_in = CHUNK_SIZE;
m_stream.next_in = m_origBuff;
}
do
{
i++;
int bytes_read = 0;
memset (m_indeflateBuff,0,CHUNK_SIZE);
bytes_read = Inflate__(m_indeflateBuff,size_ - inflate_bytes);
assert (bytes_read);
memcpy (buff_out+inflate_bytes,m_indeflateBuff,bytes_read);
inflate_bytes += bytes_read;
if (inflate_bytes >=size_)
{
return size_;
}
else  if(m_last_inflate_ == Z_STREAM_END)
{
return inflate_bytes;
}
} while (m_stream.avail_in);
} while (m_last_inflate_ != Z_STREAM_END);
}
bool Eof()
{
if ( this ->m_last_inflate_ == Z_STREAM_END)
{
return true ;
}
else
{
return false ;
}
}
};

大概就是这么一回事,IIStream*       m_origStream;其实有那么点像原始流delegate这个改成代理模式应该会更好,其实现在就是那么个代理模式的意思。

作者:码农日记
Android, iOS, HTML5, Cloud and more
原文地址:zlib流式解压, 感谢原作者分享。

发表评论