Snappy の使い方

矢田 晋

Abstract: Snappy は高速な圧縮アルゴリズムのライブラリです.zlib や libbzip2 などの有名な圧縮ライブラリと比べると,圧縮率は低いものの,圧縮・伸長にかかる時間を桁一つ短縮することができます.圧縮・伸長速度が HDD の転送速度を上回るため,ディスク使用量や通信量の削減だけでなく,I/O の高速化を目的として利用することもできます.本記事は,C/C++ から Snappy を利用する方法の解説になっています.

はじめに

最新のドキュメントは Subversion のリポジトリ,および公式サイトからダウンロードできるソースコードに含まれています.また,Snappy のヘッダである snappy.hsnappy-c.h には,C/C++ API の説明がコメントとして記述されています.

Snappy のインタフェースは zlib や libbzip2 と比べて単純です.圧縮・伸長に必要な領域の大きさを求める関数と,圧縮・伸長をおこなう関数しかありません.あまりにも単純なためか,わざわざ Snappy の使い方を解説するサイトは見当たりません.英語アレルギーを患っている方でなければ,snappy.hsnappy-c.h を見るだけで,簡単に使えるようになると思います.

以下,Snappy のインストール方法を紹介した後,C 言語における圧縮・伸長C++ における圧縮・伸長について説明します.また,Snappy を用いて圧縮・伸長をおこなうプログラムのサンプルコードを用意しました.

インストール

Debian 系の環境における開発用パッケージのインストール

$ sudo apt-get install libsnappy-dev
$ sudo aptitude install libsnappy-dev

Debian 系の環境であれば,apt-get もしくは aptitude を使って簡単にインストールできます.インストールには root 権限が必要なので,必要に応じて sudosu を使ってください.

Red Hat 系の環境における開発用パッケージのインストール

$ sudo yum install snappy-devel

Red Hat や CentOS であれば,yum を使って簡単にインストールできます.こちらも必要に応じて sudosu を使ってください.

ソースコードからのインストール

$ wget http://snappy.googlecode.com/files/snappy-1.0.5.tar.gz
$ tar zxf snappy-1.0.5.tar.gz
$ cd snappy-1.0.5
$ ./configure CPPFLAGS=-DNDEBUG
$ make
$ make check
$ sudo make install

ソースコードの tarball は公式サイトからダウンロードできます.ブラウザや wget により tarball をダウンロードした後は,configure, make, make check, make install という一般的な手順でインストールします.Snappy を軽く試してみるのであれば,make check まで終わった時点で,snappy-c.h, libsnappy.a のみをコピーして使うことも可能です.

デバッグを目的とするのでなければ,configure に対して CPPFLAGS=-DNDEBUG というオプションを渡すことで assert() を無効にしてください.assert() が有効な状態では圧縮時間が長くなってしまいます.

インストールの確認

// test.c
#include <snappy-c.h>
int main(void) {
  snappy_max_compressed_length(100);
  return 0;
}
$ gcc -Wall test.c -lsnappy

簡単なソースコードをコンパイル・リンクしてみれば,Snappy が問題なくインストールされているかどうか分かります.リンクにおいては,Snappy を指定するオプション(-lsnappy)が必要になります.

サンプルは C 言語のコードになっていますが,Snappy が C++ により実装されているため,環境によっては -lstdc++ というオプションが必要になるかもしれません.

C 言語における圧縮・伸長

圧縮

#include <snappy-c.h>

Snappy による圧縮の手順は,snappy_max_compressed_length() により得られるサイズの領域を出力バッファとして確保した後で snappy_compress() を呼び出すだけです.圧縮前後のデータをすべてメモリ上に展開するため,入力データが大きいときやメモリの使用量を節約したいときは,入力データを分割して圧縮するなどの工夫が必要となります.

size_t snappy_max_compressed_length(size_t source_length);

snappy_max_compressed_length() の第 1 引数である source_length には入力データのサイズを指定するようになっています.出力バッファに割り当てるべき領域のサイズが戻り値になります.

snappy_status snappy_compress(const char *input,
                              size_t input_length,
                              char *compressed,
                              size_t *compressed_length);

