zlib學習小結

來源:互聯網
上載者:User

標籤:

官方手冊 : 

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

參考串連 :

http://blog.csdn.net/zhoudaxia/article/details/8039519

http://www.oschina.net/code/snippet_65636_22542

http://blog.163.com/bh_binghu/blog/static/94553512011626102054571/

請多多支援以上作者...

另外,推薦下載源碼同步學習.

研究了好久的zlib,今天終於可以拿得出手了.這裡分享一下使用的經驗.

基礎資料結構

z_stream : 壓縮演算法,壓縮程度以及輸入輸出buffer和長度等都儲存在這裡,可以理解為壓縮上下文.

常用的函數

deflateInit : 參數比較少,裡面的實現其實是調用的deflateInit2

deflateInit2 : 壓縮初始化的基礎函數,有很多參數,下面會重點介紹

deflate : 壓縮函數.

deflateEnd : 壓縮完成以後,釋放空間,但是注意,僅僅是釋放deflateInit中申請的空間,自己申請的空間還是需要自己釋放.

inflateInit : 解壓初始化函數,內部調用的inflateInit2.

inflateInit2 : 解壓初始化的基礎函數.後面重點介紹.

infalte : 解壓函數

inflateEnd : 同deflateEnd作用類似.

compress : 全部附加選項預設壓縮,內部調用compress2

compress2 : 帶level的壓縮方式.

uncompress : 解壓縮.

壓縮函數介紹

deflateInit2 :

函數原型為 : int ZEXPORT deflateInit2(z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy)

z_stream : 這個是壓縮上下文,你在調用deflateInit2之前首先要做的就是初始化這個結構體,很簡單,個裡面的三個回調賦值就行了.連官方給出的例子也是置成NULL.所以這裡直接這樣初始化,strm.zalloc = NULL; strm.zfree = NULL; strm.opaque = NULL.

level : 這個參數目前可選值有四個(好吧,在我看來,有兩個沒啥用嘛),如下:

Z_NO_COMPRESSION : 不壓縮

Z_BEST_SPEED : 速度優先,可以理解為最低限度的壓縮.

Z_BEST_COMPRESSION : 壓縮優先,但是速度會有些慢.
Z_DEFAULT_COMPRESSION : 沒仔細看源碼.compress裡面用的就是這個選項.

method : 就壓縮嘛,所以選項就只有Z_DEFLATED

windowBits : 這個選項可就有點複雜了,也很重要,取值與壓縮方式如下:

  * -(15 ~ 8) : 純deflate壓縮
  * 15 ~ 8 : 帶zlib頭和尾
  * > 16 : 帶gzip頭和尾

所以,無論是zlib的壓縮還是gzip的壓縮,再或者是純deflate流,zlib都能搞定.驗證方式 : 

1. zlib的話可以使用命令 cat 壓縮檔名 | zlib-deflate -uncompress驗證(家裡的電腦沒有這個命令,忘記什麼包裡面的了.)

2. 如果是純deflate的壓縮資料,只能位元組寫解壓的代碼.注意調用inflateInit2的時候這個值要對應.

3. 如果是gzip的方式,就直接用gunzip驗證.

memLevel : 目前只有一個選項,MAX_MEM_LEVEL,無非是運行過程中對記憶體使用量的限制.

strategy : 直接給預設就行Z_DEFAULT_STRATEGY.

deflate :

函數原型 : ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));

strm : 這個參數就是上面調用了deflateInit2初始化過後的變數.調用deflate之前,需要為它指定四個成員.

strm.next_in = 你的待壓縮資料

strm.next_out = 壓縮以後資料存放區的buffer

strm.avail_in = 待壓縮資料的長度

strm.avail_out = 壓縮資料存放區buffer的長度.

這個next_out 沒有固定的長度,並不是說因為是壓縮,壓縮以後的資料一定比原來的短,所以就可以少給,一般建議起碼給到next_in的長度.

flush : 沒有用過別的選項,

