zlib流式解壓

zlib實現解壓的例子官方已經給出

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

最常見的解壓方式就是現成從堆分配出適合大小的內存,直接向這個內存裏解壓,這樣是不錯的,一些情況下這樣是非常適合的,但是如果文件很大,需要實現一個流式解壓的功能,比如文件非常大,需要向文件系統裏寫文件。

實現的效果如下:

    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);

爲了方便擴展,定義一個解壓前的數據流式讀取接口

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讀取實現一個流式讀取接口

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);
}

爲方便擴展利用內存池再提供一個內存管理接口

void * AllocateMem(size_t size_)
{
    return malloc(size_);
}

void RecycleMem(void * p)
{
    free(p);
}

解壓流的實現

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
                {
                    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這個改成代理模式應該會更好,其實現在就是那麼個代理模式的意思。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章