bzip2を読むタイトル__2_

bzip2を読む ハフマン符号8

こんにちは、junkawaです。

本記事では、ハフマン符号後のデータを出力バッファに書き出す処理について紹介します。

コードの紹介

sendMTFValues() @ compress.c
https://github.com/junkawa/bzip2/blob/master/bzip2-1.0.6/compress.c#L239

  544:    /*--- And finally, the block data proper ---*/

MTF変換後のデータをハフマン符号化して書き出します。

  545:    nBytes = s->numZ;

現在の出力バッファのサイズs->numZをnBytesに保存します。

  546:    selCtr = 0;
  547:    gs = 0;

selCtrは(MTF変換後のデータを50シンボル毎に分割した)ブロック番号です。0で初期化します。

gsはブロックの開始シンボル番号です。0で初期化します。

  548:    while (True) {

全ブロックのハフマン符号化したシンボルを書き出すまでループを回します。

  549:       if (gs >= s->nMTF) break;

gsがMTF後データのシンボル数(s->nMTF)を超えた場合、書き出しが完了したとして、ループを抜けます。

  550:       ge = gs + BZ_G_SIZE - 1;
  551:       if (ge >= s->nMTF) ge = s->nMTF-1;

今回のループでは、s->mtfv[gs]〜s->mtfv[ge]のシンボルを書き出します。

551行目で、geがMTF後のシンボル数を超えた場合、最後のデータとなるs->nMTF-1としています。

  552:       AssertH ( s->selector[selCtr] < nGroups, 3006 );
  553:

ブロックselCtrに割り当てたハフマンテーブルs->selector[selCtr](0〜nGroups-1)がnGroups未満であることを確認します。

  554:       if (nGroups == 6 && 50 == ge-gs+1) {
  555:             /*--- fast track the common case ---*/

nGroupsが6で、ブロックのシンボル数が50の時、処理を高速化できます。
ループを展開することで処理を高速化しています。

  556:             UInt16 mtfv_i;
  557:             UChar* s_len_sel_selCtr
  558:                = &(s->len[s->selector[selCtr]][0]);
  559:             Int32* s_code_sel_selCtr
  560:                = &(s->code[s->selector[selCtr]][0]);
  561:

s_len_sel_selCtrは、ハフマンテーブルselCtrの符号長配列の先頭ポインタです。

s_code_sel_selCtrは、ハフマンテーブルselCtrの符号配列の先頭ポインタです。

  562: #           define BZ_ITAH(nn)                      \
  563:                mtfv_i = mtfv[gs+(nn)];              \
  564:                bsW ( s,                             \
  565:                      s_len_sel_selCtr[mtfv_i],      \
  566:                      s_code_sel_selCtr[mtfv_i] )
  567:

BZ_ITAH(nn)では、シンボルmtfv[gs+nn]をハフマンテーブルsetCtrで符号化した符号s_code_sel_selCtr[mtfv_i] を、出力バッファに書き出します。

シンボルmtfv[gs+nn]は、符号長 s_len_sel_selCtr[mtfv_i]、符号s_code_sel_selCtr[mtfv_i] で書き出されます。

  568:             BZ_ITAH(0);  BZ_ITAH(1);  BZ_ITAH(2);  BZ_ITAH(3);  BZ_ITAH(4);
  569:             BZ_ITAH(5);  BZ_ITAH(6);  BZ_ITAH(7);  BZ_ITAH(8);  BZ_ITAH(9);
  570:             BZ_ITAH(10); BZ_ITAH(11); BZ_ITAH(12); BZ_ITAH(13); BZ_ITAH(14);
  571:             BZ_ITAH(15); BZ_ITAH(16); BZ_ITAH(17); BZ_ITAH(18); BZ_ITAH(19);
  572:             BZ_ITAH(20); BZ_ITAH(21); BZ_ITAH(22); BZ_ITAH(23); BZ_ITAH(24);
  573:             BZ_ITAH(25); BZ_ITAH(26); BZ_ITAH(27); BZ_ITAH(28); BZ_ITAH(29);
  574:             BZ_ITAH(30); BZ_ITAH(31); BZ_ITAH(32); BZ_ITAH(33); BZ_ITAH(34);
  575:             BZ_ITAH(35); BZ_ITAH(36); BZ_ITAH(37); BZ_ITAH(38); BZ_ITAH(39);
  576:             BZ_ITAH(40); BZ_ITAH(41); BZ_ITAH(42); BZ_ITAH(43); BZ_ITAH(44);
  577:             BZ_ITAH(45); BZ_ITAH(46); BZ_ITAH(47); BZ_ITAH(48); BZ_ITAH(49);
  578:

mtfv[gs]〜mtfv[ge](mtfv[gs+49])のシンボルを、ハフマンテーブルsetCtrで符号化し、出力バッファに書き出します。

  579: #           undef BZ_ITAH
  580:

BZ_ITAHをundefします。

  581:       } else {
  582: 	 /*--- slow version which correctly handles all situations ---*/

nGroupsが6ではないか、ブロックのシンボル数が50ではない時、このブロックを実行します。

  583:          for (i = gs; i <= ge; i++) {
  584:             bsW ( s,
  585:                   s->len  [s->selector[selCtr]] [mtfv[i]],
  586:                   s->code [s->selector[selCtr]] [mtfv[i]] );
  587:          }

s->selector[selCtr]は、このブロックに割り当てたハフマンテーブルの番号です。
mtfv[i]はブロック中のシンボルです。

bsW()で、符号長s->len [s->selector[selCtr]] [mtfv[i]]ビットで、符号s->code [s->selector[selCtr]] [mtfv[i]] を出力バッファに書き出します。

  588:       }
  589:
  590:
  591:       gs = ge+1;
  592:       selCtr++;

次回ループのために、ブロックの開始シンボル番号gsを、今回のブロックの終端geの次のシンボルge+1にします。

ブロック数setCtrをインクリメントします。

  593:    }
  594:    AssertH( selCtr == nSelectors, 3007 );
  595:

ブロック数selCtrが、以前に計算したブロック数nSelectorsと一致するか確認します。

  596:    if (s->verbosity >= 3)
  597:       VPrintf1( "codes %d\n", s->numZ-nBytes );
  598: }
  599:
  600:

書き出したハフマン符号化データのサイズを表示します。

まとめ

本記事では、ハフマン符号後のデータを出力バッファに書き出す処理について紹介しました。

ここまでの連載で、ブロックソート、MTF変換、ハフマン符号化の処理を全て紹介しました。

次回は、ブロックソートの呼び出し元に戻って、圧縮ファイルのデータ構造という点で紹介したいと思います。

ご覧下さりありがとうございます。いただいたサポートは図書館への交通費などに使わせていただきます。