zlibを使うには、まずヘッダファイルをインクルードしなければなりません。zlibで使われるヘッダファイルはzlib.hと、zconf.hです。この二つをインクルードファイルを検索するときのパスで見つかるように、適当な場所にコピーします。UNIX系だと、makeしたときにコピーされているはずです。
実際にインクルードするファイルはzlib.hだけです。
圧縮を行うには、z_stream構造体を作るところから始めなければなりません。zlibは圧縮や解凍をストリームを操作するかのように扱います。そのため、ファイルから順次読み込みながら圧縮したり、解凍したりすることができます。
z_stream構造体ですが、とりあえずは次のように設定しておけば大丈夫です。
z_stream z;
z.zalloc = Z_NULL;
z.zfree = Z_NULL;
z.opaque = Z_NULL;
これらには、メモリの割り当てで使用する関数を指定することができます。しかし、普通はデフォルトの関数を使えばよいのでZ_NULLを設定しておきます。
z.next_in = (圧縮するデータへのポインタ);
z.avail_in = (圧縮するデータの長さ);
z.next_out = (圧縮されたデータを格納する場所へのポインタ);
z.avail_out = (格納場所のサイズ);
deflateInit( &z, Z_DEFAULT_COMPRESSION );
z.avail_inとz.avail_outは、現時点で有効なバッファの大きさです。たとえば、圧縮するデータが10000バイトあり、メモリ上に読み込まれている圧縮データのサイズが5000バイトだったならば、z.avail_inには5000を設定します。同様に、z.avail_outには格納場所として確保されたバッファのサイズを指定します。
初期化が完了したら、いよいよ圧縮処理本体です。
圧縮の基本的な流れを説明しますが、入力部と出力部に分けて説明します。それぞれ、互いに依存はしますが干渉せずに動作します。入力部には圧縮するデータを流し込んでゆき、現在メモリ上にあるデータが圧縮されたら、残るデータを流し込み……ということを繰り返していきます。出力部では、格納バッファがいっぱいになったらそれをディスクなどに書き込みバッファを空けるということを繰り返します。
圧縮はdeflate()関数で行います。
deflate( &z, Z_NO_FLUSH );
ここがややこしいのですが、deflate()関数の返す値はひとつだけではありません。Z_OKとZ_STREAM_ENDの二つの値が返されます。Z_OKは圧縮処理が行われたことを示しますが、すべてのデータを処理したとは限りません。Z_STREAM_ENDは圧縮処理が完全に終了したことを示します。
これ以外はエラーだと考えてよいでしょう。
deflate()関数の第二引数も重要です。ここではZ_NO_FLUSHが指定されていますが、このままでは圧縮は完了しません。圧縮を終わらせるためには、最後の圧縮でZ_FINISHを指定しなければならないのです。Z_FINISHを指定することで、保留されていた入出力が処理され、すべてのデータに対する入出力が完了するのです。
このあたりは、コードを見た方が早いかもしれませんね。
#include <zlib.h>
#include <stdio.h>
#include <string.h>
FILE * input_fp;
FILE * output_fp;
Bytef * input_image;
uInt input_size;
Bytef output_buffer[ 1024 ];
void compress( void )
{
z_stream z;
int rcv;
z.zalloc = Z_NULL;
z.zfree = Z_NULL;
z.opaque = Z_NULL;
z.next_in = input_image;
z.avail_in = input_size;
z.next_out = output_buffer;
z.avail_out = 1024;
if( deflateInit( &z, Z_DEFAULT_COMPRESSION ) != Z_OK )
{
printf( "error: %s\n", z.msg );
return;
}
while( z.avail_in > 0 )
{
rcv = deflate( &z, Z_NO_FLUSH );
printf( "a:avail_in = %d, total_in = %d, avail_out = %d, total_out = %d.\n", z.avail_in, z.total_in, z.avail_out, z.total_out );
if( rcv != Z_OK )
{
printf( "error: %s\n", z.msg );
deflateEnd( &z );
return;
}
if( z.avail_out == 0 )
{
fwrite( output_buffer, sizeof(Bytef), 1024, output_fp );
z.next_out = output_buffer;
z.avail_out = 1024;
}
}
do
{
rcv = deflate( &z, Z_FINISH );
printf( "b:avail_in = %d, total_in = %d, avail_out = %d, total_out = %d.\n", z.avail_in, z.total_in, z.avail_out, z.total_out );
if( rcv == Z_OK )
fwrite( output_buffer, sizeof(Bytef), 1024-z.avail_out, output_fp );
else if( rcv != Z_STREAM_END )
{
printf( "error: %s\n", z.msg );
deflateEnd( &z );
return;
}
}
while( rcv != Z_STREAM_END );
fwrite( output_buffer, sizeof(Bytef), 1024-z.avail_out, output_fp );
deflateEnd( &z );
return;
}
void main( int argc, char * argv[] )
{
if( argc != 3 )
{
puts( "usage: compress [input] [output]" );
return;
}
input_fp = fopen( argv[1], "rb+" );
if( !input_fp )
{
printf( "cannot open file %s.\n", argv[1] );
return;
}
output_fp = fopen( argv[2], "wb" );
if( !output_fp )
{
printf( "cannot create file %s.\n", argv[2] );
fclose( input_fp );
return;
}
fseek( input_fp, 0, SEEK_END );
input_size = ftell( input_fp );
input_image = new Bytef[ input_size ];
fseek( input_fp, 0, SEEK_SET );
fread( input_image, sizeof(Bytef), input_size, input_fp );
compress();
delete[] input_image;
fclose( input_fp );
fclose( output_fp );
}
汚いソースですが、とりあえずはこれで圧縮処理を行うことができます。解凍処理も同じようなものです。deflateをinflateに変更するだけです。ただ、inflateInitでは圧縮レベルを指定する必要がないというところが異なります。
圧縮操作が完了したら、zlibが使用したメモリを解放するためにdeflateEnd()関数を呼び出さなければなりません。
リンクに関する補足なのですが、zlibを使うプログラムはきちんとzlib.libをリンクするように指定しなければなりません。リンクできない場合は、makeがうまくいっているか、zlib.libがライブラリ検索パス内にあるかどうか、といったところをチェックしてみて下さい。
zlib.hの和訳されたものがあればよいのですけれど……。
頑張って訳してみようかな?