bzip2を読む ファイルフォーマット
こんにちは、junkawaです。
本記事では、bzip2の圧縮ファイルの形式について紹介します。
ストリームヘッダ
上段左端の‘BZ’、’h’、blocksizeがヘッダとなります。blocksizeは、’1’〜’9’となります。
‘BZ’ (16bit) は識別子です。compress.c 624、625行目で書き出します。
‘h’ (8bit)はバージョンです。bzip2は’h’、bzip1は’0’です。compress.c 626行目。
blocksize(8bit)は入力ブロックサイズです。’1’〜’9’となります。’9’の時、ブロックサイズは900,000バイトとなります。compress.c 627行目。
ストリームテイラ
上段右端の0x177245385090、CRCがテイラとなります。
0x177245385090(48bit)は、sqrt(π) (円周率πの平方根) の BCD(Binary-coded decimal)です。compress.c 659、660、661行目。
CRC(32bit)は、圧縮前の入力データに対応するCRC(Cyclic Redundancy Check)です。compress.c 662行目。
CRCの後ろに、1バイトアライメントになるようにパディング(1〜7bit)します。compress.c 665行目。
ブロックデータ
ヘッダの後には、入力ブロックデータに対応する出力ブロックデータが続きます。入力ブロックデータは一般に複数あるので、対応する出力ブロックデータも複数あります。図中の下段が、出力ブロックデータの中身です。
0x314159265359(48bit)は、円周率πのBCDです。compress.c 632、633、634行目。
CRC(32bit)は入力ブロックデータに対応するCRCです。compress.c 637行目。
0(1bit)はランダム化の使用不使用です。現在のbzip2ではランダム化は行わず、常に0です。compress.c 648行目。
origPtr(24bit)はブロックソート逆変換に使用する、オリジナルデータのソート後の行番号です。compress.c 650行目。
used_map(16bit)はused_bitmaps[0]〜[15]のpresence bit(存在ビット)です。used_mapが1となるビットに対応するused_bitmapsのみが出力データに含まれます。compress.c 505行目。
used_bitmaps(16〜256bit)は入力データ中に(ビット位置に対応する)シンボルが存在するかどうかを表します。compress.c 510行目。
huffman_groups(3bit)はマルチプルハフマン符号化で使用するハフマンテーブルの数です。compress.c 519行目。
selector_used(15bit)はハフマンテーブルを割り当てた(MTF変換後のデータを50シンボル毎に区切った)ブロックの数です。compress.c 520行目。
selector_list(1〜6 * selector_used bit)はブロックに割り当てたハフマンテーブルの番号です。厳密には、テーブル番号をMTF変換した後にデルタ符号化した値です。compress.c 521〜524行目。
huffman_trees (可変長ビット) はハフマンテーブルです。huffman_groups個のハフマンテーブル毎のシンボルと符号長の組です。compress.c 531〜539行目。
huffman_encoded_data(可変長ビット)はハフマン符号化された圧縮データ本体です。compress.c 548〜593行目
参考
https://github.com/dsnet/compress/blob/master/doc/bzip2-format.pdf
https://en.wikipedia.org/wiki/Bzip2#File_format
コードの紹介
BZ2_compressBlock() @ compress.c
https://github.com/junkawa/bzip2/blob/master/bzip2-1.0.6/compress.c#L602
602: void BZ2_compressBlock ( EState* s, Bool is_last_block )
603: {
入力データ900,000バイト毎にBZ2_compressBlock () が呼び出されます。
(900,000は、コマンドラインオプションで変更できます)
604: if (s->nblock > 0) {
605:
s->nblockが0の時に呼び出されることはある?ブロックにデータが1つでもある時。lastblockの時、テイラだけ書き出すことがある?
入力データブロックが存在する時、このブロックを実行します。
606: BZ_FINALISE_CRC ( s->blockCRC );
s->blockCRCは、(ランレングス符号化前の)入力データブロックのCRCです。
bzip2では、AutoDIN II多項式で生成したCRCを利用します。
bzlib_private.h
162: #define BZ_FINALISE_CRC(crcVar) \
163: { \
164: crcVar = ~(crcVar); \
165: }
607: s->combinedCRC = (s->combinedCRC << 1) | (s->combinedCRC >> 31);
608: s->combinedCRC ^= s->blockCRC;
s->combinedCRCは入力データのCRCです。
609: if (s->blockNo > 1) s->numZ = 0;
610:
s->numZは、出力バッファに書き出したデータサイズです。0で初期化します。
ここで、s->blockNo > 1 としている理由は不明です。
s->blockNoが1の時はprepare_new_block()@bzlib.cで初期化しているためでしょうか。
611: if (s->verbosity >= 2)
612: VPrintf4( " block %d: crc = 0x%08x, "
613: "combined CRC = 0x%08x, size = %d\n",
614: s->blockNo, s->blockCRC, s->combinedCRC, s->nblock );
615:
s->blockNoは入力データブロックの番号です。入力データブロックのCRC s->blockCRCとこれまでの入力データのCRC s->combinedCRC、入力データブロックのシンボル数(バイト数)s->nblock を表示します。
616: BZ2_blockSort ( s );
入力データブロックをブロックソートします。
s->origPtrに、オリジナルデータのソート後の巡回行列の行番号が入ります。
617: }
618:
619: s->zbits = (UChar*) (&((UChar*)s->arr2)[s->nblock]);
620:
入力データブロック配列s->arr2 の後尾を、出力バッファs->zbitsとします。
621: /*-- If this is the first block, create the stream header. --*/
622: if (s->blockNo == 1) {
入力データブロック番号が1、つまり初めてBZ2_compressBlock()が呼ばれた時、出力バッファの先頭にヘッダ情報を書き出します。
623: BZ2_bsInitWrite ( s );
出力バッファを初期化します。
624: bsPutUChar ( s, BZ_HDR_B );
625: bsPutUChar ( s, BZ_HDR_Z );
626: bsPutUChar ( s, BZ_HDR_h );
627: bsPutUChar ( s, (UChar)(BZ_HDR_0 + s->blockSize100k) );
ヘッダ情報(“BZ”、”h”、’1’〜’9’)を書き出します。
628: }
629:
630: if (s->nblock > 0) {
631:
入力データブロックが存在する時、このブロックを実行します。TODO 補足必要
632: bsPutUChar ( s, 0x31 ); bsPutUChar ( s, 0x41 );
633: bsPutUChar ( s, 0x59 ); bsPutUChar ( s, 0x26 );
634: bsPutUChar ( s, 0x53 ); bsPutUChar ( s, 0x59 );
635:
ブロックヘッダのマジックナンバー(0x314159265359)を書き出します。
636: /*-- Now the block's CRC, so it is in a known place. --*/
637: bsPutUInt32 ( s, s->blockCRC );
638:
入力データブロックに対応するCRCを書き出します。
639: /*--
640: Now a single bit indicating (non-)randomisation.
641: As of version 0.9.5, we use a better sorting algorithm
642: which makes randomisation unnecessary. So always set
643: the randomised bit to 'no'. Of course, the decoder
644: still needs to be able to handle randomised blocks
645: so as to maintain backwards compatibility with
646: older versions of bzip2.
647: --*/
648: bsW(s,1,0);
649:
ランダム化ビットに0(不使用)をセットします。1.0.6ではランダム化を使用しません。
650: bsW ( s, 24, s->origPtr );
ブロックソート逆変換で必要となるs->origPtrを書き出します。これは、ブロックソート後のオリジナルデータの(巡回行列の)行番号です。
651: generateMTFValues ( s );
MTF変換を行います。
652: sendMTFValues ( s );
ハフマン符号化を行い、伸長に必要な情報と符号化したデータを出力バッファに書き出します。
653: }
654:
655:
656: /*-- If this is the last block, add the stream trailer. --*/
657: if (is_last_block) {
658:
最後の入力データブロックの場合、テイラ情報を書き出します。
659: bsPutUChar ( s, 0x17 ); bsPutUChar ( s, 0x72 );
660: bsPutUChar ( s, 0x45 ); bsPutUChar ( s, 0x38 );
661: bsPutUChar ( s, 0x50 ); bsPutUChar ( s, 0x90 );
テイラのマジックナンバー(0x177245385090)を書き出します。
662: bsPutUInt32 ( s, s->combinedCRC );
入力データ全体に対応するCRCを書き出します。
663: if (s->verbosity >= 2)
664: VPrintf1( " final combined CRC = 0x%08x\n ", s->combinedCRC );
入力データ全体に対応するCRCを表示します。
665: bsFinishWrite ( s );
書き出しバッファをバイトアライメントにパディングします。
666: }
667: }
まとめ
bzip2の圧縮ファイルの形式について紹介しました。
ご覧下さりありがとうございます。いただいたサポートは図書館への交通費などに使わせていただきます。