このページの内容

LZ4ストリーミングAPIの例: 行単位のテキスト圧縮

著者: Takayuki Matsuoka

blockStreaming_lineByLine.cは、行単位の逐次圧縮・展開を実装する、LZ4ストリーミングAPIのサンプルです。

次の制約に注意してください。

  • まず「LZ4ストリーミングAPIの基礎」を読んでください。
  • 比較的高度なアプリケーションの例です。
  • 出力ファイルはlz4frameと互換性がなく、プラットフォームに依存します。

このサンプルのポイント

  • 行単位の逐次圧縮・展開。
  • 少量のメモリで巨大なファイルを扱う。
  • 一般にBlock APIより高い圧縮率。
  • 不均一なブロックサイズ。

圧縮の仕組み

まず、入力用の「リングバッファー」と、出力用のLZ4圧縮データバッファーを確保します。

(1)
    Ring Buffer

    +--------+
    | Line#1 |
    +---+----+
        |
        v
     {Out#1}


(2)
    Prefix Mode Dependency
          +----+
          |    |
          v    |
    +--------+-+------+
    | Line#1 | Line#2 |
    +--------+---+----+
                 |
                 v
              {Out#2}


(3)
          Prefix   Prefix
          +----+   +----+
          |    |   |    |
          v    |   v    |
    +--------+-+------+-+------+
    | Line#1 | Line#2 | Line#3 |
    +--------+--------+---+----+
                          |
                          v
                       {Out#3}


(4)
                        External Dictionary Mode
                +----+   +----+
                |    |   |    |
                v    |   v    |
    ------+--------+-+------+-+--------+
          |  ....  | Line#X | Line#X+1 |
    ------+--------+--------+-----+----+
                            ^     |
                            |     v
                            |  {Out#X+1}
                            |
                          Reset


(5)
                                    Prefix
                                    +-----+
                                    |     |
                                    v     |
    ------+--------+--------+----------+--+-------+
          |  ....  | Line#X | Line#X+1 | Line#X+2 |
    ------+--------+--------+----------+-----+----+
                            ^                |
                            |                v
                            |            {Out#X+2}
                            |
                          Reset

次に、図(1)のように最初の行をリングバッファーに読み込み、LZ4_compress_continue()で圧縮します。初回は、LZ4は先行する依存先を知りません。そのため、その行を依存関係なしで圧縮し、LZ4圧縮データバッファーに圧縮した行{Out#1}を生成します。その後、{Out#1}をファイルに書き込み、リングバッファーのオフセットを進めます。

二番目の行にも同じ処理を行います(図(2))。ただし、今度は、LZ4がLine#1への依存関係を使い、圧縮率を改善できます。この依存関係を「プレフィックスモード」と呼びます。

やがてLine#Xでリングバッファーの末尾に到達します(図(4))。この時点でリングバッファーのオフセットをリセットすべきです。リセット後、Line#X+1のポインターは隣接していませんが、LZ4はまだそのメモリを保持しています。これを「外部辞書モード」と呼びます。

Line#X+2(図(5))では、LZ4はついにほぼすべてのメモリを忘れますが、Line#X+1はまだ残っています。これはLine#2と同じ状況です。

テキストファイルの末尾まで、この手順を繰り返します。

展開の仕組み

展開では、逆の処理を行います。

  • ファイルから圧縮した行をバッファーに読み込みます。
  • リングバッファーに展開します。
  • 展開したプレーンテキストの行をファイルに出力します。
  • リングバッファーのオフセットを進めます。オフセットがリングバッファーの末尾を超えた場合は、リセットします。

圧縮ファイルの末尾まで、この手順を繰り返します。