zlib 入門

公式サイトは http://zlib.net/ になりました。 以下の記述はかなり古いものを急いで書き直したものです。 おかしなところがありましたらご教示ください。

zlib とは

zlib は Zip や gzip に使われている圧縮アルゴリズムをライブラリ化したものです。 作者は Zip や gzip 同様 Jean-loup Gailly と Mark Adler です。 ライセンスは BSD 類似です(詳しくは zlib 配布に含まれる zlib.h のコメントをご覧ください)[標準では使用されませんが,アセンブラ版 match.S はGPLです]。 画像フォーマット PNG の圧縮も zlib で行われています。

Java(JDK 1.1 以降)には zlib に相当するものが組み込まれています。 java.util.zip のドキュメントをご覧ください。

zlib の圧縮アルゴリズムは PKZIP 2.x の deflate アルゴリズムで, もともとは私と吉崎栄泰さんが LHA のために開発したものをハッシュで高速化したものです(zlib のソースコード中に,ほんのちょっとですが私の名前が入っています)。 その後,LHA もハッシュを使うようになりましたので,LHA と gzip/zlib/Zip の実質的な相違はなくなりました。

インストール

UNIX 系 OS 上にはすでに /usr/lib/ あたりに libz.a(スタティック),libz.so または libz.dylib(ダイナミック)といった名前で入っているはずです。 また,このライブラリを使うためのヘッダファイル zlib.h とその下請けの zconf.h/usr/include あたりにあるはずです。 ソースから作りたい場合は次のようにします。

tar xvzf zlib-1.2.2.tar.gz
cd zlib-1.2.2
./configure --shared --prefix=/usr
make
make test
make install

使い方(基本関数)

ここ あるいはソース中の old/zlib.html に zlib 1.1.4 のマニュアルがあります。 最新版のドキュメントは zlib.h のコメント中にあります。

標準的な使い方を解説しておきます(zlib 1.1.x,1.2.x 共通)。

zlib を使うプログラムは,頭で

#include <zlib.h>

とします。 そして,まず z_stream という型の構造体を作ります。 名前は何でもいいのですが,仮に z としておきましょう。

z_stream z;

標準的な圧縮の仕方は,まず三つの変数を

    z.zalloc = Z_NULL;
    z.zfree = Z_NULL;
    z.opaque = Z_NULL;

のようにセットしてから,初期化ルーチン deflateInit(&z, Z_DEFAULT_COMPRESSION) を呼び出します。 この戻り値が Z_OK でなければエラーです。 zlib のエラーメッセージはすべて z.msg を先頭番地とする領域に '\0' で終わる文字列として書き込まれます。

次に,

    z.next_in = 入力ポインタ;
    z.avail_in = 入力データの残量;
    z.next_out = 出力ポインタ;
    z.avail_out = 出力バッファの残量;

のようにセットして,deflate(&z, Z_NO_FLUSH) を呼び出します。 戻り値が Z_OK でなければエラーです。 deflate(&z, Z_NO_FLUSH) は入力を消費して出力を生産し,ポインタ z.next_inz.next_out を進め,残量 z.avail_inz.avail_out を減らします。 z.avail_in がゼロになれば,次の入力データを準備して,その先頭アドレスを z.avail_in,データ量を z.avail_in にセットします。 同様に,z.avail_out がゼロになれば,ファイルに書き出すなどして,z.next_outz.avail_out を更新します。

最後の入力になったら,deflate(&z, Z_FINISH) として呼び出します。 このときは,戻り値が Z_OK であればまだやり残しがあるという意味ですので,出力バッファをファイルに書き出すなどして余裕を作り,再度 deflate(&z, Z_FINISH) を呼び出します。 これを戻り値が Z_STREAM_END になるまで続けます。

最後に後始末のため deflateEnd(&z) を呼び出し,使ったメモリを開放します。 やはり戻り値が Z_OK でなければエラーです。

圧縮したものの復号(展開)もほぼ同様ですが,関数の名前が deflate... から inflate... に変わります。 引数の与え方などは圧縮の場合より若干簡単になります。

圧縮と展開の簡単な例として,comptest.c という簡単な圧縮プログラムの例を作っておきましたので,ご覧ください。

zlib を使ったプログラムのコンパイル(正確にはリンク)は,たとえば

gcc comptest.c -o comptest -lz

のように -lz オプションを付けて行います。 これは zlib ライブラリ libz.a をリンクするという意味です。

出来上がったサンプルプログラム comptest は,comptest c 入力ファイル名 出力ファイル名 で圧縮し,comptest d 入力ファイル名 出力ファイル名 で展開します。

使い方(高次関数)

以下も zlib 1.1.x,1.2.x 共通です。

入力ファイルを例えば1Kバイトごとに区切って圧縮し,圧縮ファイルのランダムアクセスを可能にする方法です。 サンプルプログラム comptest2.c を見ながらお読みください。

圧縮ファイルのフォーマットは,圧縮ブロックのサイズと圧縮ブロックを任意個並べたものとします。 ファイルの最後を明示的に示すため,最後の圧縮ブロックのサイズを 0 にしておくことにします。

+----+----+----+----+-...-+-+
|size|data|size|data| ... |0|
+----+----+----+----+-...-+-+

まず zlib.h を読み込んでおきます。

#include <zlib.h>

次に,元データと圧縮データ用のバッファを確保します。 「圧縮」するとサイズが増えることがあるので,圧縮バッファサイズは元バッファサイズの少なくとも0.1%増しとし,さらに管理領域用に12バイトを加えた値とします。 余裕を見て大きめに取っておけばいいでしょう。

#define ORIGBUFSIZ  1024        /* 元データバッファサイズ(任意) */
#define COMPBUFSIZ  1040        /* 圧縮データバッファサイズ(≧上×1.001+12) */
unsigned char origbuf[ORIGBUFSIZ]; /* 元データバッファ */
unsigned char compbuf[COMPBUFSIZ]; /* 圧縮データバッファ */

元データと圧縮データのサイズ(バイト数)を入れる変数を用意しておきます。

unsigned long origsize, compsize;  /* 元サイズ,圧縮サイズ */

これで,origbuf に origsize 分のデータを入れ,

compsize = COMPBUFSIZ;
compress(compbuf, &compsize, origbuf, origsize);

とすれば,compbuf に compsize 分の圧縮データが生成されますので,compsize の値と,それに続いて compbuf の内容をファイルに出力します。

圧縮をもっと良くするには,compress() の代わりに,次のように compress2() を呼び出します。

compress2(compbuf, &compsize, origbuf, origsize, 9);

最後の数値 9 は,1〜9 までの値で,数が大きいほど圧縮は良くなりますが時間がかかります。

こうして出力した圧縮データを元に戻す(復号する)には,

origsize = ORIGBUFSIZ;
uncompress(origbuf, &origsize, compbuf, compsize);

とします。 origsize に元のサイズが入ります。

実際には compress(),compress2(),uncompress() の戻り値を調べてエラーがないことを確認しなければなりません。 戻り値は次のいずれかです。


奥村晴彦

Last modified: 2007-02-14 08:49:05