Z_NO_FLUSH : 如果是一個大檔案,沒有辦法一次載入進記憶體,那麼這個選項就派上用場了.它告訴zlib我還會再給你資料的.

這個參數對應的deflate的返回值是Z_OK.

Z_FINISH : 這個標記才是告訴zlib,我沒有資料了,這次是最後一個.

這個參數對應的deflate的返回值是Z_STREAM_END

deflateEnd : 

上面的描述已經足夠清楚,它沒有更多的作用了.

compress2 : 

函數原型 : ZEXTERN int ZEXPORT compress2 OF((Bytef *dest,   uLongf *destLen, const Bytef *source, uLong sourceLen, int level));

如果你看了它的內部實現,就會知道,這其實就是上面三個函數的封裝.供使用者偷懶用的,誰不喜歡一步到位呢.不過要明確它的缺點 :

a) 它的確很方便,但是卻限制了所有的可能性,因為它只能壓縮一次,也就是說內部直接調用的Z_FINISH

b) 不要覺得儲存的是壓縮的資料就給小buffer,有一篇部落格給出的值是輸入buffer的四倍.這個虧我是吃過的,前幾天用這個函數,給了輸入buffer的大小,我覺得足夠了.但是始終返回Z_BUF_ERROR.後來看到那篇部落格才恍然大悟.

解壓函數介紹有壓縮自然也得有解壓,並且都是一一對應的.

inflateInit2 : 初始化

函數原型 : inflateInit2(z_streamp strm, int windowBits)

strm : 和deflate一樣,初始化三個回調以後即可.有的參考文檔說還需要初始化第四個選項,具體記不清哪個了.不過我試過以後發現貌似不用.

windownBits : 含義和deflateInit2一樣,而且一定要對應起來.

inflate : 解壓

函數原型 : ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));

strm : 四個參數.

strm.next_in = 你的待解壓資料

strm.next_out = 解壓以後資料存放區的buffer

strm.avail_in = 待解壓資料的長度

strm.avail_out = 解壓資料存放區buffer的長度.

flush : 和deflate一樣,如果是Z_NO_FLUSH說明還有資料沒有解壓,如果是Z_FINISH說明這是最後一包待解壓資料.

inflateEnd : 釋放上面兩步驟裡面申請的資源.

uncompress :

函數原型 : ZEXTERN int ZEXPORT uncompress OF((Bytef *dest,   uLongf *destLen, const Bytef *source, uLong sourceLen));

dest : 解壓以後的資料存在這裡

destLen : dest 大小

source : 待解壓資料buffer

sourceLen : 待解壓資料長度

其實這個函數就是簡單封裝了inflateInit, inflate, inflateEnd.同樣,這個函數只適合單獨解壓情境,不適合需要多次傳入的情境.

關於返回值

現在看來,除了deflate和inflate成功會有兩個返回值以外,其他的函數返回值成功的話都是Z_OK.

如果 flush == Z_NO_FLUSH, 這兩個函數返回值是Z_OK

如果flush == Z_FINISH, 這兩個函數的返回值是Z_STREAM_END

一定要區分.

一個簡單的例子這個例子太像官方提供的例子了.不過還是貼出來,加了幾句注釋,希望能夠協助理解.

