konoです。実に一ヶ月以上投稿してなく、弊社のブログ管理者から圧迫かけられたので、またちょっとした小ネタを。
忙しくて予定していた全文検索ネタは後回しです。
そちらはGW中にでもまとめようかと。
さて、今回は題名の通りObjective-C(APIはCocoa使ってます)での圧縮です。
素直にZipArchiveとか使えよって話ですが、色々と理由があって、サードパーティのライブラリを使わず自分でデータを圧縮する必要があり、Cocoa付属のzlibを使って圧縮を行ってみました。
マニアックなネタなんであんまり役に立たないかもしれませんが。
ちなみに今後、私はマニアックなネタ路線で行くつもりなので。
と言うわけで、以下コードの抜粋になります。
CompressUtil.h
#import <zlib.h> @interface Compress : NSObject { } + (NSData *) cmpDeflate:(NSData *) srcData; @end
CompressUtil.m
#import "CompressUtil.h" @implementation + (NSData *) cmpDeflate:(NSData *) srcData { // 構造体の定義 z_stream zlibStrmStruct; // zalloc,zfree,opaqueにZ_NULLをセット zlibStrmStruct.zalloc = Z_NULL; zlibStrmStruct.zfree = Z_NULL; zlibStrmStruct.opaque = Z_NULL; // 圧縮アルゴリズムの初期化 zlibStrmStruct.total_out = 0; // zlibStrmStruct.next_in = (Bytef *)[srcData bytes]; zlibStrmStruct.avail_in = [srcData length]; // 初期化 int initError = deflateInit2(&zlibStrmStruct, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY); if(initError != Z_OK) { // エラー処理 return nil; } // アウトプット用メモリバッファ // (zlibのドキュメントによるとバッファサイズはavail_inより0.1%多く、さらに12バイト足したサイズが必要?) NSMutableData *cmpData = [NSMutableData dataWithLength:[srcData length] * 1.01 + 12]; // 結果格納用 int deflateStatus; // 全てのデータが圧縮されるまで繰り返し do { zlibStrmStruct.next_out = [cmpData mutablebytes] + zlibStrmStruct.total_out; zlibStrmStruct.avail_out = [cmpData length] - zlibStrmStruct.total_out]; deflateStatus = deflate(&zlibStrmStruct, Z_FINISH); } while ( deflateStatus == Z_OK ); // 圧縮後のステータスがZ_STREAM_END以外はエラーと判断 if (deflateStatus = != Z_STREAM_END) { // エラー処理 deflateEnd(&zlibStrmStruct); return nil; } // メモリ解放 deflateEnd(&zlibStrmStruct); // サイズ確定 [cmpData setLength:zlibStrmStruct.total_out]; return cmpData; } @end
一応、zlibの公式ページへのリンクを貼っておきます。
が、私は「zlib」でググって出てくる和訳ページにお世話になりましたので、公式ページはほとんど見ていません。
さて、処理の流れですがおおざっぱに言うと、初期化→格納バッファ確保→圧縮といった感じで、ほんとおおざっぱですみません。
キモは初期化部分で、圧縮方式やら圧縮率やらを設定します。
int initError = deflateInit2(&zlibStrmStruct, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
この部分ですが構文はこんな感じです。
ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, // データ構造体のアドレス
int level, //圧縮レベル(0〜9まで指定可)
int method, // 圧縮命令(Z_DEFLATED固定らしい)
int windowBits, // 圧縮方式や圧縮時のメモリ使用率を指定(だと思う)
int memLevel, // これもメモリ使用率に関係
int strategy)); // 圧縮アルゴリズム
関数名がdeflateInit2と2が付いていますが、deflateInitと無印もあります。
違いはあんまりちゃんと調べてないので、あやふやですがdeflateInitはzip圧縮、deflateInit2はzlib形式(gzipも指定可能)なんですかね。
それぞれの引数ですが、コメントアウトに大雑把な説明を書いておきました。
第4引数はちょっと複雑で、8から15までの整数を与えることができ、大きい数であるだけ圧縮率が高くなりますが、そのぶんメモリの使用量が増加するようです。
デフォルトの値は15です。
- 8から-15の整数を指定すると、ZLIBヘッダとトレイラのない生のdeflateデータを作成して、15以上はGZIPエンコーディング、WINDOW-BITSに16を足すと、zlibラッパの代わりに、シンプルなgzipヘッダとトレイラが圧縮データの前後に書き出されます。
詳細はzlibのドキュメントでご確認を。
私自身あやふやな部分があるので、もうちょっと調べたら追記しようと思います。