zlib流式解压

By | 2014/12/15

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 free task manager.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这个改成代理模式应该会更好,其实现在就是那么个代理模式的意思。

发表评论

电子邮件地址不会被公开。 必填项已用*标注