#include <stdio.h>#include <stdlib.h>#include <stdint.h>#include <sys/stat.h>#include <fcntl.h>#include <zlib.h>#include <time.h>#include <errno.h>#include <assert.h>#define READ_LEN    8192// gzip頭#define GZIP_MAGIC     "\037\213" /* Magic header for gzip files, 1F 8B */// 目前只有這種壓縮方式#define DEFLATED    8// 標記壓縮為快速還是高品質#define FAST 4#define SLOW 2// gzip格式// (u16)MAGIC + (u8)DEFLATE + (u8)flag + (u32)time + (u8)deflate_flag + (u8)OS_CODEint main(int argc, char **argv){    if (argc < 3)    {        printf("usage : %s src_file dst_file\n", argv[1]);        return -1;    }    int ret = 0;    int rfd = 0;    uint64_t read_len = 0;    char *read_buf = NULL;    int wfd = 0;    uint64_t write_len = 0;    char *write_buf = NULL;    rfd = open(argv[1], O_RDONLY);    if (-1 == rfd)    {        perror("\n");        return -1;    }    wfd = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0666);    if (wfd < 0)    {        perror("\n");        return -1;    }    read_buf = (char *)malloc(READ_LEN);    if (NULL == read_buf)    {        return -1;    }    write_len = READ_LEN * 4;    write_buf = (char *)malloc(write_len);    if (NULL == write_buf)    {        return -1;    }    uint8_t *ptr = (uint8_t*)write_buf;    z_stream stream;    stream.zalloc = NULL;    stream.zfree = NULL;    stream.opaque = NULL;    deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + 16, 8, Z_DEFAULT_STRATEGY);AGAIN:    while ((read_len = read(rfd, read_buf, READ_LEN)) >= 0)    {        int flush = Z_NO_FLUSH;        if (read_len == 0)        {            flush = Z_FINISH;        }        stream.next_in = (uint8_t*)read_buf;        stream.avail_in = read_len;        do        {            write_len = 4 * READ_LEN;            stream.next_out = (uint8_t*)write_buf;            stream.avail_out = write_len;            ret = deflate(&stream, flush);            if (flush == Z_NO_FLUSH && ret != Z_OK)            {                goto ERROR;            }            if (flush == Z_FINISH && ret != Z_STREAM_END)            {                goto ERROR;            }            int avail = 4 * READ_LEN - stream.avail_out;            write(wfd, write_buf, avail);        }while (stream.avail_out == 0); // 如果avail_out不夠用了,說明輸入的buffer還有沒有壓縮的部分,那麼要繼續壓縮.        // 如果輸入長度不是0,說明壓縮不完整,可以直接退出.        assert(stream.avail_in == 0);        if (read_len == 0)        {            break;        }    }    if (read_len < 0)    {        if (errno == EINTR)        {            goto AGAIN;        }    }    return 0;ERROR:    deflateEnd(&stream);    close(rfd);    close(wfd);    return -1;}
這段代碼,我使用的是gzip的壓縮方式,所以如果要解壓,不需要在單獨寫一份代碼了.只要gunzip命令就可以驗證程式是否正常.


後記 : 

關於三種壓縮格式的區分.

deflate : 說他是壓縮格式有點不確切,其實它就是壓縮的核心演算法.

zlib : zlib header + deflate + zlib tailer

gzip : gzip header + deflate + gzip tailer


另外我封裝了一套單buffer的壓縮和解壓函數.也放在這裡,有需要的同學可以參考一下.

compress.h

#ifndef MYZIP_H#include <unistd.h>#include <stdint.h>#include <zlib.h>#ifdef __cplusplusextern "C" {#endiftypedef z_streamp PZSTREAM;typedef z_stream  ZSTREAM;/* =========================================================================== * 指定壓縮(解壓)類型 * 官方文檔提到 : *      -(15 ~ 8) : raw deflate壓縮 *      15 ~ 8 : 帶zlib頭和尾 *      > 16 : 帶gzip頭和尾 */typedef enum{    E_ZIP_RAW = -MAX_WBITS,    E_ZIP_ZLIB = MAX_WBITS,    E_ZIP_GZIP = MAX_WBITS + 16} zip_type;// 壓縮回呼函數typedef void (*flate_cb)(void *buf, int len);/****************************************************************************** ** 執行deflate壓縮 ** NOTE : 1. 這組函數僅僅適用於小buffer           2. 在調用deflate_beg之前,需要自己初始化strm           3. 如果deflating失敗,要執行deflate_end釋放資源           4. 明確一點,deflate_end只做了stream內部資源釋放工作,如果stream是動態分配的,需要自己free******************************************************************************/int deflate_beg(PZSTREAM strm, zip_type type);int deflating(PZSTREAM strm, uint8_t *inbuf, uint32_t in_len, uint8_t *outbuf, uint32_t *out_len);int deflate_end(PZSTREAM strm);/****************************************************************************** ** 執行inflate解壓 ** NOTE : 1. 這組函數僅僅適用於小buffer           2. 在調用inflate_beg之前,需要自己初始化strm           3. 如果inflating失敗,要執行inflate_end釋放資源           4. 明確一點,inflate_end只做了stream內部資源釋放工作,如果stream是動態分配的,需要自己free******************************************************************************/int inflate_beg(PZSTREAM strm, zip_type type);int inflating(PZSTREAM strm, uint8_t *inbuf, uint32_t in_len, uint8_t *outbuf, uint32_t *outlen);int inflate_end(PZSTREAM strm);#ifdef __cplusplus}#endif#endif

