S-EBXA の圧縮形式について
笠原 基之
(m-kasahr@sra.co.jp)
2001年 7月 29日
本書は、S-EXBA から採用された圧縮形式について、現時点で分かっていること を記したものである。 一応、本書の解釈に従って実装したプログラムで、ソニーデータディスクマン DD-S1000 付属の「日本大百科全書」の伸長ができていることは確認している。
この圧縮形式の解析は、主に根本隆さんの功績による。
S-EBXA の圧縮は START
ファイル中の本文データ部だけに
対して行われる。
しかも、すべての S-EBXA が必ずデータの圧縮を行っているわけではなく、
圧縮を採用しているのは、ごく一部の S-EBXA だけである。
圧縮された S-EBXA において、本文以外のデータは圧縮されていないため、 従来の電子ブックと同様の処理方法でアクセスできる。 またその性質上、圧縮された本文データは、ファイルの末尾に置かれるよう である。 つまり、本文データの後方にさらに非圧縮データが置かれることはない。
圧縮を採用した S-EBXA をソフトウェアが扱う場合、アクセスしようとしている データが本文データの範囲内にあるのかどうかをまず確認する必要がある。 範囲外ならそのままアクセスすれば良いが、範囲内にあるなら読み込んだデータ を伸長する必要がある。
書籍管理情報は、通常 START
ファイルの先頭ブロックに置かれ、
ファイルの構成要素の一覧を記述してある部分である。
EPWING および電子ブックでは、それぞれの書籍構成要素は次のようなデータ からなる (この情報は JIS X 4081 の記述に基づく)。
書籍構成要素識別子 |
予備領域 3 |
開始ブロック |
ブロック数 |
インデックス作成情報有効性 |
インデックス作成情報 |
予備領域 4 |
本文データの書籍構成要素識別子には、00H が割り当てられている。 たとえば、本文データの書籍構成要素は、次のようなバイト列で表現される。
00 00 00 00 8f 89 00 01 1d e4 01 00 00 00 00 00
この例では、開始ブロックは 00008f89H であり、ブロック数は 00011de4H で ある。 ただし、1 ブロックは 2048 バイトからなり、最初のブロックを第 1 ブロック と数えるものとする。
以上の説明は本文データが圧縮されている、いないにかかわらず、共通する話 である。 本文データが圧縮されている場合は、さらに次の 2 個の識別子が書籍管理情報 に記載される。
識別子 | 意味 |
22H | 圧縮本文データへのインデックス |
21H | 圧縮本文データ |
電子ブックにおいて、S-EBXA 形式のデータ圧縮が行われているかどうかは、 この識別子 22H と 21H の有無から判定できる。 書籍構成要素のデータは、具体的には次のようになっている。
22 00 00 00 8f 89 00 00 00 48 01 00 00 00 00 00 21 00 00 00 8f d1 00 00 c0 79 01 00 00 00 00 00
ここで、「圧縮本文データへのインデックス」「圧縮本文データ」の領域は、 いずれも先ほどの「本文データ」の領域と重なっており、しかも「圧縮本文 データへのインデックス」と「本文データ」の開始位置が同じである点に注意 されたい。
圧縮本文データへのインデックス | 8f89H 〜 8fd0H |
圧縮本文データ | 8fd1H 〜 1504aH |
本文データ | 8f89H 〜 1ad6dH |
圧縮された S-EBXA において、「本文データ」構成要素の示す開始ブロック およびブロック数は、データを伸長したらその位置およびブロック数になる ということを意味している。
START
ファイル内ではすべて、参照先の位置の表現は非圧縮時
のもので表現されている。
たとえば、単語の検索を行うと見つかった単語の本文の位置が得られるが、
このときの位置は非圧縮時のものである。
したがって、得られた位置情報をもとに START
ファイル内の
データをアクセスする際は、そこが圧縮されている部分かどうかの判定に
「本文データ」構成要素の情報が必要となる。
先の例なら、得られた位置情報がブロック 8f89H 〜 1ad6dH の範囲内で
あれば、その位置にあるデータは圧縮されていることになる。
S-EBXA の圧縮本文データは先頭から 4096 バイトずつを 1 つの塊として、 それぞれ独立して圧縮されている (圧縮アルゴリズムについては次の節で 詳しく述べる)。以下、この塊のことを「スライス」と呼ぶことにする。
各スライスの圧縮データの先頭位置を書き並べているのが、「圧縮本文データ へのインデックス」部である。 各スライスの先頭位置は、「圧縮本文データ」の開始位置からの相対位置を バイト単位で記されている。
それぞれの相対位置は 4 バイトで表現される。 また、一番先頭のスライス (第 1 スライス) の相対位置は 00000000H と 決まっているため記載されず、二番目のスライス (第 2 スライス) の相対位置 から順に記載される。
「圧縮本文データへのインデックス」の先頭部分の具体的なデータ列を 以下に記す。 この例では、第 2 スライスの相対位置が 00000bcaH、第 3 スライスが 00001712H、以下 000021b3H、00002d8cH... と続く。
00000bca 00001712 000021b3 00002d8c 00003899 00004348 00004ce6 000055fc 0000611f 00006c67 0000781a 000083ce
たとえば、圧縮本文データの開始ブロックを 8fd1H とすると、第 2 スライス の圧縮データの開始位置 (バイト単位) は、次のように計算することができる。
(8fd1H - 1) * 2048 + 00000bcaH = 047e8bcaH
蛇足であるが、すべてのスライスのインデックスを書き終わった箇所がブロック の途中であった場合は、そのブロックの末尾まで 00000000H が埋められている。
各スライスの圧縮アルゴリズムについて説明する。 基本的にこのアルゴリズムは、LZ77 の変形と言える。
スライスの圧縮データは、連続する「グループ」で構成される。 スライスのデータは、先頭のグループから順に逐次伸長していけば、 スライス全体を伸長できるようになっている。
各グループはさらに第 1 から 第 8 までの 8 個の「圧縮データ要素」 からなる。 グループのデータを伸長する際は、やはり先頭の第 1 要素から順に逐次 伸長していく。 おのおのの圧縮データ要素は 1 バイトないし 2 バイトで構成され、 さらに各圧縮データ要素の「モード」の情報を記録するために、1 バイト を消費している (「モード」については後述する)。 グループ内の圧縮データ要素の構成順序は、次の通りである。
各圧縮データ要素のモード情報 第 1 圧縮データ要素 第 2 圧縮データ要素 : (略) 第 8 圧縮データ要素
個々の圧縮データ要素は、次のいずれかのモードを取り、モードに応じた データを有している。
「1 バイトの非圧縮データ」というのは、1 バイト分のデータを、圧縮なし にそのまま記録したものである。
もう一つの「前方参照」というのは、前に現れたデータ列と同じデータがそ こに置かれることを示す。 前方参照の場合、圧縮データ要素は 2 バイトで構成される。 最初のバイトを c0、次のバイトを c1 とすると、圧縮データ要素は次の ような意味を持つ。ただしここで、& は論理積、% は剰余算とする。
先頭バイト位置 := (c0 + (c1 & f0H) * 16 + 18) % 4096 バイト数 := (c1 & 0fH) + 3
この「先頭バイト位置」「バイト数」によって、スライスの先頭何バイト目 から何バイト分のデータをコピーするのかを表す。 「先頭バイト」はスライス先頭からの相対バイト数を表すので、スライスの 先頭バイトは 0 バイト目となる。
なお、定かな情報ではないが、コピーの動作は 1 バイトずつ行う必要がある かも知れない。 たとえば、現在位置がスライス先頭から 2 バイト目なのに、「スライスの 先頭 0 バイト目から 5 バイトコピー」というデータが記録されることも有り 得るかも知れない。 この場合は、次のような処理を順々に行うことで、2 〜 6 バイト目のデータ の内容が一意に確定する。
0 バイト目のデータを 2 バイト目にコピー 1 バイト目のデータを 3 バイト目にコピー 2 バイト目のデータを 4 バイト目にコピー 3 バイト目のデータを 5 バイト目にコピー 4 バイト目のデータを 6 バイト目にコピー
グループの先頭に付加される「各圧縮データ要素のモード情報」には、グループ 内の第 1 から 第 8 までの 8 個の圧縮データ要素がどちらのモードでデータが 書かれているのかが記録されている。 「各圧縮データ要素のモード情報」は 1 バイトで構成され、圧縮データ要素 1 個に対して 1 ビットが割り当てられている。
第 1 圧縮データ要素に割り当てられているのが LSB (Least Significant Bit)
であり、第 8 圧縮データ要素に割り当てられているのが、MSB (Most Significant
Bit) である。
ビットが 1 なら「1 バイトの非圧縮データ」モードであり、0 なら「前方参照」
モードであることを示す。
つまり、モード表すバイトの値を m
とすると、次のようになる。
第 1 圧縮データ要素: (m & 01H) = 0 なら「前方参照」 第 2 圧縮データ要素: (m & 02H) = 0 なら「前方参照」 第 3 圧縮データ要素: (m & 04H) = 0 なら「前方参照」 第 4 圧縮データ要素: (m & 08H) = 0 なら「前方参照」 第 5 圧縮データ要素: (m & 10H) = 0 なら「前方参照」 第 6 圧縮データ要素: (m & 20H) = 0 なら「前方参照」 第 7 圧縮データ要素: (m & 40H) = 0 なら「前方参照」 第 8 圧縮データ要素: (m & 80H) = 0 なら「前方参照」