snappy_compress() の第 1 引数である input と第 2 引数である input_length には入力データの開始アドレスとサイズを指定するようになっています.第 3 引数の compressed に出力バッファの開始アドレスを指定するのは入力と同様ですが,第 4 引数の compressed_length には出力バッファのサイズを格納した変数へのポインタを渡すようになっています.圧縮が成功したときは,compressed_length の指す変数が圧縮後のサイズによって上書きされます.snappy_compress() の戻り値は,圧縮に成功したときは SNAPPY_OK となります.compressed_length の指す値が snappy_max_compressed_length() の返す値より小さければ圧縮に失敗し,戻り値は SNAPPY_BUFFER_TOO_SMALL となります.

伸長

#include <snappy-c.h>

Snappy による伸長の手順は,snappy_uncompressed_length() により得られるサイズの領域を出力バッファとして確保した後で snappy_uncompress() を呼び出すだけです.圧縮データのサイズが要求されるため,圧縮の段階でサイズを保存しておく必要があります.

snappy_status snappy_uncompressed_length(const char *compressed,
                                         size_t compressed_length,
                                         size_t *result);

snappy_uncompressed_length() の第 1 引数である compressed と第 2 引数である compressed_length には圧縮データの開始アドレスとサイズを指定するようになっています.第 3 引数である result には圧縮前のサイズを受け取る変数へのポインタを指定するようになっています.サイズの取得に成功すれば SNAPPY_OK が戻り値になります.入力が正しい圧縮データでなければ SNAPPY_INVALID_INPUT が戻り値になります.

snappy_status snappy_uncompress(const char *compressed,
                                size_t compressed_length,
                                char *uncompressed,
                                size_t *uncompressed_length);

snappy_uncompress() の第 1 引数である compressed と第 2 引数である compressed_length には圧縮データの開始アドレスとサイズを指定するようになっています.第 3 引数の uncompressed に出力バッファの開始アドレスを指定するのは入力と同様ですが,第 4 引数の uncompressed_length には出力バッファのサイズを格納した変数へのポインタを渡すようになっています.圧縮が成功したときは,uncompressed_length の指す変数が圧縮前のサイズによって上書きされます.snappy_uncompress() の戻り値は,伸長に成功したときは SNAPPY_OK となります.uncompressed_length の指す値が snappy_uncompressed_length() の返す値より小さければ伸長に失敗し,戻り値は SNAPPY_BUFFER_TOO_SMALL となります.入力が正しい圧縮データでなければ SNAPPY_INVALID_INPUT が戻り値になります.

snappy_status snappy_validate_compressed_buffer(const char *compressed,
                                                size_t compressed_length);

snappy_validate_compressed_buffer() を使うことにより,圧縮データの正当性を高速に判定できます.実際に伸長する方法と比べると,通常 4 倍以上高速です.第 1 引数である compressed と第 2 引数である compressed_length には圧縮データの開始アドレスとサイズを指定するようになっています.正当であると判定されたときは SNAPPY_OK が戻り値になり,正当でないと判定されたときは SNAPPY_INVALID_INPUT が戻り値になります.

C++ における圧縮・伸長

圧縮

#include <snappy.h>

C++ における圧縮の手順は,snappy::Compress() を呼び出すだけです.C 言語と同様に snappy::MaxCompressedLength() により得られるサイズの領域を出力バッファとして確保した後で snappy::RawCompress() を呼び出すという手順でも圧縮できますが,引数と戻り値が異なるので注意してください.

size_t snappy::Compress(const char *input, size_t input_length, string *output);

snappy::Compress の第 1 引数である input と第 2 引数である input_length には入力データの開始アドレスとサイズを指定するようになっています.第 3 引数の output には出力先として用いる std::string へのポインタを指定するようになっています.圧縮後のサイズが戻り値になります.std::string の拡張に失敗したときは例外を送出するため,必要に応じて try, catch を使うようにしてください.

size_t snappy::MaxCompressedLength(size_t source_bytes);

snappy::MaxCompressedLength() の第 1 引数である source_bytes には入力データのサイズを指定するようになっています.出力バッファに割り当てるべき領域のサイズが戻り値になります.