compress.c

#include <unistd.h>#include "compress.h"/// strm需要在傳入之前自己初始化int deflate_beg(PZSTREAM strm, zip_type type){    int ret = 0;    int window_bits;    if (NULL == strm)    {        goto ERROR;    }    strm->zalloc = Z_NULL;    strm->zfree = Z_NULL;    strm->opaque = Z_NULL;    strm->next_in = Z_NULL;    strm->avail_in = 0;    window_bits = (int)type;    ret = deflateInit2(strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, window_bits, 8, Z_DEFAULT_STRATEGY);    if (ret != Z_OK)    {        goto ERROR;    }    return 0;ERROR:    return -1;}int deflating(PZSTREAM strm, uint8_t *inbuf, uint32_t in_len, uint8_t *outbuf, uint32_t *out_len){    int ret = 0;    if (NULL == strm || NULL == inbuf || NULL == outbuf || NULL == out_len)    {        goto ERROR;    }    ret = deflateReset(strm);    if (ret != Z_OK)    {        goto ERROR;    }    strm->next_in = inbuf;    strm->avail_in = in_len;    strm->next_out = outbuf;    strm->avail_out = *out_len;    // 此函數的應用情境都是小buffer情境,所以這裡inbuf的傳入都是完整需要壓縮的,    // 所以這裡直接調用Z_FINISH    ret = deflate(strm, Z_FINISH);    if (ret != Z_STREAM_END)    {        goto ERROR;    }    *out_len = strm->total_out;    return 0;ERROR:    return -1;}int deflate_end(PZSTREAM strm){    int ret = 0;    ret = deflateEnd(strm);    if (ret != Z_OK)    {        goto ERROR;    }    return 0;ERROR:    return -1;}int inflate_beg(PZSTREAM strm, zip_type type){    int ret = 0;    int window_bits = 0;    if (NULL == strm)    {        goto ERROR;    }    strm->zalloc = (alloc_func)0;    strm->zfree = (free_func)0;    strm->opaque = (voidpf)0;    window_bits = (int)type;    ret = inflateInit2(strm, window_bits);    if (ret != Z_OK)    {        goto ERROR;    }    return 0;ERROR:    return -1;}int inflating(PZSTREAM strm, uint8_t *inbuf, uint32_t in_len, uint8_t *outbuf, uint32_t *outlen){    int ret = 0;    if (NULL == strm || NULL == inbuf || NULL == outbuf || NULL == outlen)    {        goto ERROR;    }    ret = inflateReset(strm);    if (ret != Z_OK)    {        goto ERROR;    }    strm->next_in = inbuf;    strm->avail_in = in_len;    strm->next_out = outbuf;    strm->avail_out = *outlen;    ret = inflate(strm, Z_FINISH);    if (Z_STREAM_END != ret)    {        goto ERROR;    }    *outlen = strm->total_out;    return 0;ERROR:    return -1;}int inflate_end(PZSTREAM strm){    int ret = 0;    ret = inflateEnd(strm);    if (ret != Z_OK)    {        goto ERROR;    }    return 0;ERROR:    return -1;}

zlib學習小結

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.