void snappy::RawCompress(const char *input,
                         size_t input_length,
                         char *compressed,
                         size_t *compressed_length);

snappy::RawCompress() の第 1 引数である input と第 2 引数である input_length には入力データの開始アドレスとサイズを指定するようになっています.第 3 引数の compressed に出力バッファの開始アドレスを指定するのは入力と同様ですが,第 4 引数の compressed_length には圧縮後のサイズを受け取る変数へのポインタを渡すようになっています.snappy_compress() とは異なり,出力バッファのサイズを確認することはなく,戻り値はありません.

伸長

#include <snappy.h>

C++ における伸長の手順は,snappy::Uncompress() を呼び出すだけです.C 言語と同様に snappy::GetUncompressedLength() により得られるサイズの領域を出力バッファとして確保した後で snappy::RawUncompress() を呼び出すという手順でも伸長できますが,引数と戻り値が異なるので注意してください.

bool snappy::Uncompress(const char *compressed, size_t compressed_length,
                        string *uncompressed);

snappy::Uncompress の第 1 引数である compressed と第 2 引数である compressed_length には圧縮データの開始アドレスとサイズを指定するようになっています.第 3 引数の uncompressed には出力先として用いる std::string へのポインタを指定するようになっています.圧縮に成功すれば true が戻り値になり,失敗すれば false が戻り値になります.std::string の拡張に失敗したときは例外を送出するため,必要に応じて try, catch を使うようにしてください.

bool snappy::GetUncompressedLength(const char *compressed, size_t compressed_length,
                                   size_t *result);

snappy::GetUncompressedLength() の第 1 引数である compressed と第 2 引数である compressed_length には圧縮データの開始アドレスとサイズを指定するようになっています.第 3 引数である result には圧縮前のサイズを受け取る変数へのポインタを指定するようになっています.サイズの取得に成功すれば true が戻り値になり,失敗すれば false が戻り値になります.

bool snappy::RawUncompress(const char *compressed, size_t compressed_length,
                           char *uncompressed);

snappy::RawUncompress() の第 1 引数である compressed と第 2 引数である compressed_length には圧縮データの開始アドレスとサイズを指定するようになっています.第 3 引数の uncompressed には出力バッファの開始アドレスを指定するようになっています.出力バッファのサイズが snappy::GetUncompressedLength() 以上であることは呼び出し側で確認する必要があります.圧縮に成功すれば true が戻り値になり,失敗すれば false が戻り値になります.

bool snappy::IsValidCompressedBuffer(const char* compressed,
                                     size_t compressed_length);

snappy::IsValidCompressedBuffer() を使うことにより,圧縮データの正当性を高速に判定できます.実際に伸長する方法と比べると,通常 4 倍以上高速です.第 1 引数である compressed と第 2 引数である compressed_length には圧縮データの開始アドレスとサイズを指定するようになっています.正当であると判定されたときは true が戻り値になり,正当でないと判定されたときは false が戻り値になります.

サンプルコード

$ gcc -Wall -O2 -std=c99 snappy-test.c -lsnappy -o snappy-test
$ g++ -Wall -O2 snappy-test.cc -lsnappy -o snappy-test

Snappy 形式の圧縮・伸長をおこなうプログラムのサンプルコードとして snappy-test.csnappy-test.cc を用意しました.C99 の機能を使っているので,gcc には -std=c99 を渡すようにしてください.

$ ./snappy-test --help
Usage: ./snappy-test [OPTION]... [FILE]...
Options:
  -c, --compress    圧縮します (default)
  -u, --uncompress  伸長します
  -o, --output=[FILE]  出力ファイルを指定します (default: stdout)
  -h, --help     このヘルプを表示します

コマンドライン引数による圧縮・伸長の切り替えが可能になっています.-h もしくは --help をオプションとして渡すことにより,コマンドライン引数の一覧を確認することができます.

おわりに

本記事では C/C++ における Snappy の使い方を説明しました.C/C++ 以外における Snappy の使い方については公式サイトにあるリンク集をご利用ください.