OSDN Git Service

Should not create symlinks in `lha p' command.
[lha/lha.git] / Hacking_of_LHa
1 $Id$
2
3                The Hacking of LHa for UNIX (3rd draft)
4              -------------------------------------------
5
6                                 Koji Arai <arai@users.sourceforge.jp>
7
8 本書は、LHa for UNIX 1.14i のソースを解読し、その圧縮アルゴリズムの実
9 装を確認するためのものだ。この成果は別の形でまとめなおされ資料になるか
10 もしれないし、このままの形で保管されるかもしれないし、新たにソースを書
11 き起こす元になるかもしれない。とにかく、暇な正月休みを潰すためにやって
12 みただけのものだ。(休みが明けるとまた忙しくなるので、これ以上まったく
13 何もしないかもしれない)
14
15 本書は、まだ未完成である。にもかかわらず公開するのはこれ以上続かないか
16 もしれないからである(気が向いたらまた続きを書くだろう。あるいは応援の
17 お手紙がくればやる気が出るかもしれない)。
18
19 本書はフリーである。複製、改変、再配布は自由であるということだ。ただし
20 本書により生じたあらゆる損害、不利益に対しては一切の保証はない。本書に
21 は嘘があるかもしれない。それに対して嘘を教えられたと著者を避難をしない
22 で頂きたい。しかし間違いの指摘は構わない(ぜひお願いしたい)、著者は圧縮
23 処理に関しては無知である。用語の使い方等は適切でないかもしれないのでこ
24 の方面でも御指導頂ければ幸いである。
25
26 < 目次 >
27
28 表記について
29 slide 辞書法 (slide.c)
30 bit 入出力ルーチン (crcio.c)
31 Huffman 法 (huf.c)
32 LHA ファイルの構造(まとめ)
33 セキュリティバグの考察
34
35 ===============================================================================
36 表記について
37
38 * 関数は、その定義ソース file.c と関数名 func() を示すのに
39      file.c:func()
40   という記述を使う
41
42 * 配列の添字は、Python言語のスライス演算子の記法に準じた
43
44     a[m:n] は、m <= i < m+n の範囲の a[i] を意味する。
45
46 * 値の範囲は、Ruby言語の範囲演算子の記法に準じた。これを配列の
47   添字に使用する場合もある。
48
49     m <= i <= n   -> i = m..n
50     m <= i < n    -> i = m...n
51
52     a[m..n] は、m <= i <= n の範囲の a[i] を意味する。
53
54 * m の n 乗 は、m^n で表す。^ は、排他的論理和としても利用されるが
55   文脈から判断してもらう。
56
57 * v{n} は、変数 v の値が n であることを表す。n は、サンプルの値であったり
58   定数の値であったりする。
59
60   v=n は代入文
61
62   配列の内容は、
63     ary[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 }
64   のように書く
65
66 o 用語について
67
68 * 符号
69         符号化、符号語、圧縮文
70
71         符号語は、1つの文字を符号化した結果。圧縮文は符号語の並び。
72
73 * 復号
74         復号化、復号語、復号文
75
76         復号語は、圧縮文から1つの文字を復号化した結果。復号文は復号語の並び。
77
78 * 平文
79         圧縮前の文を示す。対して復号文は、復号した後の文を意味する。
80
81 * slide 辞書法
82
83 * Huffman 法
84    動的 Huffman 法、静的 Huffman 法
85
86 ===============================================================================
87
88 \f
89 slide 辞書法 (slide.c)
90 ----------------------
91
92 まず、構造について考える。
93
94 slide辞書法は、encoding にさまざまな工夫が凝らされるのでとても複雑だが、
95 普通 decoding は単純である。decoding を解析することでどのような圧縮文
96 を作っているのかを調べてみる。
97
98 decoding を行う処理は、slide.c の decode() である。この処理を見てみる
99 と思った通り簡単に解読できた(以下)
100
101   1. huffman coding により復号した文字を環状バッファ dtext に書き込む
102      通常の文字 c は、c < 256 で表現されている(つまりそのまま)
103
104   2. 通常の文字でないものが現れたら、それは長さである。長さ len は、
105      len > 255 で表現されている。len から 0xfd(253) を引いた値が
106      実際の長さを表す(-lzs- method の場合は、0xfe(254)を引く)
107     「長さ」が、現れたらその直後には「位置」が書かれているのでそれを
108      読む。こうして、長さと位置のペア<len, pt>を得る
109
110      dtext から pt+1 バイト前の len バイトを読み、dtext に追加で書き込む
111
112    3. dtext が一杯(dicsiz)になったらファイルに書き出す
113
114 これの繰り返しである。つまり、slide 辞書法の圧縮文は、文字 c と<len,
115 pt> の並びであることがわかる。例えば、文字列 c1 c2 c1 c2 は、以下のよ
116 うに表現されているはずである。(本当は、長さが 2 以下では圧縮が起こらな
117 いので平文のまま出力する。長さは最低でも 3 は必要)
118
119         +----+----+--------+
120         | c1 | c2 | <2, 1> |
121         +----+----+--------+
122
123 では、この構造を作成する圧縮処理について考える。slide 辞書法では、ファ
124 イルから読み込んだ文字列 token が、以前に読み込んだ辞書に存在すれば
125 <len, pt> のペアを出力し、存在しなければ token をそのまま出力する。読
126 み込んだ token は、辞書に追加し、辞書の語が溢れたら古い情報を捨てる。
127
128 何も予備知識がない状態で書けば
129
130         while (read_file(&token, tokensiz)) {
131           len = search_dict(dict, token, &pt);
132           if (len == -1) {
133             print_token(token);
134           else
135             print_pair(len, pt);
136           update_dict(dict, token);
137         }
138
139 のようになるはず。ここで、tokensiz は token の最大サイズで、最長一致長
140 を表す。この値が大きければ大きい程、圧縮効率は良くなるはずで、lha では、
141 これは MAXMATCH{256}である。また、dict は辞書でこのサイズは lha の 
142 -lh5- メソッドでは、8192 となっている。この辞書も大きければ大きい程良
143 いはずだ。その方が一致文字列が見つかりやすい。(ただし、辞書が大きいと
144 一致位置を示す情報 <len, pt> の情報量が増えるはずだし、速度も遅くなる
145 だろう。後で検証する)
146
147 で、実際にソースを見てみると(slide.c:encode())・・・、まったくこのよう
148 な構造にはなってないように見える。何やらややこしいことばかりでまったく
149 わからない。なぜここまでややこしいのかと泣きたくなってくるが、それは速
150 度のためである(本当?)。上記のコードで、search_dict() は、単に dict か
151 ら token に一致する位置を検索するだけで良さそう(実際にそれでも良い)だ
152 が、これではまったく速度が出ない。このあたりの工夫が slide 辞書法のキ
153 モである。
154
155 そういうわけで、この部分を読み解くことにする。なお、予備知識として lha 
156 では、辞書から token を探すのにハッシュが使われているらしいことを記し
157 ておく。
158
159 ここでは実際にデバッガで動作させながら解読するのではなく、ソースを読む
160 だけで理解できるかを試すことにする。また、本文は某書(謎)のノリをマネて
161 いると指摘する方がいるかもしれない・・・がまったくその通りだ。
162
163 まず、そのものずばりの encode() (slide.c) を見る。以下がこの関数だが
164 処理の要点だけに着目するために不要そうな部分は(現時点で予測で)削った。
165
166 unsigned int
167 encode()
168 {
169     int lastmatchlen;
170     unsigned int lastmatchoffset;
171
172     /* (A) */
173     init_slide();  
174
175     /* (B) */
176     remainder = fread_crc(&text[dicsiz], txtsiz-dicsiz, infile);
177     encoded_origsize = remainder;
178     matchlen = THRESHOLD - 1;
179
180     pos = dicsiz;
181
182     if (matchlen > remainder) matchlen = remainder;
183
184     /* (C) */
185     hval = ((((text[dicsiz] << 5) ^ text[dicsiz + 1]) << 5) 
186             ^ text[dicsiz + 2]) & (unsigned)(HSHSIZ - 1);
187
188     /* (D) */
189     insert();
190
191     while (remainder > 0 && ! unpackable) {
192         /* (E) */
193         lastmatchlen = matchlen;  lastmatchoffset = pos - matchpos - 1;
194         --matchlen;
195
196         /* (F) */    /* (G) */
197         get_next();  match_insert();
198         if (matchlen > remainder) matchlen = remainder;
199
200         /* (H) */
201         if (matchlen > lastmatchlen || lastmatchlen < THRESHOLD) {
202             /* (H.1) */
203             encode_set.output(text[pos - 1], 0);
204             count++;
205         } else {
206             /* (H.2) */
207             encode_set.output(lastmatchlen + (UCHAR_MAX + 1 - THRESHOLD),
208                (lastmatchoffset) & (dicsiz-1) );
209             --lastmatchlen;
210
211             while (--lastmatchlen > 0) {
212                 get_next();  insert();
213                 count++;
214             }
215             get_next();
216             matchlen = THRESHOLD - 1;
217             match_insert();
218             if (matchlen > remainder) matchlen = remainder;
219         }
220     }
221 }
222
223 まず、この関数から概観を見てみると、ループの前に初期化処理として
224 以下が行われている。
225
226 (A) init_slide() 初期化する
227 (B) ファイルを読み込み text[] に格納する。
228 (C) ハッシュ値 hval を計算する。
229 (D) insert() する (きっと辞書に token を追加しているのだろう)
230
231 そして、ループ処理では以下の処理が行われている
232
233 (E) lastmatchlen, lastmatchoffset, matchlen を更新する。
234 (F) get_next()  (次の token を読む。たぶん)
235 (G) match_insert()  (辞書に追加する。たぶん)
236
237 (H) matchlen > lastmatchlen || lastmatchlen < THRESHOLD なら
238
239 (H.1) output() する。(マッチしなかったらそのまま出力しているのだろう。たぶん)
240 (H.2) そうでなければ(マッチしたなら)、output()し、何かいろいろする。
241
242 現時点で、(H.2) の部分はよく解読できなかった。何やら再度 get_next() が
243 呼ばれていたりして思った通りの処理フローにはなっていない。だが、ここで
244 は焦らず放置することにして、ここまで予想で書いた部分の細部に触れること
245 にする(単にここまでの予想が間違っているだけかもしれないのだから、わか
246 らない部分を無理にわかるように頑張る必要はなかろう)
247
248 関数の細部に触れる前にデータ構造について調べておく。データ構造に対して
249 の理解が深まればアルゴリズムの80%は分かったも同然だ(誇張)。slide.c で
250 使用されているデータ構造は以下の通りだ。(不要そうだと思うものは除いて
251 ある)
252
253 static unsigned int *hash;
254 static unsigned int *prev;
255 unsigned char *too_flag;
256 static unsigned int txtsiz;
257 static unsigned long dicsiz;
258 static unsigned int hval;
259 static int matchlen;
260 static unsigned int matchpos;
261 static unsigned int pos;
262 static unsigned int remainder;
263
264 too_flag だけ、static がついてないが、他のソースを grep してもこの変数
265 を使っている箇所はない、単に static の付け忘れだろう。
266
267 これらの変数は、encode() の冒頭 init_slide() で初期化されている・・の
268 かと思ったら違った。slide.c:encode_alloc() で行われている。
269
270 int
271 encode_alloc(method)
272     int method;
273 {
274     if (method == LZHUFF1_METHOD_NUM) { /* Changed N.Watazaki */
275         encode_set = encode_define[0];
276         maxmatch = 60;
277         dicbit = 12;   /* 12 Changed N.Watazaki */
278     } else { /* method LH4(12),LH5(13),LH6(15) */
279         encode_set = encode_define[1];
280         maxmatch = MAXMATCH;
281         if (method == LZHUFF7_METHOD_NUM)
282             dicbit = MAX_DICBIT; /* 16 bits */
283         else if (method == LZHUFF6_METHOD_NUM)
284             dicbit = MAX_DICBIT-1;      /* 15 bits */
285         else /* LH5  LH4 is not used */
286             dicbit = MAX_DICBIT - 3;    /* 13 bits */
287     }
288
289     dicsiz = (((unsigned long)1) << dicbit);
290     txtsiz = dicsiz*2+maxmatch;
291
292     if (hash) return method;
293
294     if (alloc_buf() == NULL) exit(207); /* I don't know this 207. */
295
296     hash = (unsigned int*)malloc(HSHSIZ * sizeof(unsigned int));
297     prev = (unsigned int*)malloc(DICSIZ * sizeof(unsigned int));
298     text = (unsigned char*)malloc(TXTSIZ);
299     too_flag = (unsigned char*)malloc(HSHSIZ);
300
301     if (hash == NULL || prev == NULL || text == NULL || too_flag == NULL)
302         exit(207);
303
304     return method;
305 }
306
307 引数に渡された method (これは、lh1, lh5, lh6, lh7 などを示す)によって、
308 初期化される内容が変わる(encode_alloc()前半部分)。このことから各変数の
309 用途もわかる。
310
311         method  maxmatch     dicbit
312         ----------------------------
313         -lh1-       60         12
314         -lh5-      256         13
315         -lh6-      256         15
316         -lh7-      256         16
317
318 ということらしい。dicbit というのは辞書サイズのbitサイズで、辞書サイズ
319 は 2^dicbit で表されている。lh5 が 8KB(2^13)、lh6 が 32KB(2^15)、lh7 
320 が 64KB(2^16) の辞書サイズを利用すると言うのは予備知識である。maxmatch 
321 というのは、token の最長一致長である。このことも予備知識として詳細には
322 触れない。(ところで、本書では当面、lh5, 6, 7 のことしか言及しない)
323
324 encode_set, encode_define というのがあるが、method によって、Huffman
325 coding の方法を変えていることはちょっと見ればすぐにわかるし、大したこ
326 とではない。以降無視する。
327
328 encode_alloc() の後半では、他の変数の初期化(バッファの割り当て)が行われる。
329
330     dicsiz = (((unsigned long)1) << dicbit);
331
332 dicsiz はそのものずばり辞書サイズである。
333
334     txtsiz = dicsiz*2+maxmatch;
335
336 現時点で txtsiz が何なのかはわからない。
337
338     if (hash) return method;
339
340 hash はこの直後で割り当てられる。つまり、一度割当を行ったら、
341 encode_alloc() は、以降メモリの割当を行わない。ただそれだけだ。
342
343     if (alloc_buf() == NULL) exit(207); /* I don't know this 207. */
344
345 alloc_buf() は、huf.c で定義された関数。このことから Huffman coding の
346 ためのバッファを割り当てているのだろう。ここでは無視。(しかし、207 と
347 いうのは何なのだろう?)
348
349     hash = (unsigned int*)malloc(HSHSIZ * sizeof(unsigned int));
350     prev = (unsigned int*)malloc(DICSIZ * sizeof(unsigned int));
351     text = (unsigned char*)malloc(TXTSIZ);
352     too_flag = (unsigned char*)malloc(HSHSIZ);
353
354     if (hash == NULL || prev == NULL || text == NULL || too_flag == NULL)
355         exit(207);
356
357 hash は、ハッシュ用の何か、HSHSIZ は、固定値で 2^15 である。
358
359 prev は、DICSIZから辞書だろう。要素の型が char でなく int であることに
360 も注目しておく。DICSIZ は dicsiz でも構わないはず。単に「大は小を兼ね
361 る」を実践しているだけであろう、TXTSIZ も同様である。おそらく、一度の
362 実行で複数の圧縮メソッドを使用した場合、そのメソッド毎に領域を割り当て
363 るよりは最大の値をあらかじめ一度だけ割り当てた方が良いと考えたのだろう。
364 しかし、ソースを参照するときは繁雑になるので以降、
365    DICSIZ == dicsiz
366    TXTSIZ == txtsiz
367 であるとする。これ重要。
368
369 text は、現時点では不明
370
371 too_flag も不明
372
373 っとなる。まだ、良く分からないが、以下の図を書いておこう。後で何度も見
374 ることになるだろう。この図はスケールが lh7 の場合を想定しているが。こ
375 のことは大したことではないはずだ。また、too_flag と hash のスケールが
376 一緒だがこれはサイズ(領域のバイト数)が一緒なのではなく、要素数が一緒で
377 あることを示している。ほとんどの場合要素の型の違いというのは処理内容に
378 とって重要なことではないはずだ。
379
380 ----------------------------------------------------------------------------
381
382        0            2^15=32768
383        +-------------+
384   hash |             |
385        +-------------+          dicsiz=2^dicbit
386        +-------------+-------------+                          2*2^dicbit
387   prev |             |             |                           |
388        +-------------+-------------+                           v   txtsiz
389        +-------------+-------------+-------------+-------------+---+
390   text |             |             |             |             |   |
391        +-------------+-------------+-------------+-------------+---+
392                                                                <--->
393                                                                 maxmatch{256}
394   too_flag           2^15
395        +-------------+
396        |             |
397        +-------------+
398 ----------------------------------------------------------------------------
399
400
401 先に示した変数の中でまだ初期化には現れていないものがある。列挙すると
402
403 static unsigned int hval;
404 static int matchlen;
405 static unsigned int matchpos;
406 static unsigned int pos;
407 static unsigned int remainder;
408
409 だ、ざっとソースを眺めると slide.c:insert() という関数に
410         hash[hval] = pos;
411 というのが現れているから、hval は、hash[] の位置を指し、hash には、pos 
412 を格納すると推測される。同様に
413         prev[pos & (dicsiz - 1)] = hash[hval];
414 というのも現れているから pos は、prev[] の位置を指し、prev には、
415 hash[hval] つまり、pos を格納しているようだ。これは少し謎な処理だが、
416 insert() の全貌は短い(というかこれだけ)なので、ちょっと横道にそれて詳
417 細に見てみよう。(現在の解析の趣旨は、変数の用途の概要を予想すること)
418
419 /* 現在の文字列をチェーンに追加する */
420
421 static void insert()
422 {
423     prev[pos & (dicsiz - 1)] = hash[hval];
424     hash[hval] = pos;
425 }
426
427 コメントはまったく意味不明だが、無視して処理内容に着目する。prev[] の
428 インデックス pos & (dicsiz - 1) は、dicsiz が 2^n であることからdicsiz 
429 はビットマスクであることがわかる。例えば仮に dicsiz が 2^8 だと
430 dicsiz - 1 は、
431
432                8 7 6 5 4 3 2 1 0 bit
433       --------------------------
434       dicsiz   1 0 0 0 0 0 0 0 0
435       dicsiz-1   1 1 1 1 1 1 1 1
436
437 である。このすべて 1 が立ったビットマスクと pos を & すると、どのよう
438 な pos の値に対しても pos & (dicsiz - 1) は、prev[] のインデックスの範
439 囲に納まる。もう少し言うと pos が仮にインデックスの最大値+1だった場合、
440 pos & (dicsiz - 1) は、0 になる。これにより次の予想が立つ。
441
442   o pos が、prev[] の位置を指すのではなく、pos & (dicsiz - 1) が
443     prev[]の位置を指す。(pos は、このインデックスの範囲を越える可能性がある)
444   o それに反して、prev[] は環状バッファらしいという予想が立てばやはり
445     pos は、prev のインデックスである。
446
447 prev が環状バッファであると予想が付けば話が早い。pos & (dicsiz-1) は、
448 pos と同義だと解釈可能だからである(prev が環状バッファでなく無限長のバッ
449 ファであると想像しよう)そして、pos & (dicsiz-1) を pos に置き換えて、
450 再度処理内容に着目すると
451
452     prev[pos] = hash[hval];
453     hash[hval] = pos;
454
455 ということから、
456     1. (この関数に来る前に) pos が更新される。(予想)
457     2. prev[pos] に以前の hash[hval] (以前の pos)を格納する
458     3. hash[hval] に新しい pos を書く。
459 といった処理であることが予想される。コメントの「チェーン」もなんとなく
460 納得できる。新たな事実(まだ予想だが)が分かったので、図に書き記そう。
461
462 ----------------------------------------------------------------------------
463        0            2^15=32768
464        +-+---+-------+
465   hash | |pos|...    |
466        +-+---+-------+
467          `-hval
468
469               .-----------.
470              v            |
471        +----+-----+--------------------
472   prev |    |pppos|      |ppos|        . . .
473        +----+-----+--------------------
474             `- ppos      `-pos
475
476   * hash の取り得る値は pos その位置は hval
477   * ppos は以前の pos を示す。pppos はさらに以前の pos を指す。
478   * prev は無限長のバッファ(本当は環状バッファ)
479 ----------------------------------------------------------------------------
480
481 まだ、解析できてない変数が残っている。
482
483 static int matchlen;
484 static unsigned int matchpos;
485 static unsigned int remainder;
486
487 しかしこれらはどうにもパッと見ではわからない。処理内容を追いかけないと
488 だめそうだ。仕方ないので変数名で予想しよう。(幸い前の変数名と違って予
489 想しやすい)以下
490
491 ----------------------------------------------------------------------------
492  * matchlen     一致した文字列長
493  * matchpos     一致した辞書上の位置
494  * remainder    token の残りサイズ
495 ----------------------------------------------------------------------------
496
497 はたして、予想はあっているのか、今はまだ分からない。
498
499 slide.c を見る限りデータ構造は網羅できた。結局分かったのか分からないの
500 か良く分からないが少しずつでも前進はしているはずだ。ここで、再度 
501 encode() の処理を追いかけよう。今度は細部にも着目する。
502
503 前に、encode() のソースには (A) 〜 (H) までの記号を記した。この順番に
504 解析を進めよう。
505
506     /* (A) */
507     init_slide();  
508
509 まあ、初期化である。内容を見てみると
510
511     for (i = 0; i < HSHSIZ; i++) {
512         hash[i] = NIL;
513         too_flag[i] = 0;
514     }
515
516 だけである。NIL というのは、0 であると slide.c で定義されている。普通
517 このような初期値は、通常の値が取り得ない値を示している。NIL が 0 なら 
518 hash[] に格納される pos は 0 にならない可能性がある。まあ、予想ばかり
519 書いても仕方ないので、この関数は終ろう。余談だが、nil は null と同じで。
520 「ない」の意味だが、NULL がC言語ではポインタだから。別のマクロ名にした
521 のかも知れない。いずれにしてもこの程度はマクロにする必要もなかろうとは
522 思うのは、余計なお世話かもしれない。
523
524     /* (B) */
525     remainder = fread_crc(&text[dicsiz], txtsiz-dicsiz, infile);
526     encoded_origsize = remainder;
527     matchlen = THRESHOLD - 1;
528
529     pos = dicsiz;
530
531     if (matchlen > remainder) matchlen = remainder;
532
533 ファイルを読み込み、各変数の初期値を設定している。注目すべきはファイル
534 を読み込んだバッファの位置である。fread_crc() は、crcio.c で定義された
535 汎用関数で、CRC値を計算したり漢字コード変換をしたりを除けば、fread() 
536 と同じである。つまり、ファイルは最初、
537
538   &text[dicsiz] の位置に、txtsiz-dicsiz 分だけ読まれる。
539
540 ことを示す。図示しよう。
541
542 ----------------------------------------------------------------------------
543 < 初期状態 >
544
545                                 dicsiz=2^dicbit               2*2^dicbit
546                                    v                           v   txtsiz
547        +-------------+-------------+-------------+-------------+---+
548   text |             |             |             |             |   |
549        +-------------+-------------+-------------+-------------+---+
550                                    `-pos                       <--->
551                                                                 maxmatch{256}
552
553                                    <------ remainder -------------->
554
555                                    |--- この位置に最初の  ---------|
556                                         データが読まれている
557 ----------------------------------------------------------------------------
558
559 ますます、text[] の用途が不明だが、slide 辞書法の典型的な読み込み処理
560 のことを考えるとある程度予想がつく(それを先に示した方が良いか?)。まあ、
561 ここではフーンっと鼻で納得して済まそう。
562
563 fread_crc() は、読み込んだバッファ長を返す。remainder がその値で、既に
564 図示してある。encoded_origsize は、ソースを見ると、元のファイルのサイ
565 ズを表すためだけの変数のようだ。以降は無視しよう。
566
567 ところで、ファイルサイズが小さい場合図の通りにならないっと考えるかも知
568 れない。その通りなのだが、例外条件は少ない方がソースは理解しやすい。単
569 純な場合だけを考えた方が、あれこれ考えをめぐらす必要がないからだ。なに
570 しろ既に動くソースを見ているのだから、細かいことに目をつぶってもエンバ
571 グすることはないのである。そういうわけで、当面はこの図が唯一の初期状態
572 であると考える。
573
574 (B) の部分はもう少し注目すべき箇所がある。
575
576     matchlen = THRESHOLD - 1;
577
578 matchlen は、「一致した文字列長」であると予想したが THRESHOLD の値は 3
579 (固定値)であるから、matchlen の初期値は 2 だ。いきなり予想がはずれた気
580 がする。予想を立て直そう。2 という謎な数値と match*len* について考える
581 と、冒頭で <len, pt> のペアの len は 2 であることはないと説明した。無
582 意味だからであるが、matchlen の初期値はこの 2 と関連するかもしれない。
583 そこで、matchlen の用途を以下のように予想しなおすことにする。以下のよ
584 うにメモを更新しよう。THRESHOLD(threshold は閾値の意)も一緒に予想した。
585
586 ----------------------------------------------------------------------------
587 * matchlen      最低限一致しなければならない長さ-1
588 * THRESHOLD     最低限一致しなければならない長さ
589 ----------------------------------------------------------------------------
590
591 うーん、本当だろうか?
592
593 (B) の残りの部分を片付けよう
594
595     pos = dicsiz;
596
597     if (matchlen > remainder) matchlen = remainder;
598
599 pos が dicsiz であることからどうやら、pos は、text[] のインデックスら
600 しい。前の予想で pos は、prev[] のインデックスでもあり、hash[] の値で
601 もあると予想したのだが(これはもちろん間違いではなかろうが)。どうやら
602 本当の意味は、処理するテキストの先頭を示しているのではないかとも思える。
603 まあ、ここでは無難に「text[] のインデックス(でもある)」とだけ理解しよう。
604 既に図には書き込んである。
605
606 次の if だが、remainder が matchlen よりも小さい場合の条件だ。また、
607 matchlen の予想が覆されそうな予感がしないでもないが、この if 文は*例外
608 条件*なので無視することにした。都合の悪いことは見ない方が良いのだ。
609
610     /* (C) */
611     hval = ((((text[dicsiz] << 5) ^ text[dicsiz + 1]) << 5) 
612             ^ text[dicsiz + 2]) & (unsigned)(HSHSIZ - 1);
613
614 (C) である。これは難解である。複雑な数式は苦手であるが、じっくり考えよ
615 う。まず求める値は hval である。これは hash[] のインデックスなのだが、
616 このような複雑な式で求まるインデックスなんて想像もつかない。まず、最初
617 のインスピレーションを大事にすることにしよう。冒頭で、(C) の処理は「ハッ
618 シュ値 hval を計算する。」っと苦もなく予想した。そしてこれは間違いでは
619 ないだろう(きっと)。hash[] との関連をここで考えてもわからないから、こ
620 のハッシュ値の計算だけを考えることにしよう。
621
622 式をじっくり見てみる。。。じっくり見てみると以下のことがわかる。
623
624         x(i) = text[dicsiz + i]
625 とすると
626         hval = (( x(0) << 5
627                 ^ x(1)      ) << 5
628                 ^ x(2)             )
629                & (unsigned)(HSHSIZ - 1);
630
631 である。演算子 << は、演算子 ^ よりも優先順位が低いので余計な括弧は省
632 略した。最後の & (unsigned)(HSHSIZ - 1) は、前にも似たような式が出たが
633 これはある範囲の数値(ここでは、0 〜 HSHSIZ{2^15}-1)を抽出するためのビッ
634 トマスクである。ハッシュ関数と言うのはある符号をある集合の符号に写像す
635 る関数であるからこのようなビットマスクは当然必要だし、良く行われる事だ
636 (普通は mod 素数を行うんだけど)。また、hval は、hash[] のインデックス
637 なのだから、写像する集合とは hash[] のインデックスだ。おっ、案外簡単に
638 わかった。x(i) が text[dicsiz + i] で、ハッシュ関数の変数は x(0),
639 x(1), x(2) だから、先頭の 3 バイトの文字列(平文)のハッシュ値を求めてい
640 るわけだ。その他の計算(<< 5 とか ^ とか) は大したことではない。無視し
641 よう。また、続けて (D) の処理も見るが、
642
643     /* (D) */
644     insert();
645
646 insert() は、幸い解読ずみである pos を hash[] に格納する処理だ。
647 予想の段階では、(C) と (D) を別個の処理と考えていたのだがこれは
648 どうやらセットである。
649
650    (C) pos の位置の 3 文字のハッシュ値を計算し
651    (D) hash[ハッシュ値] = pos を行う
652
653 もう少し注意深く検討すると「posの位置の3文字」と、求めた「ハッシュ値」
654 は論理的には = である。
655
656 つまり、(C) (D) の処理は
657
658   hash[文字列] = 位置
659
660 という処理を行っている。ハッシュ値の衝突はここでは考えない。slide 辞書
661 法では、ある文字列に対し以前その文字列が現れたかどうかを検索し、その位
662 置を求める必要があるのだが、この最初の 3 文字に関しては現時点でその用
663 件(位置を求める)を満たす事ができている。ここまでで自ずと encode() の全
664 体像も予想がつきそうな気がする。
665
666 衝突は考えないっとしたが、ちょっと考えたらすぐわかった。prev[] には、
667 以前のハッシュ値で求めた文字列の位置が入っている。つまり、prev[] はハッ
668 シュが衝突したときのためのバッファだ。このハッシュはチェーン法だ。
669
670 例えば、insert() で、
671     prev[pos] = hash[hval];
672     hash[hval] = pos;
673 っと処理をしているのだから
674
675         hash[hval] = pos1
676                       |
677                       v
678                 prev[pos1] = pos2
679                               |
680                               v
681                          prev[pos2] = pos3
682                                 ...
683
684 といった値が入る事になる。ある文字列(のハッシュ値) hval に対して、その
685 辞書上の位置は pos1, pos2, pos3 という候補があるわけだ。実際にどの pos
686 を選ぶかは比較によって行われるのだろう。
687
688 # それにつけても、(C) と (D) の部分を見るだけでもこのソースがちょっと
689 # 汚いことがわかる。もう少し、引数とか戻り値とか考えてくれても良かっ
690 # たはずだ。ハッシュ関数にしても少なくともマクロぐらいにはしようよ。
691
692 (E) 〜 (H) に移ろうこれはループの中身で、処理の本題だ。まずループの脱
693 出条件を見てみると
694
695     while (remainder > 0 && ! unpackable) {
696
697 remainder は、バッファ上に読み込んだ平文の長さであるからこれがなくなる
698 までループすることになる。さらに unpackable というのは、crcio.c の 
699 putcode() でその値を設定している箇所が出て来るのだが、符号化した出力サ
700 イズが元のサイズを越えたときに真になる。つまり、これ以上処理しても圧縮
701 の意味がないとわかったらループを抜けるわけだ。
702
703 では、(E)を見よう。
704
705         /* (E) */
706         lastmatchlen = matchlen;  lastmatchoffset = pos - matchpos - 1;
707         --matchlen;
708
709 ちょっと見ただけではやはりわからない。これらの変数はまだ予想しかしてな
710 いからである。が、わかるだけの情報は書きしるそう。
711
712 ----------------------------------------------------------------------------
713 * lastmatchlen    以前の matchlen の値 (変数名から)
714 * lastmatchoffset 以前マッチした位置 (変数名から)
715 ----------------------------------------------------------------------------
716
717 以前の値をlast〜に退避し、新たな値を設定する準備をしているわけだ。そし
718 て、「新たな値の設定」は、--matchlen で早速行われている。しかし、「マッ
719 チした長さ」をまだ何もしてないのに -1 するというのはいったいどういうこ
720 とだろう? matchlen はループの頭で 2 に設定されている。これが 1 になっ
721 た。本当の初期値は 1 なのか?
722
723 ----------------------------------------------------------------------------
724 < 各変数の初期値 >
725
726   matchlen = 1
727   matchpos = 0
728   pos = dicsiz
729
730   lastmatchlen = 2
731   lastmatchoffset = dicsiz - 1  (pos - matchpos - 1)
732 ----------------------------------------------------------------------------
733
734 この (E) はまた後で見る事になるだろう。
735
736 (F) (G) である。また、その直後には以前にも見た境界条件がある。
737
738         /* (F) */    /* (G) */
739         get_next();  match_insert();
740         if (matchlen > remainder) matchlen = remainder;
741
742 if 文 は無視して関数の中身だけを追いかけてみよう。まず、get_next() こ
743 れは 次の token を取ってくる処理だと予想してある。はたしてどうだろうか?
744
745 static void get_next()
746 {
747     remainder--;
748     if (++pos >= txtsiz - maxmatch) {
749         update();
750     }
751     hval = ((hval << 5) ^ text[pos + 2]) & (unsigned)(HSHSIZ - 1);
752 }
753
754 remainder を消費し、pos を進めている。予想通りだ。ひとまず if の条件は
755 無視すると、直後で hash 値を求め直している。このハッシュ関数は、以前のハッ
756 シュ値を利用しているが、これは pos が以前より + 1 されていることを考え
757 ると関連が見えて来る。以前のhash関数を pos の関数として書き直すと
758
759         x(pos+i) = text[pos + i]
760
761         hval(pos) = (( x(pos+0) << 5
762                      ^ x(pos+1)      ) << 5
763                      ^ x(pos+2)             )
764                     & (unsigned)(HSHSIZ - 1);
765
766 であり、また、今度のハッシュ関数は、
767
768         hval(pos+1) = ( hval(pos) << 5
769                        ^ x(pos+1 + 2)  )
770                       & (unsigned)(HSHSIZ - 1);
771
772 だ、繁雑なので & (HSHSIZE-1) を外すと、
773
774         hval(pos+1) = (( x(pos+0) << 5
775                        ^ x(pos+1)      ) << 5
776                        ^ x(pos+2)             ) << 5
777                        ^ x(pos+3)
778
779 っとなる。この次 get_next() が呼び出されれば、
780
781         hval(pos+2) = ((( x(pos+0) << 5
782                         ^ x(pos+1)      ) << 5
783                         ^ x(pos+2)             ) << 5
784                         ^ x(pos+3)                    ) << 5
785                         ^ x(pos+4)
786
787 である。順にハッシュ値を求める文字列長を増やしている。とにかく、
788 get_next() は、pos を進め、remainder を縮め、新たな(以前より1文字長い) 
789 文字列のハッシュ値 hval を求める関数のようだ。
790
791 しかし、いつまでも hash 値の元となる文字列を伸ばしてもしょうがないだろ
792 う。hval はどこかでまたリセットされるはずだ。っと思ってソースを探して
793 みたがそのような箇所は見当たらない。なぜだろう?考えてみる・・・最初は
794 わからなかったが数式をよく見てみたらわかった。<< 5 が鍵だ、hval(pos+2) 
795 の式を見ると x(pos+0) は、<< 5 が、4回も行われているつまり、20ビットの
796 シフトだ。hval(pos+3) なら、25ビット、hval(pos+4) なら 30 ビットのシフ
797 トだ。さすがにこれだけシフトすれば、x(pos+0)の情報は消えてもいいだろう。
798
799 実際、hval は何文字分の情報を持つのだろう?hval は、unsigned int で、
800 普通 32 bit であるから、6.4 文字分だろうか?いや、実際にはハッシュ値の
801 計算時にHSHSIZ (15bit) でマスクをかけているから 15 bit の情報しか持た
802 ない。つまり、3文字だ。ビット計算は苦手なので図示して確認しよう。
803
804                  15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
805                 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
806           hval  |--|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
807                 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
808
809 最初の hval(0) は、x(0), x(1), x(2) に対して、
810
811                     <---  5 -----> <---  5 -----> <---  5 ----->
812                 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
813     x(0) <<10    --  x  x  x  x  x
814                 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
815     x(1) << 5    --        x  x  x  x  x  x  x  x
816                 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
817     x(2)         --                       x  x  x  x  x  x  x  x
818                 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
819
820 の排他的論理和である。hval(0) の時点で x(0) の情報は 5 ビット残ってい
821 るが hval(1) になれば消えてしまうのは自明である。どうにも最初の文字に
822 関しては 5 ビットしか情報を使用しないと言うのが気持悪いのだが、15 bit 
823 サイズの変数 hval には、過去 3 文字分の情報しか保持されないのは間違い
824 ない。get_next() の処理を見れば、位置 pos に対して、hval は常に pos,
825 pos+1, pos+2 の情報しか持たないわけだ。これは重要だ。メモしよう
826
827 ----------------------------------------------------------------------------
828  * hval  hash[]のインデックス。現在位置 pos に対して、
829          text[pos], text[pos+1], text[pos+2] のハッシュ値で、論理的には
830              hval == text[pos] + text[pos+1] + text[pos+2]
831          と同義
832 ----------------------------------------------------------------------------
833
834 ところで、前回、hval の計算とinsert() はセットだと言った。今回はどうだ
835 ろう?次の match_insert() を見てみる。
836
837 static void match_insert()
838 {
839     ... 省略 ...
840
841     prev[pos & (dicsiz - 1)] = hash[hval];
842     hash[hval] = pos;
843 }
844
845 ・・・強敵であった。強敵すぎたので逃げて、最後の2 行だけに着目した。こ
846 れは、insert()と同じだ。予想は当たっている。get_next() で hval を更新
847 した後は、この match_insert() で、prev[] と hash[] を更新するわけだ。
848 そして、match_insert() の省略した部分は、どうやら matchpos, matchlen,
849 too_flag を更新しているだけのようだ。これが本当なら match_insert()で、
850 insert()の処理をせず、関数を分けるかした方が良さそうだ。(真偽の程は詳
851 細を見てからになる)
852
853 おもむろに後続の処理 (H) を見ると、
854
855         /* (H) */
856         if (matchlen > lastmatchlen || lastmatchlen < THRESHOLD) {
857
858 これが真なら「見つからなかった状態」と予想した(なぜだろ?)。そして、
859 lastmatchlen は初期状態では 2 である。予想した条件は逆か? matchlen ま
860 わりは予想ばかりで進めすぎた。そしてどうやら match_insert() を読みとか
861 なければこの先も分からずじまいになりそうだ。
862
863 このまま match_insert() を詳細に解析する事にしよう。match_insert()
864 をすべて再掲する。
865
866 /* 現在の文字列と最長一致する文字列を検索し、チェーンに追加する */
867
868 static void match_insert()
869 {
870     unsigned int scan_pos, scan_end, len;
871     unsigned char *a, *b;
872     unsigned int chain, off, h, max;
873
874     max = maxmatch; /* MAXMATCH; */
875     if (matchlen < THRESHOLD - 1) matchlen = THRESHOLD - 1;
876     matchpos = pos;
877
878     off = 0;
879     for (h = hval; too_flag[h] && off < maxmatch - THRESHOLD; ) {
880         h = ((h << 5) ^ text[pos + (++off) + 2]) & (unsigned)(HSHSIZ - 1);
881     }
882     if (off == maxmatch - THRESHOLD) off = 0;
883     for (;;) {
884         chain = 0;
885         scan_pos = hash[h];
886         scan_end = (pos > dicsiz) ? pos + off - dicsiz : off;
887         while (scan_pos > scan_end) {
888             chain++;
889
890             if (text[scan_pos + matchlen - off] == text[pos + matchlen]) {
891                 {
892                     a = text + scan_pos - off;  b = text + pos;
893                     for (len = 0; len < max && *a++ == *b++; len++);
894                 }
895
896                 if (len > matchlen) {
897                     matchpos = scan_pos - off;
898                     if ((matchlen = len) == max) {
899                         break;
900                     }
901                 }
902             }
903             scan_pos = prev[scan_pos & (dicsiz - 1)];
904         }
905
906         if (chain >= LIMIT)
907             too_flag[h] = 1;
908
909         if (matchlen > off + 2 || off == 0)
910             break;
911         max = off + 2;
912         off = 0;
913         h = hval;
914     }
915     prev[pos & (dicsiz - 1)] = hash[hval];
916     hash[hval] = pos;
917 }
918
919 まず、初期化部分の前半
920
921     max = maxmatch; /* MAXMATCH; */
922     if (matchlen < THRESHOLD - 1) matchlen = THRESHOLD - 1;
923     matchpos = pos;
924
925     off = 0;
926
927 maxmatch は、固定値で 256 だ、だから max も 256
928 2行目の if 文は、これまでしつこいくらいに出て来た条件に似ているが、今
929 回は条件を満たすらしい。これまでは、
930
931     if (matchlen > remainder) matchlen = remainder;
932
933 という条件だった。そして今回は、
934
935     if (matchlen < THRESHOLD - 1) matchlen = THRESHOLD - 1;
936
937 だから、全体的に matchlen の値は、
938
939     THRESHOLD-1 <= matchlen <= remainder
940
941 つまり、
942
943               2 <= matchlen <= バッファに残ったテキスト長
944
945 の範囲に納められるようだ。ここでは、matchlen は下限値を下回るので2 に
946 設定される。次に matchpos, off が初期化され。以下の図の状態になる。
947 (pos, remainder は、get_next() で更新されていることに注意)
948
949 ----------------------------------------------------------------------------
950
951                                 dicsiz=2^dicbit               2*2^dicbit
952                                    v                           v   txtsiz
953        +-------------+-------------+-------------+-------------+---+
954   text |             |             |             |             |   |
955        +-------------+-------------+-------------+-------------+---+
956                                      `-pos(=dicsiz+1)          <--->
957                                        matchpos(=pos)           maxmatch{256}
958                                        off(=0)
959
960                                      <------ remainder ------------>
961
962                                    |--- この位置に最初の  ---------|
963                                         データが読まれている
964 ----------------------------------------------------------------------------
965
966 初期化部分の後半
967
968     for (h = hval; too_flag[h] && off < maxmatch - THRESHOLD; ) {
969         h = ((h << 5) ^ text[pos + (++off) + 2]) & (unsigned)(HSHSIZ - 1);
970     }
971     if (off == maxmatch - THRESHOLD) off = 0;
972
973 h は、too_flag[] が今のところすべて0だから hval だ。(too_flag は、h つ
974 まり hval をインデックスに取るらしい。hash[] と同じだ。再掲はしないが
975 メモに書き加えておこう)
976
977 off は、pos の位置からのオフセットのようだ(h を更新する for 文の中身か
978 ら)。図もその位置に書いた。最後の if 文は off が上限に達した場合に0 に
979 再初期化している。よくわからないので無視しよう。for 文の中身からh や 
980 off の用途はどうも先読みしたハッシュ値とその先読みの位置なのではないか
981 と想像する。too_flag[] の状態によって先読みすべき値が変わるのだろうか?
982
983 とにかく処理の本題に入る事にしよう。まず、この関数に現れる局所変数を列
984 挙しておこう
985
986     unsigned int scan_pos, scan_end, len;
987     unsigned char *a, *b;
988     unsigned int chain, off, h, max;
989
990 off, h, max はすでに出て来たので残りは
991
992   scan_pos, scan_end, len, a, b, chain
993
994 だ、これだけの変数の意味を解読しなくてはならない。変数は状態を表すから、
995 その数が多いと言うのはそれだけ複雑な処理だということだ。めげる。
996
997 この関数のメインとなるループの中をざっと眺めてみるさらに内部にループが
998 ある。ひとまず、二重ループの中身を省略しよう。
999
1000     for (;;) {
1001         chain = 0;
1002         scan_pos = hash[h];
1003         scan_end = (pos > dicsiz) ? pos + off - dicsiz : off;
1004
1005         while (scan_pos > scan_end) {
1006             chain++;
1007             ... 略 ...
1008         }
1009
1010         if (chain >= LIMIT)
1011             too_flag[h] = 1;
1012
1013         if (matchlen > off + 2 || off == 0)
1014             break;
1015         max = off + 2;
1016         off = 0;
1017         h = hval;
1018     }
1019
1020 まず、前半部分から
1021
1022         chain = 0;
1023         scan_pos = hash[h];
1024         scan_end = (pos > dicsiz) ? pos + off - dicsiz : off;
1025
1026 chain, scan_pos, scan_end はすべて while ループに渡されるべき変数だ。
1027 さらに、while の後には、scan_pos, scan_end は現れないから(仮に while 
1028 ループが1つの関数だったとすると)これらは while ループ部の引数(入力)だ。
1029 この2つの変数はどうやりくりしようとも、while ループ部内の状態しか表さ
1030 ないので、ここでは無視しよう。
1031
1032 while ループの後を見てみると
1033
1034         if (chain >= LIMIT)
1035             too_flag[h] = 1;
1036
1037         if (matchlen > off + 2 || off == 0)
1038             break;
1039         max = off + 2;
1040         off = 0;
1041         h = hval;
1042
1043 chain が LIMITを越えた場合、too_flag[h] = 1 としている。chain は、ざっ
1044 と見て、while ループのカウンタらしいが、LIMIT は 0x100 だ。どうにも例
1045 外条件っぽい(LIMITという名前や数値がそう思わせる)のでここでは無視しよ
1046 う。while ループが 256以上回る可能性がある点だけ心にとどめておこう。
1047
1048 次の条件では、matchlen と off が条件判断されている。ということはこのど
1049 ちらか、あるいは両方は while ループの返り値(出力)だ。ざっと 
1050 match_insert()全体を見てみると off は最初とこの直後でしか更新されない
1051 らしい。つまり、while ループ部の返り値はmatchlen の方だ。
1052 この条件は for () ループの脱出条件でもある。心にとどめて、次に進む。
1053
1054         max = off + 2;
1055         off = 0;
1056         h = hval;
1057
1058 ふむ。よくわからない。しかし注目すべき点はある。off はここで、0 になる
1059 がこれ以降は off の値は変わらない。つまり、off は最初は何らかの値で 
1060 while ループ部に渡されるが、その次からは、0 だ。この for ループが何度
1061 回ろうとも 0 だ。h も同じで最初は何らかの値を持つが、2回目のループ以降、
1062 h は hval だ。max は、off を 0 にする直前に更新しているから、h や off 
1063 と事なり、3つの状態を持つ、すなわち。maxmatch, off+2, 2 だ。
1064
1065 いや、脱出条件を見てみると off == 0 なら break とある。つまり、この 
1066 for ループはどんなに頑張っても2回しか回らないらしい。やっぱり max も 2 
1067 つの状態しか持たないようだ。
1068
1069 これで、1 回目、2回目に while ループ部に入る直前の状態が書ける。この関
1070 数 match_insert() は、while ループ部を1回か2回実行する処理と言うわけだ。
1071
1072 ここで無視していた。while ループ部の入力となる scan_pos, scan_end
1073 もそれぞれどのような状態になるか書いておく
1074
1075 ----------------------------------------------------------------------------
1076 < 1回目 >
1077    h = 何か
1078    off = 何か
1079    max = maxmatch
1080
1081    scan_pos = hash[h]
1082    scan_end = pos + off - dicsiz  (あるいは、off)
1083
1084    matchlen = 2
1085    matchpos = pos
1086 < 2回目 >
1087    h = hval
1088    off = 0
1089    max = 前の off + 2
1090
1091    scan_pos = hash[hval]
1092    scan_end = pos - dicsiz  (あるいは、0)
1093
1094    matchlen = ?
1095    matchpos = ?
1096 ----------------------------------------------------------------------------
1097
1098 上記は一般化した場合だが、今回(初回)の場合、h や off の値は、hval であ
1099 り、0 だった。2回目ループのときの状態と同じである。2回のループの違いは 
1100 max の値がmatchpos であるか off+2 (すなわち2)であるかの違いしかないようだ。
1101
1102 ここは、条件を少なくするためにこの場合だけにしぼって処理を考えよう。
1103 while ループの2回の呼び出しを行う際の状態は以下の通りに書き直せる。
1104
1105 ----------------------------------------------------------------------------
1106 < 1回目 >
1107    h = hval
1108    off = 0
1109    max = maxmatch
1110
1111    scan_pos = hash[hval]
1112    scan_end = pos - dicsiz  (あるいは、0)
1113
1114    matchlen = 2
1115    matchpos = pos
1116 < 2回目 >
1117    h = hval
1118    off = 0
1119    max = 2
1120
1121    scan_pos = hash[hval]
1122    scan_end = pos - dicsiz  (あるいは、0)
1123
1124    matchlen = ?
1125    matchpos = ?
1126 ----------------------------------------------------------------------------
1127
1128 うーん、まだ、すっきりしない。何がすっきりしないかというと scan_end の
1129 値だ。これが何を意味するのかがよくわからない。scan_pos は、わかるのか?
1130 というと、わかる。hash[hval]だから現在の文字列と同じ文字列の辞書上の位
1131 置だ。さらに、現時点では get_next() で、hval を更新してから insert() 
1132 を行っていないので、hash[hval] には何も入っていない。すなわち 0 だ。
1133
1134         scan_end = (pos > dicsiz) ? pos + off - dicsiz : off;
1135
1136 を考えよう。off は、0 だから
1137
1138         scan_end = (pos > dicsiz) ? pos - dicsiz : 0;
1139
1140 なわけだ。さらに、posは現時点で dicbit+1 であるから、1 だ。図に書こう。
1141
1142 ----------------------------------------------------------------------------
1143
1144                                 dicsiz=2^dicbit               2*2^dicbit
1145                                    v                           v   txtsiz
1146        +-------------+-------------+-------------+-------------+---+
1147   text |             |             |             |             |   |
1148        +-------------+-------------+-------------+-------------+---+
1149        ^ ^                           `-pos(=dicsiz+1)
1150        | |
1151        | scan_end はこの辺(1)
1152        scan_pos はこの辺(0)
1153
1154    h = hval
1155    off = 0
1156    max = 2
1157
1158 ----------------------------------------------------------------------------
1159
1160 ついに、text[] バッファの左半分に指しかかる。これが何なのかは現時点で
1161 は明確に書いてなかったが予想するとこの左半分はズバリ辞書だ。言い切って
1162 やろう。今まで辞書らしい(dicsizのサイズを持つ)バッファは hash[] や 
1163 prev[] があったが、hash[], prev[] の用途はもう明確である。辞書となり得
1164 るバッファはもうこの text[] しかないのだ。
1165
1166 さらに、左半分に限らずこの text[] 全体が辞書であろうと予想する。これは
1167 ただの勘だが text[] は環状バッファなのではなかろうかと考えている。
1168
1169 # 最初の方で prev[] が辞書だと予想したが間違った予想をしていたことにこ
1170 # の時点で気づいた。prev[] が辞書と同じサイズを持つ理由はまだよくわか
1171 # らない。
1172
1173 この時点ではまだ scan_pos や scan_end の真の意味はわからない。off のこ
1174 とを無視しているから予想も立ちにくいが、ひとまず初期状態がどういったも
1175 のかはわかったのでこのまま、while ループ部を見てみたいと思う。
1176
1177         while (scan_pos > scan_end) {
1178             chain++;
1179
1180             if (text[scan_pos + matchlen - off] == text[pos + matchlen]) {
1181                 {
1182                     a = text + scan_pos - off;  b = text + pos;
1183                     for (len = 0; len < max && *a++ == *b++; len++);
1184                 }
1185
1186                 if (len > matchlen) {
1187                     matchpos = scan_pos - off;
1188                     if ((matchlen = len) == max) {
1189                         break;
1190                     }
1191                 }
1192             }
1193             scan_pos = prev[scan_pos & (dicsiz - 1)];
1194         }
1195
1196 まず、if 文の条件を満たさない場合だけを考える。
1197
1198         while (scan_pos > scan_end) {
1199             chain++;
1200
1201             if (text[scan_pos + matchlen - off] == text[pos + matchlen]) {
1202                 ...
1203             }
1204             scan_pos = prev[scan_pos & (dicsiz - 1)];
1205         }
1206
1207
1208 offは 0 なので、text[scan_pos + matchlen] != text[pos + matchlen] とい
1209 う条件の場合を想定するわけだが、
1210
1211 text[scan_pos + matchlen]
1212
1213
1214
1215 text[pos + matchlen]
1216
1217 を比べている
1218
1219 text[scan_pos]  辞書上の文字列の*先頭*
1220 text[pos]       現在の文字列の*先頭*
1221
1222 を比べないのは matchlen が前に予想した「最低限一致しなければならない長さ-1」
1223 だからであろう。現時点で、matchlen が 2 だから
1224
1225 text[scan_pos + 0] == text[pos + 0]
1226 text[scan_pos + 1] == text[pos + 1]
1227
1228 であったとしても、
1229
1230 text[scan_pos + 2] != text[pos + 2]
1231
1232 であれば、「最低限一致しなければならない長さ」という条件を満たさないの
1233 である。なので matchlen の位置から先に比較して無駄な比較をしないように
1234 している。後でちゃんとした比較の処理が出て来るだろう。このような処理は
1235 処理としては効率が良いのだが、ことソース理解と言う点では冗長である。わ
1236 かりにくいのだ。仕方ないのだけど。
1237
1238 # matchlen の意味の予想はどうやら当たっているようだ。matchlen は最短一
1239 # 致長で、minmatchlen っと名付けても良さそうな変数だ。
1240
1241 そして、比較に失敗したら scan_pos を更新する。
1242
1243             scan_pos = prev[scan_pos & (dicsiz - 1)];
1244
1245 ハッシュのチェーンをたどっている、つまり次の候補を辞書から取り出してい
1246 るわけだ。ここまでで、while ループの処理内容の想像はついた。このループ
1247 は辞書から(最長)一致する文字列を探しているのだろう。
1248
1249 順番が前後したが、while ループの脱出条件を見てみる
1250
1251         while (scan_pos > scan_end) {
1252
1253 これはどういうことだろう? scan_pos は、ハッシュのチェーンをたどって同
1254 じハッシュ値を持つ文字列の位置を探す、この値はだんだんと小さくなって行
1255 くものなのだろうか?
1256 その通りである。hash[] への格納はファイルから取って来た文字列を順に格
1257 納して行くのでチェーンの奥には、より前の方の位置が書かれているはずだ。
1258 逆にチェーンの浅い部分にはより現在位置に近い位置が書かれているのだろ
1259 う。では、その境界 scan_end はどうやってわかるのだろうか?これは後でま
1260 た検証しよう。
1261
1262 では、処理の本質 if 文の中を見る事にしよう
1263
1264             if (text[scan_pos + matchlen - off] == text[pos + matchlen]) {
1265                 {
1266                     a = text + scan_pos - off;  b = text + pos;
1267                     for (len = 0; len < max && *a++ == *b++; len++);
1268                 }
1269
1270                 if (len > matchlen) {
1271                     matchpos = scan_pos - off;
1272                     if ((matchlen = len) == max) {
1273                         break;
1274                     }
1275                 }
1276             }
1277
1278 最初の意味もなくブロックになっている部分を見る、
1279
1280                 {
1281                     a = text + scan_pos - off;  b = text + pos;
1282                     for (len = 0; len < max && *a++ == *b++; len++);
1283                 }
1284
1285 この処理では a, b といういかにも局所な名前の変数が使われている。これは、
1286 本当にこのブロック内で局所的なもののようだ。ならば定義位置もこのブロッ
1287 ク内にして本当に局所的にして欲しかった。
1288
1289 さらに、この処理は単に文字列 a, b を比較しているだけのようだ。memcmp() 
1290 ではまずいのかと言うとここで求めているものが「どこまで一致したか(len)」
1291 のようなので、memcmp() では役不足だ。
1292
1293 その次の処理、
1294
1295                 if (len > matchlen) {
1296                     matchpos = scan_pos - off;
1297                     if ((matchlen = len) == max) {
1298                         break;
1299                     }
1300                 }
1301
1302 で、matchlen (最低一致長)より大きい場合に条件を満たす。条件を満たさな
1303 ければ、scan_pos を更新し、次のループに移る。では、条件を満たす場合を
1304 見てみよう。まず最短一致長の一致条件を満たした場合、matchpos と 
1305 matchlen を更新する。
1306
1307 matchpos はマッチした位置、
1308 matchlen はマッチした長さ
1309
1310 で、matchlen が max なら最長一致長に達しているので、これ以上探索はしな
1311 い。matchlen は最短一致長でありながら、一致長でもある変数のようだ。
1312 (どうやら以前の2つの予想はどちらも当たっていた模様)
1313
1314 とにかく while ループ部の出力は、この matchpos と matchlen のようだ。
1315 前に書いた通りこのループは「最長一致文字列を求める処理」だ。
1316
1317 match_insert() の全体をもう一度見てみよう。ただし、以下の書き換えを行う。
1318
1319 o while ループ部は search_dict(pos, scan_pos, scan_end, max) という関数
1320   に置き換えたものとする。
1321
1322 o 末尾の insert() と同等の処理を行っている部分も insert() の呼び出しに
1323   すり替えよう。(match_insert() 関数の中で insert() 処理を本当に行うべ
1324   きものなのかどうかが疑問だが)
1325
1326 o chain という変数にかかわる処理も隠した(search_dict内で行う)
1327
1328 o for ループは、2回しかまわらないので、2 度の search_dict() の呼び出し
1329   に書き換える
1330
1331 static void match_insert()
1332 {
1333     unsigned int off, h;
1334     unsigned int scan_end;
1335
1336     if (matchlen < THRESHOLD - 1) matchlen = THRESHOLD - 1;
1337     matchpos = pos;
1338
1339     off = 0;
1340     for (h = hval; too_flag[h] && off < maxmatch - THRESHOLD; ) {
1341         h = ((h << 5) ^ text[pos + (++off) + 2]) & (unsigned)(HSHSIZ - 1);
1342     }
1343     if (off == maxmatch - THRESHOLD)
1344         off = 0;
1345
1346     scan_end = (pos > dicsiz) ? pos + off - dicsiz : off;
1347     search_dict(pos, hash[h], scan_end, maxmatch);
1348
1349     if (off > 0 && matchlen <= off + 2) {
1350       off = 0;
1351
1352       scan_end = (pos > dicsiz) ? pos + off - dicsiz : off;
1353       search_dict(pos, hash[hval], scan_end, off+2);
1354     }
1355
1356     insert();
1357 }
1358
1359 だいぶすっきりした(が、まだ繁雑だ)。まだ、off にかかわる部分がよく分か
1360 らないが、ひとまず良いだろう。この関数の解析はこれで終って良いだろうか。
1361
1362 いや、良くない。肝心の match_insert() の出力がよくわからない。この関数
1363 は「最長一致文字列を探し、hash を更新する処理」(くどいようだが、hashを
1364 更新するは余計に思う)なのだろうが、最長一致文字列が見つからなかったと
1365 いうのはどう判断されるのだろう?
1366
1367 まず、search_dict() で見つからなかった場合、matchlen は更新されない
1368 (matchpos は、pos になる)。そして、おそらく 2 度目の search_dict() の
1369 呼び出しが行われる。が、too_flag[] というので、判断できそうな気もする
1370 がこれはむしろハッシュのチェーンをたどりすぎるのを止めるためのフラグで
1371 あるように思える。
1372
1373 2度目の search_dict()で、max の値が変わるのが鍵だろうか?。今回の場合、
1374 max は 256 から 2 になる。最長一致長として 2 が限界値になると、
1375 search_dict() の動作は変わるだろうか?いや、やはり変わらない。どうにも
1376 この関数だけでは見つかったか見つからなかったかという判断はできないよう
1377 だ。(本当はわかっているはずなのにその情報を直接外に持ち出していない)
1378
1379 気持悪いがやはりこの関数の解析を終え、次に移る事にしよう。
1380
1381 (H) である。以前、
1382
1383 (H) matchlen > lastmatchlen || lastmatchlen < THRESHOLD なら
1384
1385 (H.1) output() する。(マッチしなかったらそのまま出力しているのだろう。たぶん)
1386 (H.2) そうでなければ(マッチしたなら)、output()し、何かいろいろする。
1387
1388 っと予想した部分だ。今や match_insert() は、解析済みだからこれの真偽が
1389 わかるか?というとやっぱり、わからない。ただ、
1390         matchlen > lastmatchlen
1391 というのは、辞書から文字列が見つかった場合の条件になりそうだから、やはり
1392 これは予想が逆だろうか?とにかく、比較的簡単そうな、(H.1) から見よう。
1393
1394         if (matchlen > lastmatchlen || lastmatchlen < THRESHOLD) {
1395             /* (H.1) */
1396             encode_set.output(text[pos - 1], 0);
1397             count++;
1398         } else {
1399
1400 どうも、文字 text[pos-1] を出力しているだけのように思える。文字の出力
1401 は、slide 辞書法では「辞書から見つからなかった場合」を意味するから、や
1402 はり最初の予想はあってそうなのだが・・・仕方ないので、output()の処理を
1403 覗いて見よう。これは、lh5, 6, 7 の場合、huf.c:output_st1(c, p) である。
1404 現時点で処理の内容を見てもわけがわからないが、結論から言うと第一引数 c 
1405 は、文字で、第二引数 p は、位置である。冒頭の decode 処理で、文字 c は
1406 長さも兼ねていると説明済みなので、(そして、text[pos-1] には現時点で文
1407 字そのものしか書かれていない)これはやはり文字を出力している処理だ。つ
1408 まり「見つからなかった場合」の処理だ。
1409
1410 なぜ、pos-1 なのだろう?確かに Huffman coding に文字を渡すのはこれが初
1411 めてで、現在 pos の位置はバッファの1文字進んだ位置にある。pos-1 は出力
1412 しなければならないのは当然だ。ということは pos は常に「未出力文字の位
1413 置 + 1」なのかもしれない。
1414
1415 次の count++ を見る。count はどうやらこの関数の変数ではないらしい、困っ
1416 た事に局所変数の名前っぽいがグローバル変数だ。これはよろしくない。ちょっ
1417 と grep しただけでは、他にどこでこの変数を使っているのかわからなかった。
1418 まあ、今 1 文字を渡した所なので、入力文字数なのだと仮定しておこう。こ
1419 の変数が大勢に影響を与える事はないだろうからこれ以上は見ないと言うのも
1420 アリだ。
1421
1422 # その後、dhuf.c:decode_p_dyn() でのみ count を使用している事がわかった。
1423
1424 次は (H.2) である。これがまた難解なのだがゆっくり片付けよう。
1425
1426         } else {
1427             /* (H.2) */
1428             encode_set.output(lastmatchlen + (UCHAR_MAX + 1 - THRESHOLD),
1429                (lastmatchoffset) & (dicsiz-1) );
1430             --lastmatchlen;
1431
1432             while (--lastmatchlen > 0) {
1433                 get_next();  insert();
1434                 count++;
1435             }
1436             get_next();
1437             matchlen = THRESHOLD - 1;
1438             match_insert();
1439             if (matchlen > remainder) matchlen = remainder;
1440         }
1441
1442 まず、output() に渡している引数は、それぞれ「長さ」と「位置」であろう
1443 ことは予想済みだ。さらに UCHAR_MAX{255} + 1 - THRESHOLD{3} だから
1444
1445  長さ  lastmatchlen + 253
1446  位置  lastmatchoffset & (dicsiz-1)
1447
1448 となっている。冒頭の decode() の解析で、長さは 253 を足す事は確認済み
1449 だ(-lhs- の場合 254 を足すという動作が、encoding 部分では考慮され
1450 てないのは、-lhs- の encoding 機能がないからだ)。ところで、一致長 
1451 lastmatchlen は 3 以上で初めて 255 を越えることができる。以前予想した、
1452 THRESHOLD の意味「最低限一致しなければならない長さ」はあっているらしい。
1453
1454 もう一点、注意しなくてはならないのは、出力しているのが lastmatchlen と 
1455 lastmatchoffset である。これらは、match_insert() のときには更新してい
1456 ない(last〜の更新は次のループの先頭 (E) で行われる)。先程 (H.1) のとき
1457 も書き出していたのは、text[pos-1] であった。pos 位置は一歩先読みした位
1458 置を指すらしい。このような処理を行う場合、最後に調整が必要なはずだ(で
1459 ないと最後の文字が出力されない)。その調整はどこで行われるのだろう?
1460
1461 さて、後続の処理だが、<長さ、位置>のペアを出力した後は、
1462
1463             --lastmatchlen;
1464
1465             while (--lastmatchlen > 0) {
1466                 get_next();  insert();
1467                 count++;
1468             }
1469
1470 という処理を行っている。get_next() は、pos を進める処理、insert() は辞
1471 書に登録する処理だから、これは文字列を読み飛ばしている処理だ。確かに 
1472 lastmatchlen 分の情報は出力済みだから、これは納得である。lastmatchlen 
1473 を 1 余分に引いているのが謎だがこれは pos が一歩先に進んでいるからであ
1474 ろうと予想する。つまり、この後は pos の位置はまた「現在位置」に戻る。
1475 なるほど、先程調整が必要と書いたがここで行われているらしい。細部は不明
1476 だが少なくとも辞書に文字列が見つかった場合は最後まで出力されるようだ。
1477
1478 次に進もう
1479
1480             get_next();
1481             matchlen = THRESHOLD - 1;
1482             match_insert();
1483             if (matchlen > remainder) matchlen = remainder;
1484
1485 せっかく pos が現在の位置に戻ったのに、get_next() でまた先読みされた。
1486 うーむ。そして、matchlen は初期化される。一致情報はすでに出力済みだか
1487 らこれはうなずける。そして、match_insert() が呼ばれる。この時点で再度
1488 辞書が検索される。pos はまた1文字進んでいるのだから、これは先程(初期状
1489 態)のmatch_insert() と大差ない処理だ。(その直後のif文は境界条件なので
1490 無視)
1491
1492 そうして、また次のループに移る。このとき続けざま get_next(),
1493 match_insert() が行われる。どうやら pos は次のループからは、 2 文字文
1494 先に進んでしまうようだ。なぜだろう?
1495
1496 # 後でわかった事だが、while (--lastmatchlen > 0) のループ回数を読み間
1497 # 違えた。例えば、lastmatchlen が 1 なら、この while ループ内では 
1498 # get_next() は1回も呼ばれない。
1499
1500 どうにもソースを見ただけで解読するには、このあたりが限界のようだ。どう
1501 しても細部がわからないし、事実が見えないから予想の積み重ねがたまって不
1502 安になる。
1503
1504 実は、もう少しマメに図を起こして読み進んで行けばもっとわかることがあっ
1505 ただろうと思うのだが、それは面倒だし、間違える可能性がある(ここまでで
1506 何度か痛い思いをした)。以降は、いくつかのデータを実際に圧縮させその動
1507 きをデバッガで追うことで、これまでの解析結果を検証してみよう。
1508
1509 ・・・っと、その前に、ここまでですべての関数を網羅してしまったと思って
1510 いたのだが、一つ忘れていたものがあった。update() だ。この関数は、
1511 get_next() で呼び出されていたのだが、以前は無視していた。先にここを見
1512 ておこう。
1513
1514 まず、get_next() を再掲する。
1515
1516 static void get_next()
1517 {
1518     remainder--;
1519     if (++pos >= txtsiz - maxmatch) {
1520         update();
1521     }
1522     hval = ((hval << 5) ^ text[pos + 2]) & (unsigned)(HSHSIZ - 1);
1523 }
1524
1525 remainder と pos を進めた後、pos が txtsiz - maxmatch に達してしまった
1526 場合(pos == 2 * 2^dicbit の場合)に呼び出されるようだ。つまり、以下の図
1527 の状態だ。これが、update() を呼び出す時の初期状態だ。
1528
1529 ----------------------------------------------------------------------------
1530
1531                                 dicsiz=2^dicbit               2*2^dicbit
1532                                    v                           v   txtsiz
1533        +-------------+-------------+-------------+-------------+---+
1534   text |             |             |             |             |   |
1535        +-------------+-------------+-------------+-------------+---+
1536                                                               /<--->
1537                                                              /  maxmatch{256}
1538                                                            pos
1539
1540                                                                 <-->
1541                                                               remainder
1542
1543 ----------------------------------------------------------------------------
1544
1545 では、update() に入る。
1546
1547 static void update()
1548 {
1549     unsigned int i, j;
1550     unsigned int k;
1551     long n;
1552
1553 #if 0
1554     memmove(&text[0], &text[dicsiz], (unsigned)(txtsiz - dicsiz));
1555 #else
1556     {
1557         int m;
1558         i = 0; j = dicsiz; m = txtsiz-dicsiz;
1559         while (m-- > 0) {
1560             text[i++] = text[j++];
1561         }
1562     }
1563 #endif
1564     n = fread_crc(&text[(unsigned)(txtsiz - dicsiz)], 
1565                                (unsigned)dicsiz, infile);
1566
1567     remainder += n;
1568     encoded_origsize += n;
1569
1570     pos -= dicsiz;
1571     for (i = 0; i < HSHSIZ; i++) {
1572         j = hash[i];
1573         hash[i] = (j > dicsiz) ? j - dicsiz : NIL;
1574         too_flag[i] = 0;
1575     }
1576     for (i = 0; i < dicsiz; i++) {
1577         j = prev[i];
1578         prev[i] = (j > dicsiz) ? j - dicsiz : NIL;
1579     }
1580 }
1581
1582 先頭で、なぜか memmove() を for ループで書き換えている。なぜこのような
1583 ことを行っているのだろう。for ループを見てみてもやっていることは変わら
1584 ない。謎だが、とにかく、text[] の右半分(maxmatch 部分も含む) を左に移
1585 している。
1586
1587 次に fread_crc() で、新たにファイルを読み込む。今度の読み込み位置は
1588 &text[txtsiz - dicsiz] で、長さは dicsiz である。当然、remainder も更
1589 新している。encoded_origsize は以前と同様無視。pos は dicsiz 分減らさ
1590 れている。これはつまり図示すると、以下の状態になると言う事だ
1591
1592 ----------------------------------------------------------------------------
1593
1594                                 dicsiz=2^dicbit               2*2^dicbit
1595                                    v                           v   txtsiz
1596        +-------------+-------------+---+---------+-------------+---+
1597   text |             |             |   |         |             |   |
1598        +-------------+-------------+---+---------+-------------+---+
1599                                   /<--->                       <--->
1600                                  / maxmatch{256}              maxmatch{256}
1601                                 pos
1602
1603                                    <------------------------------->
1604                                               remainder
1605
1606        |------- 以前のデータ  ---------|--- 新しいデータ  ---------|
1607
1608 ----------------------------------------------------------------------------
1609
1610 以降、ファイルの読み込みは常にこの update()でしか行われない。pos は、
1611 初期状態と同じ位置なので、初期状態が再現されている。ここまでで、
1612 maxmatch の領域はなんだろうと思うが、おそらく先読みのためだろう。それ
1613 らしい処理は、match_insert() の冒頭にあった(が、現時点で詳細には触れて
1614 いない)。
1615
1616 # maxmatch 分の余分な領域は、pos の位置から最大 maxmatch 長の文字列照
1617 # 合を行うために必要な領域。先読みとはまた見当外れなことを書いたものだ。
1618 # ちょっと考えればわかることなのに・・
1619
1620 update() の残りを見る。
1621
1622     for (i = 0; i < HSHSIZ; i++) {
1623         j = hash[i];
1624         hash[i] = (j > dicsiz) ? j - dicsiz : NIL;
1625         too_flag[i] = 0;
1626     }
1627     for (i = 0; i < dicsiz; i++) {
1628         j = prev[i];
1629         prev[i] = (j > dicsiz) ? j - dicsiz : NIL;
1630     }
1631
1632 内容は、想像がつくので詳細は省略しよう。単に以前のデータが移動したので、
1633 ハッシュ値を更新しているだけだ。しかし、これはなかなか無駄な処理だ。
1634
1635 以前、text[] は環状バッファだろうと予想したが予想がはずれたことがわかっ
1636 た。環状バッファにしていれば、このハッシュの書き換えは不要にできただろ
1637 うと思うのだが・・・
1638 # そのかわり、位置の大小比較が繁雑にならないので、これはこれで良いのか
1639 # もしれない。どちらが優れているかは実験してみなければわからないだろう。
1640
1641 これで、一応は slide.c を網羅する事ができた。まだ不明な点は多いが、デ
1642 バッガで実際の処理を追いかければまたわかることがあるだろう。
1643
1644 ・・・しばし、休息・・・
1645
1646 さて、デバッガでと以前は考えていたのだが、あきらめるのはまだ早い(元気
1647 が出たらしい)、そもそも最初に「デバッガを使わずにどこまで解読できるか」っ
1648 と冒頭に書いてたのにたった2回の通読でもうあきらめようとしていた。が、
1649 ここまで書いてきた本書を何度か読み返したが、まだまだ検討の余地はある。
1650
1651 まず、match_insert() の処理でわからなかった部分を解読しよう。実は、こ
1652 れに関してはどうしてもわからず悩んでいたところ、Lha for UNIX のメンテ
1653 ナである岡本さんに教えてもらうことができた(ありがとうございます)その内
1654 容を確認しつつ match_insert() を見ることにする。
1655
1656 まずは、復習だ。通常の状態に関しては match_insert() の解読は済んでいる。
1657 match_insert() は、text[pos] から始まる文字列を辞書から検索し、見つかっ
1658 た位置と一致長を matchpos, matchlen に設定する処理だ。そして、ついでに 
1659 insert() で、text[pos] の位置をハッシュ配列に記録し、次回の検索に備え
1660 ることもしている。
1661
1662 では、不明な部分はなんだったかというと too_flag[] まわりである。
1663 too_flag のフラグが立っていると。辞書検索の頼りとなるハッシュ値を変更
1664 している。この部分がまったく皆目検討がつかなかったのだ。これに関してソー
1665 スを読み進めよう。以下ソースを再掲する。
1666
1667 static void match_insert()
1668 {
1669     unsigned int scan_pos, scan_end, len;
1670     unsigned char *a, *b;
1671     unsigned int chain, off, h, max;
1672
1673     max = maxmatch; /* MAXMATCH; */
1674     if (matchlen < THRESHOLD - 1) matchlen = THRESHOLD - 1;
1675     matchpos = pos;
1676
1677     off = 0;
1678     for (h = hval; too_flag[h] && off < maxmatch - THRESHOLD; ) {
1679         h = ((h << 5) ^ text[pos + (++off) + 2]) & (unsigned)(HSHSIZ - 1);
1680     }
1681     if (off == maxmatch - THRESHOLD) off = 0;
1682     for (;;) {
1683         chain = 0;
1684         scan_pos = hash[h];
1685         scan_end = (pos > dicsiz) ? pos + off - dicsiz : off;
1686         while (scan_pos > scan_end) {
1687             chain++;
1688
1689             if (text[scan_pos + matchlen - off] == text[pos + matchlen]) {
1690                 {
1691                     a = text + scan_pos - off;  b = text + pos;
1692                     for (len = 0; len < max && *a++ == *b++; len++);
1693                 }
1694
1695                 if (len > matchlen) {
1696                     matchpos = scan_pos - off;
1697                     if ((matchlen = len) == max) {
1698                         break;
1699                     }
1700                 }
1701             }
1702             scan_pos = prev[scan_pos & (dicsiz - 1)];
1703         }
1704
1705         if (chain >= LIMIT)
1706             too_flag[h] = 1;
1707
1708         if (matchlen > off + 2 || off == 0)
1709             break;
1710         max = off + 2;
1711         off = 0;
1712         h = hval;
1713     }
1714     prev[pos & (dicsiz - 1)] = hash[hval];
1715     hash[hval] = pos;
1716 }
1717
1718 まず、too_flag[] は、最初すべての要素が 0 である。そして、新たにファイ
1719 ルを読むとき(update())も 0 に再初期化されるのだった。このフラグが立つ
1720 条件はというと、この match_insert() の中だけである。その処理は
1721
1722         if (chain >= LIMIT)
1723             too_flag[h] = 1;
1724
1725 この部分だけだ。chain が LIMIT以上になったら h (これは検索対象のハッシュ
1726 値だ)に関して、フラグを立てる。chain は while ループ(これは文字列の照
1727 合を行う処理)のループ回数だ。h に関しての検索が LIMIT{256} 以上の場合
1728 に too_flag[h] のフラグが立っている。
1729
1730 while ループは一致文字列の一致長が最長一致長に達するか、辞書を最後まで
1731 探索するまでループする。つまり、あるハッシュ h に関してそのチェーンが 
1732 256 以上のものに関しては、too_flag[h] が 1 になっている。
1733
1734 では、そのような h に関して、match_insert() がどのような処理になってい
1735 るかを見る。まず初期化部分
1736
1737     max = maxmatch; /* MAXMATCH; */
1738     if (matchlen < THRESHOLD - 1) matchlen = THRESHOLD - 1;
1739     matchpos = pos;
1740
1741 これは、とりあえず無視。
1742
1743     off = 0;
1744     for (h = hval; too_flag[h] && off < maxmatch - THRESHOLD; ) {
1745         h = ((h << 5) ^ text[pos + (++off) + 2]) & (unsigned)(HSHSIZ - 1);
1746     }
1747     if (off == maxmatch - THRESHOLD) off = 0;
1748
1749 通常 off は、0 なのだが、too_flag[h] が 1 であるものに関しては値が変わ
1750 る。検索対象となる文字列 text[pos](のハッシュ値) hval に関して、
1751 too_flag[h] が立っていれば、(このハッシュのチェーンが 256 以上であるこ
1752 とが事前にわかっていれば)
1753
1754         h = ((h << 5) ^ text[pos + (++off) + 2]) & (unsigned)(HSHSIZ - 1);
1755
1756 で、検索対象となるハッシュ値を変更している。このハッシュ値が示すのは元
1757 の検索対象文字列の 1 文字先だ。
1758
1759 ----------------------------------------------------------------------------
1760
1761                            |--- c --|
1762                         |--- b --|  |
1763                      |-- a ---|  |  |
1764        +-------------+--------+--------+
1765 text   |             |  |  |  |  |  |  |
1766        +-------------+--------+--------+
1767                       \  \
1768                       pos pos+1(off=1)
1769
1770 ----------------------------------------------------------------------------
1771
1772 元の検索対象文字列が図の a だとすると、これを図の b にしている。初期化
1773 部のループは、もしこの b のハッシュチェーンに関して too_flag[h] がさら
1774 に 1 であるならさらに 先の文字列をハッシュ値とするようになっている。
1775 (これは元の pos の 2 文字先を示す。図の c の部分だ) h は、pos+off から
1776 の3文字のハッシュ値を示すものと言う事だ。
1777
1778 ただし、h があまりにも先の方を見るようなハメになれば(off が maxmatch -
1779 THRESHOLD) off は 0 に再設定されるが、このとき h はそのままだ。この意
1780 味はまだわからないが、バグなのではないかと想像している(h = hval に再設
1781 定する必要があるだろう)
1782
1783 では off = 1 だとして本処理を見ることにしよう。外側の for ループに関し
1784 ては、while ループを2回実行するかどうかだけのものだった。なので、 
1785 while ループ部だけを見てみよう。
1786
1787         chain = 0;
1788         scan_pos = hash[h];
1789         scan_end = (pos > dicsiz) ? pos + off - dicsiz : off;
1790         while (scan_pos > scan_end) {
1791             chain++;
1792
1793             if (text[scan_pos + matchlen - off] == text[pos + matchlen]) {
1794                 {
1795                     a = text + scan_pos - off;  b = text + pos;
1796                     for (len = 0; len < max && *a++ == *b++; len++);
1797                 }
1798
1799                 if (len > matchlen) {
1800                     matchpos = scan_pos - off;
1801                     if ((matchlen = len) == max) {
1802                         break;
1803                     }
1804                 }
1805             }
1806             scan_pos = prev[scan_pos & (dicsiz - 1)];
1807         }
1808
1809         if (chain >= LIMIT)
1810             too_flag[h] = 1;
1811
1812 scan_pos, scan_end に関しては検索開始位置と終了位置と言う事でもう良い
1813 だろう。で、最初の if の条件に着目する。
1814
1815             if (text[scan_pos + matchlen - off] == text[pos + matchlen]) {
1816
1817 これが真となる状態を図示しよう。
1818
1819 ----------------------------------------------------------------------------
1820
1821                                                         |-- c ---|
1822                     |-- a ---|                       |--- b --|
1823        +---------------+--------+--------------------+--------+--------+
1824 text   |               |  |x'|  |                    |  |  |x |  |  |  |
1825        +---------------+--------+--------------------+--------+--------+
1826                        ^                             \  \
1827                       scan_pos                       pos pos+1(off=1)
1828
1829 ----------------------------------------------------------------------------
1830
1831 まず、if 条件の左辺
1832
1833   text[scan_pos + matchlen - off]
1834
1835 matchlen は、match_insert() に入る直前に 2 に初期化されている(最初は)
1836 ので、照合するのは図の x' だ。
1837
1838 if 条件の右辺
1839
1840   text[pos + matchlen]
1841
1842 これは、図の x の位置だ。x' == x ならば本格的に照合を開始する。
1843
1844                 {
1845                     a = text + scan_pos - off;  b = text + pos;
1846                     for (len = 0; len < max && *a++ == *b++; len++);
1847                 }
1848
1849 ここで比較しているのは、図の a と b だ。b は、off がどのような場合でも
1850 変わらないが、a は、off が大きければ大きい程左側を指す。off が例えば
1851 3 であるときの場合も見てみよう。
1852
1853 ----------------------------------------------------------------------------
1854
1855               |-- a ---|                             |--- b --|-- c ---|
1856        +---------------+--------+--------------------+--------+--------+
1857 text   |             x'|  |  |  |                    |  |  |x |  |  |  |
1858        +---------------+--------+--------------------+--------+--------+
1859                        ^                              \        \
1860                       scan_pos                        pos      pos+3(off=3)
1861
1862 ----------------------------------------------------------------------------
1863
1864 結局比較しているのは、pos からの文字列のハッシュ値を求めた場合と何も変
1865 わらない。off でいくら先を見ようとも比較するのは pos の位置だ。なぜこ
1866 のようなことをするのだろうか?これは最初どうしてもわからなかったのだが、
1867 説明を受けて納得した。
1868
1869 これは単に効率(速度)のためということだ。もし、図の b に関して照合文字
1870 列の候補があまりにも多い場合(too_flag[h]=1)、ハッシュのチェーンを何度
1871 もたどる事になるので効率が悪い。しかし、辞書検索のキーを何文字か進める
1872 事で、この可能性を減らす事ができる。少なくとも最悪の 256 よりはマシに
1873 なるようなものを選んでいる。そうして、この while ループのループ回数を
1874 減らしているのだ。どうせ探したいのは最長一致文字列なので先の方の文字列
1875 が一致しないと話にならないのだからこれは合理的だ。
1876
1877 これで、外側の for ループも納得だ。これは while ループをある条件でやり
1878 直す処理だった。
1879
1880         if (matchlen > off + 2 || off == 0)
1881             break;
1882
1883 最長一致長が見つかるか、あるいは off が 0 であればさっさとこの処理は終
1884 るのだが、もし off を進めて照合を行っていたとして、最長一致文字列が見
1885 つからなかったとすると
1886
1887         max = off + 2;
1888         off = 0;
1889         h = hval;
1890
1891 という条件で照合をやり直す。これは元の文字列で照合をやり直すということ
1892 だからつまりは、最悪のハッシュチェーンを仕方ないから辿り直そうと言う事
1893 だ。さらに、pos から pos+off+3 までの文字列が、辞書から見つからなかっ
1894 たので、最大一致長を off + 2 として条件を緩めている(なぜこれが条件を緩
1895 める事になるかと言うと while ループは最大一致長の文字列が見つかったら
1896 すぐに抜けるからだ)。
1897
1898 ところで、match_insert() の先の処理は以下の書き換えを行うともう少し見
1899 やすくなる。(と思う)。
1900
1901 o scan_beg という変数を用意し、これを scan_pos - off にする。
1902 o scan_end は、pos - dicsiz にする。
1903 o while 条件を while (scan_pos != NIL && scan_beg > scan_end) にする。
1904
1905 以下
1906
1907         unsigned int scan_pos = hash[h];
1908         int scan_beg = scan_pos - off;
1909         int scan_end = pos - dicsiz;
1910
1911         chain = 0;
1912         while (scan_pos != NIL && scan_beg > scan_end) {
1913             chain++;
1914
1915             if (text[scan_beg + matchlen] == text[pos + matchlen]) {
1916                 {
1917                     unsigned char *a = &text[scan_beg];
1918                     unsigned char *b = &text[pos];
1919
1920                     for (len = 0; len < max && *a++ == *b++; len++);
1921                 }
1922
1923                 if (len > matchlen) {
1924                     matchpos = scan_beg;
1925                     if ((matchlen = len) == max) {
1926                         break;
1927                     }
1928                 }
1929             }
1930             scan_pos = prev[scan_pos & (dicsiz - 1)];
1931             scan_beg = scan_pos - off;
1932         }
1933
1934         if (chain >= LIMIT)
1935             too_flag[h] = 1;
1936
1937 ----------------------------------------------------------------------------
1938
1939               |-- a ---|                             |--- b --|
1940        +---------------+--------+--------------------+--------+--------+
1941 text   |      |      x'|  |  |  |                    |  |  |x |  |  |  |
1942        +---------------+--------+--------------------+--------+--------+
1943          ^     \        \                             \        \
1944          |    scan_beg  scan_pos                        pos      pos+off
1945      scan_end
1946
1947          |----|
1948            scan_beg の有効範囲
1949
1950          |----------------- dicsiz ------------------|
1951
1952 ----------------------------------------------------------------------------
1953
1954 scan_beg, scan_end の範囲もわかりやすいし、hash[h] が NIL の場合の処理
1955 も明示的だ。この書き換えを行う場合、scan_beg が負の値になる可能性があ
1956 る。元もとの処理では scan_end 等の変数を unsigned にしているので、これ
1957 らを int にして while 条件で負の scan_beg をはじかなければならないこと
1958 に注意。そうすると、scan_pos != NIL は必要なくなるのだがわかりやすさを
1959 追求した。
1960
1961 これで match_insert() の解読は終りだ。match_insert() の処理とは以下の
1962 通りだ。
1963
1964 ----------------------------------------------------------------------------
1965   match_insert() は、text[pos] から始まる文字列に一致する文字列を辞書
1966   から検索し、見つかった位置と一致長を matchpos, matchlen に設定する。
1967
1968   もし、最長一致文字列が見つからなければ matchpos は、pos に設定され、
1969   matchlen は更新されない。(実は、matchpos = pos の情報は特に使われてない)
1970
1971   見つかった場合、matchlen は呼び出し前の matchlen よりも大きくなる。
1972   (呼び出し前では matchlen の意味は最低限一致しなくてはならない文字列
1973   長で、照合条件の一つになっている)
1974
1975   この関数の入力は
1976
1977       matchlen
1978       pos
1979
1980   出力は
1981
1982       matchlen
1983       matchpos
1984
1985   といったところ。
1986
1987   さらに、insert() と同様の処理で、pos の位置をハッシュ配列に記録し、
1988   次回の検索に備える。これはついでの処理。
1989 ---------------------------------------------------------------------------- 
1990
1991 これを踏まえた上で処理内容を再読しよう。(E) 〜 (H) だ。
1992
1993         /* (E) */
1994         lastmatchlen = matchlen;  lastmatchoffset = pos - matchpos - 1;
1995         --matchlen;
1996
1997         /* (F) */    /* (G) */
1998         get_next();  match_insert();
1999         if (matchlen > remainder) matchlen = remainder;
2000
2001         /* (H) */
2002         if (matchlen > lastmatchlen || lastmatchlen < THRESHOLD) {
2003             /* (H.1) */
2004             encode_set.output(text[pos - 1], 0);
2005             count++;
2006         } else {
2007
2008 (H) の条件とは何なのかを見る。この条件が真なら、文字列をそのまま出力す
2009 るのだが、素直に slide 辞書法の処理を考えればこの条件は「辞書から見つ
2010 からなかった場合」となる。が、実際にはもう少し複雑だ。
2011
2012         /* (H) */
2013         if (matchlen > lastmatchlen || lastmatchlen < THRESHOLD) {
2014
2015 matchlen は、pos の位置の文字列が見つかった辞書上の長さ
2016 lastmatchlen は、pos-1 の位置の文字列が見つかった辞書上の長さ
2017
2018 であるとすると、この条件は、「pos の位置で見つかった長さが、pos-1 の位
2019 置で見つかった長さよりも長ければ」っとなる。
2020
2021 これはつまり、pos-1 と pos のニ箇所に関して辞書を検索して、より長くマッ
2022 チする方を選ぼうとしているわけだ。matchlen の方が長いなら 1 つ前
2023 (pos-1)の文字はそのまま出力し、次のループに移る(もし、次の文字が
2024 さらに長くマッチするなら。またそのまま出力される)
2025
2026 この条件で「見つからなかった場合」というのはどう表されているかを考える。
2027 もし、pos の文字列が辞書になければ pos - 1 の文字列は、どうすべきかと
2028 いうと「pos-1 の文字列が見つかってなければ。そのまま出力。辞書にあった
2029 なら <lastmatchlen, lastmatchoffset> のペアを出力」っとならなければな
2030 らない。
2031
2032 lastmatchlen は、初期状態では THRESHOLD - 1 であったので、見つからなかっ
2033 たという条件は (H) の右側の条件 lastmatchlen < THRESHOLD でまず表され
2034 ている。
2035
2036 では、例えば lastmatchlen が 5 であったとしよう。このとき (E) の処理で 
2037 matchlen は lastmatchlen - 1 つまり、4 に設定される。そして、match_insert()
2038 で次の文字列がもし辞書から見つからなければ matchlen は更新されないので
2039   matchlen < lastmatchlen 
2040 となる。このような条件(前回見つかり、今回見つからない)場合に限り、(H.2)
2041 の処理が実行されるようになっている。では、(H.2) の処理を追いかけよう。
2042
2043 まず、この状態を図示する。
2044
2045 ----------------------------------------------------------------------------
2046
2047                          lastmatchlen                  lastmatchlen
2048                        |--          --|              |--          --|
2049        +---------------+--------------+--------------+--------------+--+
2050 text   |               |  |  |  |  |  |              |  |  |  |  |  |  |
2051        +---------------+--------------+--------------+--------------+--+
2052                        ^                             |   \           \
2053                       matchpos                    pos-1  pos         pos2
2054
2055                        |--------------------------|
2056                              lastmatchoffset
2057
2058 ----------------------------------------------------------------------------
2059
2060
2061             /* (H.2) */
2062             encode_set.output(lastmatchlen + (UCHAR_MAX + 1 - THRESHOLD),
2063                (lastmatchoffset) & (dicsiz-1) );
2064             --lastmatchlen;
2065
2066             while (--lastmatchlen > 0) {
2067                 get_next();  insert();
2068                 count++;
2069             }
2070             get_next();
2071             matchlen = THRESHOLD - 1;
2072             match_insert();
2073             if (matchlen > remainder) matchlen = remainder;
2074         }
2075
2076 まず、<長さ, 位置> のペアを出力する。これはいいだろう。出力する「位置」
2077 は0 なら 1 文字前を表すので、実際のオフセット pos - 1 - matchpos より
2078 も 1 小さい値になっていることに注意しておこう。
2079
2080 そして、lastmatchlen は 1 引かれる。この場合例えば 4 になる。したがっ
2081 て、次のループでは 3 文字 pos が先送りされる(4 ではない)。pos は既に 1 
2082 文字先に進んでいるので、最初に 1 引くのはこのことを考慮している。while 
2083 ループが終った後はpos の位置は実際に出力した文字列の最後の文字 pos2-1 
2084 を指していることになる。
2085
2086 そして、get_next() でまた 1 文字先に送る。pos は図の pos2 の位置になる。
2087 そして、match_insert() で、この位置の文字列を照合する。matchlen は、
2088 THRESHOLD - 1 に初期化されるので pos2 の位置の文字列が辞書から見つから
2089 なければ matchlen は、THRESHOLD-1 だ。これは初期状態と同じ状態を示すの
2090 で、次のループの処理も想像がつく((H) の条件の右側 lastmatchlen < THRESHOLD
2091 が有効になる)。では、見つかった場合はというと、次のループでさらに先 
2092 pos2+1 の照合結果と比較されるのでこの処理も想像がつく。
2093
2094 最初、どうにもこの処理内容が理解できなかったのだが「現在の文字列と、次
2095 の文字列のそれぞれで辞書を検索し、より長く見つかった方を使う」という最
2096 適化を行っている事がわかってしまった後は解読は簡単だった。(実はこの事
2097 実も教えてもらった事だ。全然ダメじゃん)
2098
2099 さて、これで一通りの解析は済んだわけだが、ここまでの解析内容を読み直し
2100 てみると、以下の点がまだひっかかる。
2101
2102 1. ハッシュ関数は最適なのか?特に HSHSIZ{2^15} は最適なのか?
2103 2. too_flag[] は、実際に照合を行いループがLIMITを越えた場合に
2104    設定される。しかし、ハッシュのチェーンを作る際にチェーンの
2105    個数をあらかじめ数えておけば一度の探索すらも行われず。より
2106    早く処理されないだろうか?
2107
2108 現状、1, 2 とも実施してみたところ特に速度の改善は見られなかった。特に 
2109 1 は、微妙なところがありほとんどの書き換えは性能を悪くするだけだった。
2110 なかなか興味深いものがある。
2111
2112 これは今後の課題としていずれまた検証しよう。そろそろ slide.c も飽きて
2113 きたのでひとまずはこれで終りにしたい。
2114
2115 \f
2116 bit 入出力ルーチン (crcio.c)
2117 ---------------------------
2118
2119 これから Huffman 法の解読に移るのだが、その前準備として bit 入出力ルー
2120 チンの解読を行う。Huffman 法の実装では必ず bit 入出力処理が必要になる。
2121 LHa for UNIX ももちろん例外ではなく、Huffman 法の実装を解読するにあた
2122 りこの部分の処理内容ははっきりさせておいた方が良いと考えたのだ。
2123
2124 LHa for UNIX version 1.14i では bit 入出力ルーチンは crcio.c で定義さ
2125 れている。(このようなファイル名に存在するのは意外な事だ。最近の LHa
2126 for UNIX では、私が bitio.c というファイルを設け、bit 入出力ルーチンは
2127 そこに切り出した)
2128
2129 crcio.c のうち bit 入出力ルーチンは fillbuf(), getbits(), putcode(),
2130 putbits(), init_getbits(), init_putbits() の 6 関数だ。
2131
2132 まず、初期化用の init_getbits(), init_putbits() を見よう。
2133
2134 void
2135 init_getbits( /* void */ )
2136 {
2137     bitbuf = 0;
2138     subbitbuf = 0;
2139     bitcount = 0;
2140     fillbuf(2 * CHAR_BIT);
2141 #ifdef EUC
2142     putc_euc_cache = EOF;
2143 #endif
2144 }
2145
2146 void
2147 init_putbits( /* void */ )
2148 {
2149     bitcount = CHAR_BIT;
2150     subbitbuf = 0;
2151     getc_euc_cache = EOF;
2152 }
2153
2154 それぞれ bit 入力、bit 出力を行うための初期化処理だ。CHAR_BIT というの
2155 は 8 で、char 型の bit サイズを表しているらしい。詳細はわからないが初期
2156 状態はとにかくこれだ。ここで使用されている変数は、
2157
2158 static unsigned char subbitbuf, bitcount;
2159
2160 が、crcio.c で定義されており、
2161
2162 EXTERN unsigned short bitbuf;
2163
2164 が、lha.h で定義されている(EUC なんたらは本質ではない無視しよう)。グロー
2165 バル変数と言うのは忌むべきものだが、とにかく使用されている変数と初期値
2166 を確認したので次に移ろう。init_getbits() で、早速 fillbuf() が呼ばれて
2167 いる。この処理内容を見る。
2168
2169 void
2170 fillbuf(n)          /* Shift bitbuf n bits left, read n bits */
2171     unsigned char   n;
2172 {
2173     /* (A) */
2174     while (n > bitcount) {
2175         n -= bitcount;
2176         /* (B) */
2177         bitbuf = (bitbuf << bitcount) + (subbitbuf >> (CHAR_BIT - bitcount));
2178         /* (C) */
2179         if (compsize != 0) {
2180             compsize--;
2181             subbitbuf = (unsigned char) getc(infile);
2182         }
2183         else
2184             subbitbuf = 0;
2185         bitcount = CHAR_BIT;
2186     }
2187     /* (D) */
2188     bitcount -= n;
2189     bitbuf = (bitbuf << n) + (subbitbuf >> (CHAR_BIT - n));
2190     subbitbuf <<= n;
2191 }
2192
2193 まず、初期状態として
2194
2195     bitbuf = 0;
2196     subbitbuf = 0;
2197     bitcount = 0;
2198
2199 であり、fillbuf の引数 n には 2 * CHAR_BIT が与えられたのだった。いき
2200 なり while 条件を満たすのでループ部の解析を行わなくてはならなくなるが、
2201 ひとまずこれを無視して最後の 3 行 (D) に着目する。条件は少ない方が良い
2202 のだ。
2203
2204     /* (D) */
2205     bitcount -= n;
2206     bitbuf = (bitbuf << n) + (subbitbuf >> (CHAR_BIT - n));
2207     subbitbuf <<= n;
2208
2209 bitbuf << n, subbitbuf << n されているので、bitbuf, subbitbuf を n ビッ
2210 ト左にずらす処理のようだ。さらに bitbuf には、subbitbuf を n ビットず
2211 らしたときに溢れた部分を bitbuf にセットしている。っと、
2212
2213    (subbitbuf >> (CHAR_BIT - n))
2214
2215 の部分を軽く説明したが、図示して確認しておこう。
2216
2217 subbitbuf は unsigned char なので 8 bit の変数だ。
2218
2219 ----------------------------------------------------------------------------
2220                7  6  5  4  3  2  1  0
2221               +--+--+--+--+--+--+--+--+
2222    subbitbuf  |                       |
2223               +--+--+--+--+--+--+--+--+
2224               <-- n -->
2225 ----------------------------------------------------------------------------
2226
2227 n が例えば 3 の場合、CHAR_BIT - n は、5 だから subbitbuf を 5 ビット右
2228 にずらした値を取っている。つまり、図の 7, 6, 5 ビット目が一番右に来る
2229 ようになっており、この値を bitbuf に足している。(C言語では、unsigned 
2230 な変数を右にシフトすると上位ビットには 0 が入る)
2231
2232 fillbuf() の後半 3 行(いや、後半2行か)は。結局 bitbuf と subbitbuf を
2233 一つの bitbuf とみなして n ビット左にずらしていることがわかる。
2234
2235 ----------------------------------------------------------------------------
2236 <ビットバッファ全体図 (予想)>
2237
2238            7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
2239           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2240   bitbuf  |                             |          x  y  z|
2241           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2242                                          \        <-- n  ->
2243                                          subbitbuf
2244            <-------------- bitcount ------------->
2245
2246 ----------------------------------------------------------------------------
2247
2248 このとき、当然図の x, y, z の部分(n = 3 は例としての値だ)が空く事になる。
2249 bitcount という変数が n 引かれていたが、これは bit バッファ全体の有効
2250 なビット数を表しているのではないかと予想しておく。すなわち図の状態なら
2251 21 だ。while ループは(関数名から)この空き部分を埋める処理なのではない
2252 かと適当に予想できる。では、while ループを見よう。もう一度初期値を確認
2253 し、最初に行われる処理内容を見よう。
2254
2255 最初、
2256
2257     bitbuf = 0;
2258     subbitbuf = 0;
2259     bitcount = 0;
2260
2261 であるから、bitバッファは空っぽだ。当然 fillbuf(2 * CHAR_BIT) されると
2262 while 条件を満たす。きっと 16 bit だけ bitバッファが補充されるはず(つ
2263 まり、bitbuf いっぱい、subbitbuf 空)だ。
2264
2265     /* (A) */
2266     while (n > bitcount) {
2267         n -= bitcount;
2268
2269 で、ビットバッファが保持する bit 数以上を要求されたので、ループに入る。
2270 n -= bitcount で、本当に足りない部分が何ビットなのかを得ている。ここで
2271 は 16 だ。次の行
2272
2273         /* (B) */
2274         bitbuf = (bitbuf << bitcount) + (subbitbuf >> (CHAR_BIT - bitcount));
2275
2276 これは先程も出て来た処理だ。ビットバッファ全体を bitcount 分左にずらし
2277 ている(ただし、まだ subbitbuf はずらされていない)。この時点で予想が少
2278 し覆された。8 - bitcount で subbitbuf をずらしているから bitcount は最
2279 大 8 の値しか持たないだろうということだ。どういうことか、考えてみる・・・
2280 考えてもわからなかったので次に進もう。
2281
2282         /* (C) */
2283         if (compsize != 0) {
2284             compsize--;
2285             subbitbuf = (unsigned char) getc(infile);
2286         }
2287         else
2288             subbitbuf = 0;
2289         bitcount = CHAR_BIT;
2290
2291 compsize というのが出て来たが、この値がどうあろうとも subbitbuf は8 ビッ
2292 ト埋められ。bitcount は 8 に設定されている。わかった bitcount は、
2293 subbitbuf に保持する bit 数だ。図を訂正しよう。
2294
2295 ----------------------------------------------------------------------------
2296 <ビットバッファ全体図>
2297
2298            7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
2299           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2300   bitbuf  |                             |            x y z|
2301           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2302                                        /          <-- n  ->
2303                                    subbitbuf
2304                                         <-------->
2305                                          bitcount
2306
2307 ----------------------------------------------------------------------------
2308
2309 この図を踏まえてもう一度初期状態での処理内容を追いかける。
2310
2311 まず、(A) で、subbitbuf は空なので、bitcount は 0 だ。要求した bit 数 
2312 n{16} より小さいのでループに入る。n は 16 のままだ。
2313
2314 (B) で、subbitbuf に残っている bit を bitbuf にずらしている。今はまだ
2315 空なのでbitbuf はここでもまだ空だ。
2316
2317 (C) で、ファイルからデータを8 ビット読む(compsize は常に0ではないと考え
2318 る)。bitcount は 8 になる。この時点で bitバッファ全体は subbitbuf だけ
2319 値が入った状態になる。
2320
2321 次のループに移ろう。(A) で、subbitbuf はいっぱいであるが要求した n{16} 
2322 よりは小さいので、まだループは続く。n はここで 8 になる。
2323
2324 (B) で、subbitbuf に残っている bit (8 bit だ)を bitbuf にずらしている。
2325 今度は subbitbuf 全体が bitbuf に移っているのと同じだ。(つまり、bitbuf
2326 = subbitbuf)
2327
2328 (C) で、また subbitbuf は 8 bit 補充される。
2329
2330 (A) で、n{8} > bitcount{8} は偽なのでループが終る。
2331
2332 (D) で、subbitbuf に残っている bit はすべて bitbuf に移る。bitbuf は 16
2333 bit いっぱいになる。bitcount は 0 だ。
2334
2335 この処理結果から fillbuf(n) は、bitbuf に n ビット読み込む処理だと言え
2336 る。引数に指定できる n が最大 16 ビットであることにも気づいて良いだろ
2337 う。処理内容を確認してみればわかる。
2338
2339 ここで、subbitbuf の用途に気づいた。ファイルからの読み込みが 8 ビット
2340 単位でしかできないので、それを補うための保存用バッファであろう。例えば
2341 1 ビットだけ bitbuf を fill したければ subbitbuf に 7 bit 残し、1 bit
2342 だけ bitbuf に設定される(確認してみればわかる)
2343
2344 fillbuf() がわかったので、それを利用している getbits() の内容を確認し
2345 よう。
2346
2347 unsigned short
2348 getbits(n)
2349     unsigned char   n;
2350 {
2351     unsigned short  x;
2352
2353     x = bitbuf >> (2 * CHAR_BIT - n);
2354     fillbuf(n);
2355     return x;
2356 }
2357
2358     x = bitbuf >> (2 * CHAR_BIT - n);
2359
2360 は、3 度も出て来たので
2361
2362      buf >> (sizeof(buf)*8 - n)
2363
2364 を buf の上位 n ビットを得る式だとしてマクロにしても良いだろう。(が、
2365 良い名前が思い付かないのでそうはしない)。とにかく、bitbuf の上位 n ビット
2366 を下位 n ビットとして x に代入している。その後で、
2367
2368     fillbuf(n);
2369
2370 している。n bit を x に渡したので bitbuf から上位 n ビットを捨てて、n 
2371 ビット補充する。ここで、bitbuf は常にいっぱいの状態になっていることが
2372 わかる。(ファイルの末尾付近の場合、正確に bitbuf に何ビット残っている
2373 かが判断できないが、種明かしをするとこのことは LHa の処理内容にとって
2374 はどうでもいいことだ。getbits() は decode 処理で使われるのだが、decode 
2375 処理は何ビットの情報を decode する必要があるかを他の情報からあらかじめ
2376 得ている)
2377
2378 次に移ろう今度は putcode() だ。put の場合まずは、init_putbits() で
2379 初期化が行われている。その値は以下だ。
2380
2381     bitcount = CHAR_BIT;
2382     subbitbuf = 0;
2383     getc_euc_cache = EOF;
2384
2385 getc_euc_cache は無視だ。bitcount と subbitbuf の値が設定され、bitbuf 
2386 は利用されない。先程とは違い subbitbuf が空なのにbitcount が 8 なので、
2387 bitcount の使われ方が多少異なるようだ。get の場合は、bitcount は、
2388 subbitbuf に保持する bit 数だったが今度は subbitbuf の空き bit 数だろ
2389 うと予想しておこう。
2390
2391 そして、putcode(n, x) を見る。実はソースを見るとわかるのだが、もう一つ
2392 の出力ルーチン putbits() は、putcode() の呼び出しに書き換え可能だ。
2393 putbits() は、
2394
2395 void
2396 putbits(n, x)           /* Write rightmost n bits of x */
2397     unsigned char   n;
2398     unsigned short  x;
2399 {
2400     x <<= USHRT_BIT - n;
2401     putcode(n, x);
2402 }
2403
2404 っと書き換えられるのだ。なので、putcode() の内容を先に確認するわけだ。
2405
2406 void
2407 putcode(n, x)           /* Write rightmost n bits of x */
2408     unsigned char   n;
2409     unsigned short  x;
2410 {
2411     /* (A) */
2412     while (n >= bitcount) {
2413         n -= bitcount;
2414         /* (B) */
2415         subbitbuf += x >> (USHRT_BIT - bitcount);
2416         x <<= bitcount;
2417         /* (C) */
2418         if (compsize < origsize) {
2419             if (fwrite(&subbitbuf, 1, 1, outfile) == 0) {
2420                 /* fileerror(WTERR, outfile); */
2421                 fatal_error("Write error in crcio.c(putcode)\n");
2422                 /* exit(errno); */
2423             }
2424             compsize++;
2425         }
2426         else
2427             unpackable = 1;
2428         subbitbuf = 0;
2429         bitcount = CHAR_BIT;
2430     }
2431     /* (D) */
2432     subbitbuf += x >> (USHRT_BIT - bitcount);
2433     bitcount -= n;
2434 }
2435
2436 処理内容が fillbuf() のときと似ている。まずは、先程と同様に while 条件
2437 を無視して考えてみる。(D) だ。
2438
2439     /* (D) */
2440     subbitbuf += x >> (USHRT_BIT - bitcount);
2441     bitcount -= n;
2442
2443 この式はもう 4 度目だ。まず、x の上位 bitcount ビットを得て、subbitbuf 
2444 に足している。bitcount は、先程 subbitbuf の空きであろうと予想したが、
2445 n 引かれているので、埋めた分空きが減っているわけだ。予想は当たっている
2446 だろう。この時点でこの関数が x の上位ビットを利用することがわかる。コ
2447 メントに rightmost n bits of x と書かれているが惑わされてはいけない。
2448 多くの場合、コメントはせいぜいヒントとしての情報でしかない。信用しては
2449 いけないものなのだ。(コメントはあまりデバッグされない。コメントが詳し
2450 ければ詳しい程コメントはエンバグしやすい。疑ってかかろう。これは本書に
2451 も言える。すべてを鵜のみにしてはいけないのだ)
2452
2453 では、処理内容に移る。まずは (A)
2454
2455     /* (A) */
2456     while (n >= bitcount) {
2457         n -= bitcount;
2458
2459 subbitbuf の空きが n 以下であればループに入る。subbitbuf 一つではn ビッ
2460 ト全部は賄えないからループで小刻みに処理しようということだろう(もう全
2461 体の処理内容の予想はついている)
2462 n から bitcount 引いているので、n ビットのうちこれから bitcount 分は
2463 処理されることをここでさっさと記録して次のループに備えている。
2464
2465         /* (B) */
2466         subbitbuf += x >> (USHRT_BIT - bitcount);
2467         x <<= bitcount;
2468
2469 x の上位 bitcount ビットを subbitbuf に足している。subbitbuf の空きが
2470 これで埋まった。subbitbuf はもういっぱいだ。x を bitcount シフトするこ
2471 とで subbitbuf に渡した x の上位ビットを捨てている。
2472
2473         /* (C) */
2474         if (compsize < origsize) {
2475             if (fwrite(&subbitbuf, 1, 1, outfile) == 0) {
2476                 /* fileerror(WTERR, outfile); */
2477                 fatal_error("Write error in crcio.c(putcode)\n");
2478                 /* exit(errno); */
2479             }
2480             compsize++;
2481         }
2482         else
2483             unpackable = 1;
2484         subbitbuf = 0;
2485         bitcount = CHAR_BIT;
2486
2487 compsize は無視しても良い。処理の本質ではないからだが、すぐにわかるので
2488 一応説明すると、
2489         if (compsize < origsize) {
2490             ...
2491         else
2492             unpackable = 1;
2493 で、圧縮ファイルサイズが元のファイルサイズを上回ったときに
2494 処理を終るようになっている(unpackable = 1 して、他の箇所でこの変数を監視する。
2495 unpackable == 1 なら処理を中断する)
2496
2497 とにかく (C) の時点では必ず subbitbuf がいっぱいになるので 1 バイトを
2498 ファイルに書き出している。その後、subbitbuf = 0, bitcount = 8 として 
2499 subbitbuf を空けて次のループに備えている。
2500
2501 もういいだろう。putcode() は、論理的には x のうち上位 n ビットを出力す
2502 る処理だ。引数 n の上限が x の最大ビットサイズ 16 になるのは説明するま
2503 でもないだろう。
2504
2505 putcode() は実装として、subbitbuf と x を一つに繋げて n bit 左にずらし
2506 ている処理だと考えても良い。そうして、subbitbuf がいっぱいになったらそ
2507 の都度(1 バイトずつ)ファイルに書き出すのだ。
2508
2509 ----------------------------------------------------------------------------
2510 <ビットバッファ全体図>
2511
2512                       <--- 左にずらす
2513
2514            7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
2515           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2516           |* * *          |x y z                          |
2517           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2518          /               / <-n->
2519       subbitbuf         x
2520                  <-------->
2521                   bitcount
2522
2523 ----------------------------------------------------------------------------
2524
2525 putbits() も見よう。先程 putcode() の呼び出しに書き換えたコードを見ると
2526 すぐわかるが、
2527
2528     x <<= USHRT_BIT - n;
2529     putcode(n, x);
2530
2531 最初の式で、x の下位 n ビットを x の上位 n ビットにしている。
2532 そうして、putcode() を呼び出しているので、putbits(n, x) は、x
2533 の下位 n ビットを出力する処理だ。
2534
2535 以上でビット入出力ルーチンは終りだ。出力に関して一つ捕捉しておくと
2536 putcode(), putbits() では最後の最後で subbitbuf に情報が残ったままファ
2537 イルに書き出されない状態になる。これを吐き出すために利用者は
2538
2539   putcode(7, 0)
2540
2541 を行う必要がある。
2542
2543 まとめよう
2544
2545 ----------------------------------------------------------------------------
2546 fillbuf(n)
2547   bitbuf から上位 n ビットを捨てて、下位 n ビットをファイルから読み込
2548   み埋める。
2549
2550 getbits(n)
2551   bitbuf の上位 n ビットを下位 n ビットとして返す。bitbuf は n ビット
2552   補充される。
2553
2554 putcode(n, x)
2555   x の上位 n ビットをファイルに出力する。最後の出力時は putcode(7, 0)
2556   する必要がある。
2557
2558 putbits(n, x)
2559   x の下位 n ビットをファイルに出力する。最後の出力時は putcode(7, 0)
2560   する必要がある。
2561
2562 init_getbits()
2563   入力処理の初期化
2564
2565 init_putbits()
2566   出力処理の初期化
2567 ----------------------------------------------------------------------------
2568
2569 読み込みに関して、bitbuf のサイズが 16 ビットで常にその状態が保持され
2570 ているのは LHa にとって重要な事だ。decode 処理では直接 bitbuf を参照す
2571 る箇所がある。
2572
2573 \f
2574 Huffman 法 (huf.c)
2575 ------------------
2576
2577 LHa for UNIX では、静的 Huffman 法として、shuf.c、動的 Huffman 法とし
2578 て dhuf.c があるらしいが、これらに関しては触れない。LHa では、これらは
2579 過去の版のアーカイブを解凍できるように decode のみサポートしているよう
2580 だ。そこで、まずは -lh4-, -lh5-, -lh6-, -lh7- で利用されている huf.c 
2581 の解析を中心に行うこととしたい。
2582
2583 ところで、本書では Huffman 法がどういったものかは予備知識として既に知っ
2584 ているものとするが、いちおう概要を簡単に説明しておこう。
2585
2586 以下の内容のテキストファイルがあったとする。
2587
2588         abcabcaba
2589
2590 このテキストは 9 バイトあるわけだが、このファイルで使われている文字は3 
2591 種類しかない、a, b, c だ。だからこのファイルだけに関して言えば 1 文字
2592 は 2 ビットあれば表現可能である。例えば各文字に対して以下のビットを
2593 割り当てたとすると
2594
2595         文字   ビット表現
2596         a      00
2597         b      01
2598         c      10
2599
2600 先のテキストファイルの内容 abcabcaba は、18ビットあれば表現可能となる。
2601
2602 さらに、出現頻度の高い文字を少ないビット数で表現し、まれにしか現れない
2603 文字を長いビット数で表すようにすればよりビット数を少なくできる。例えば
2604
2605         文字   ビット表現
2606         a      0
2607         b      10
2608         c      11
2609
2610 であるとすると a は 4回、bは3回、cは2回現れるので、全体は 4 + 2*3 +
2611 2*2 = 14 ビットで表現できることになる。これが Huffman 法の圧縮原理であ
2612 る。このように Huffman 法では文字をビット単位で扱うためビット入出力ルー
2613 チンを先に解読したわけだ。また、符号化の際はあらかじめ各文字の出現頻度
2614 を求めておく必要があり、復号化の際はどのビット列がどの文字に対応するか
2615 をあらかじめ知る必要がある。
2616
2617 文字毎にビット長のばらつきがあるような可変長符号には以下の条件がある。
2618
2619    ある符号のビットパターンは、他の符号のビットパターンの開始にはなら
2620    ない。
2621
2622 というものだ。これを「語頭条件」と言うらしい。例えば、先の例では a に 
2623 0 を割り当てたので他の文字は必ず 1 から始まるようになっている。この条
2624 件を満たさなければならない理由はちょっと考えればわかる。仮に以下の間違っ
2625 た割当を行ったとする。
2626
2627         文字   ビット表現
2628         a      0
2629         b      10
2630         c      01
2631
2632 これだと、ビットパターン 010 が ab なのか ca なのか曖昧になるのがわか
2633 るだろう。
2634
2635 文字に対応する語頭条件を満たす(最適な)ビット列を求める方法、それがハフ
2636 マン法だ。ハフマン法ではハフマン木という木構造を構築するのだが、そのア
2637 ルゴリズムは以下のとおりだ。
2638
2639 まず、圧縮対象であるテキストに関して各文字の出現回数を数える。例えば 
2640 abcabcaba というテキストでは、a は 4回、bは3回、cは2回なので、
2641
2642         4    3    2
2643         |    |    |
2644         a    b    c
2645
2646 となる。次に、出現回数の低いもの同士を一つの節に束ねる。この節は 3+2=5 
2647 という出現回数を持つものと考える。
2648
2649         4      5
2650         |     / \
2651         a    b   c
2652
2653 以降さらに出現回数の低いもの同士を一つの節に束ねる操作を繰り返す。この
2654 例では、もう一度束ねれば終りだ。
2655
2656            9
2657            /\
2658           /  \
2659          /  / \
2660         a  b   c
2661
2662 ここで、木の左側が 0 右側が 1 であるとすると。a は根から左に1つ進むだ
2663 けなので 0。b は、右(1)→左(0) なので、10。c は右(1)→右(1) なので、11 
2664 となる。実際の符号化の際は文字からビット列を求めるので葉から根にむかっ
2665 て逆順に辿ることになる。また、復号の際は入力のビット列に沿ってこの木を
2666 根から辿ることで対応する文字を求める(なので圧縮文にはこの木構造が一緒
2667 に情報として格納されることになる)。
2668
2669 このようなハフマン木を作成する箇所があるかどうかを探してみたところ 
2670 maketree.c:make_tree() が見つかった。これは「C言語による最新アルゴリズ
2671 ム辞典」(奥村晴彦著、技術評論社)に載っているものとほとんど同じだ。では、
2672 この関数の解読から始めよう(今回の解析はボトムアップ式に行うことにしよ
2673 うと思う。というのもデータ構造から攻めようにもグローバル変数がたくさん
2674 出て来るし、処理順に追っても正直よくわからなかったからだ)
2675
2676 この関数のあるファイル maketree.c で使用しているデータ構造は以下だ。
2677
2678 static short    n, heapsize, heap[NC + 1];
2679 static unsigned short *freq, *sort;
2680 static unsigned char *len;
2681 static unsigned short len_cnt[17];
2682
2683 make_tree() は以下。
2684
2685 short
2686 make_tree(nparm, freqparm, lenparm, codeparm)
2687 /* make tree, calculate len[], return root */
2688     int             nparm;
2689     unsigned short  freqparm[];
2690     unsigned char   lenparm[];
2691     unsigned short  codeparm[];
2692 {
2693     short           i, j, k, avail;
2694
2695     /* (A) */
2696     n = nparm;
2697     freq = freqparm;
2698     len = lenparm;
2699     avail = n;
2700     /* (B) */
2701     heapsize = 0;
2702     heap[1] = 0;
2703     for (i = 0; i < n; i++) {
2704         len[i] = 0;
2705         if (freq[i])
2706             heap[++heapsize] = i;
2707     }
2708     /* (C) */
2709     if (heapsize < 2) {
2710         codeparm[heap[1]] = 0;
2711         return heap[1];
2712     }
2713     /* (D) */
2714     for (i = heapsize / 2; i >= 1; i--)
2715         downheap(i);    /* make priority queue */
2716     /* (E) */
2717     sort = codeparm;
2718     do {            /* while queue has at least two entries */
2719         i = heap[1];    /* take out least-freq entry */
2720         if (i < n)
2721             *sort++ = i;
2722         heap[1] = heap[heapsize--];
2723         downheap(1);
2724         j = heap[1];    /* next least-freq entry */
2725         if (j < n)
2726             *sort++ = j;
2727         k = avail++;    /* generate new node */
2728         freq[k] = freq[i] + freq[j];
2729         heap[1] = k;
2730         downheap(1);    /* put into queue */
2731         left[k] = i;
2732         right[k] = j;
2733     } while (heapsize > 1);
2734     /* (F) */
2735     sort = codeparm;
2736     make_len(k);
2737     make_code(nparm, lenparm, codeparm);
2738     return k;       /* return root */
2739 }
2740
2741 この関数の引数に、nparm, freqparm, lenparm, codeparm というのがある。
2742 これがなんなのかいきなりではわからないだろう。実は私にもわからない。今
2743 回の解析で特殊なのは、処理内容については私は(アルゴリズム辞典で)知って
2744 いることだ。強引に無視しても処理内容から想像がつくだろうことを期待して
2745 いる。
2746
2747 とりあえず、冒頭の初期化部分 (A) で
2748
2749     /* (A) */
2750     n = nparm;
2751     freq = freqparm;
2752     len = lenparm;
2753     avail = n;
2754
2755 としている。引数で受けた入力をこのファイルの static 変数にセットし、他
2756 のルーチンとデータ共有しているようだ。avail は後で説明しよう。
2757
2758     /* (B) */
2759     heapsize = 0;
2760     heap[1] = 0;
2761     for (i = 0; i < n; i++) {
2762         len[i] = 0;
2763         if (freq[i])
2764             heap[++heapsize] = i;
2765     }
2766
2767 ここで、heap[] を初期化している。heapsize は、heap の要素数となる。こ
2768 の処理は優先待ち行列 heap[] を作る部分なのだが、なぜ優先待ち行列が必要
2769 なのかというと先の説明で Huffman 法のアルゴリズムに出現回数の少ない順
2770 に葉を節に束ねるという部分があった。優先待ち行列はこのためのものだ。と
2771 りあえず、heap[] の要素は圧縮する文字であるということだけを書いておく。
2772 詳細はすぐ後で出るだろう。freq[i] (すなわち引数 freqparm) は、文字 i 
2773 の出現回数を表している。だから、n (nparm)は、符号化するモデル上の文字
2774 の種類の数を表していることになる。たとえば通常のファイルなら nparm は 
2775 256 だろう。まあ、結局は freq[] の要素数だ。
2776
2777     nparm               最大要素数
2778     freqparm[0:nparm]   添字が文字で、その要素が出現回数
2779
2780 注意すべきなのは heap[] の要素は 1 以降を使用していることだろう。
2781 heap[0] は使われない。
2782
2783     /* (C) */
2784     if (heapsize < 2) {
2785         codeparm[heap[1]] = 0;
2786         return heap[1];
2787     }
2788
2789 これは、heapsize が 0 か 1 の場合を表している。符号化する文字の種類が 
2790 0 または 1 つしかない場合だ。heap[1] は、(B) で 0 に初期化していたので、
2791 codeparm[0] = 0 として、0 を返している。これは特殊な条件を示している。
2792 簡単に想像がつくことだが、出現する文字の種類が1種類しかない場合、ハフ
2793 マン木を作る必要がない。LHa ではこのような場合に特殊な構造あるいは方法
2794 を用いていることが想像できる。
2795
2796     /* (D) */
2797     for (i = heapsize / 2; i >= 1; i--)
2798         downheap(i);    /* make priority queue */
2799
2800 優先待ち行列 heap[] を構築する。downheap() がなんなのか、これがどういっ
2801 た処理なのかの詳細は省略しよう。アルゴリズム辞典の「ヒープソート」の項
2802 に詳しいが、heap[] は木構造を示しており、この木構造(2分木)には「親は子
2803 より優先順位が同じか高い」という規則だけがある。この木構造は、
2804
2805         1. heap[n] の左の子は heap[2*n]、右の子は heap[2*n + 1]
2806
2807 で、表現されており、このような半順序木 (partial ordered tree) には、以
2808 下の特徴がある
2809
2810         2. heap[n] の親は heap[n/2]
2811         3. heap[1.. heapsize/2] は節で、heap[heapsize/2 .. heapsize] は葉
2812
2813 この heap[] が最初ばらばらに要素が格納されているとき、葉に近い節から順
2814 に downheap() という操作を行う((D)の処理)と、ヒープを構築できるように
2815 なっている。downheap(i) は、節 heap[i] とその子 heap[2*i], heap[2*i+1] 
2816 で要素を比較し、子の優先順位の方が高ければ位置を交換する、という処理を
2817 葉に向かって繰り返す関数だ。以下、参考までに maketree.c:downheap() の
2818 内容を示す。
2819
2820 static void
2821 downheap(i)
2822 /* priority queue; send i-th entry down heap */
2823     int             i;
2824 {
2825     short           j, k;
2826
2827     k = heap[i];
2828     while ((j = 2 * i) <= heapsize) {
2829         if (j < heapsize && freq[heap[j]] > freq[heap[j + 1]])
2830             j++;
2831         if (freq[k] <= freq[heap[j]])
2832             break;
2833         heap[i] = heap[j];
2834         i = j;
2835     }
2836     heap[i] = k;
2837 }
2838
2839 とにかく (D) により、最も優先順位の高い(出現回数の少ない)要素が 
2840 heap[1] に来るようになる。この優先待ち行列はなかなか面白い(と私は思っ
2841 た)のでいろいろと調べてみるのもよいだろう。
2842
2843 さて、続けよう (E) だ。
2844
2845     /* (E) */
2846     sort = codeparm;
2847     do {            /* while queue has at least two entries */
2848         i = heap[1];    /* take out least-freq entry */
2849         if (i < n)
2850             *sort++ = i;
2851         heap[1] = heap[heapsize--];
2852         downheap(1);
2853         j = heap[1];    /* next least-freq entry */
2854         if (j < n)
2855             *sort++ = j;
2856         k = avail++;    /* generate new node */
2857         freq[k] = freq[i] + freq[j];
2858         heap[1] = k;
2859         downheap(1);    /* put into queue */
2860         left[k] = i;
2861         right[k] = j;
2862     } while (heapsize > 1);
2863
2864 最初に、
2865
2866         i = heap[1];    /* take out least-freq entry */
2867         if (i < n)
2868             *sort++ = i;
2869
2870 で、最も出現回数の少ない文字を取って来る。if の部分はひとまず無視しよう。
2871
2872         heap[1] = heap[heapsize--];
2873         downheap(1);
2874
2875 で、heap[] の最後尾の要素を先頭に持って来て downheap(1) を行っている。
2876 こうすると、ヒープが再構築され「親は子より優先順位が同じか高い」という
2877 条件をまた満たすようになる。heap[] の要素は1つ減っている。結局、ここま
2878 での処理は論理的には「優先待ち行列から優先度の高い要素を1つ取り出す」
2879 と言う処理だ。
2880
2881         j = heap[1];    /* next least-freq entry */
2882         if (j < n)
2883             *sort++ = j;
2884
2885 続けて、2番目に優先度の高い要素を取り出す。また、if は無視しておこう。
2886
2887         k = avail++;    /* generate new node */
2888         freq[k] = freq[i] + freq[j];
2889         heap[1] = k;
2890         downheap(1);    /* put into queue */
2891
2892 avail は最初 n (nparm)だった。freq[] は、文字の出現回数なので最初文字
2893 の種類数分(nparm)の要素しかないがハフマン木の節の出現回数(というか優先
2894 順位)を格納するために freq[] は、nparm * 2 - 1 の格納域が必要となるこ
2895 とがわかる。(葉が n 個ある 2 分木には、節が n - 1 個ある)
2896
2897 ----------------------------------------------------------------------------
2898
2899      +-----------------------+-----------------------+
2900 freq |                       |                       |
2901      +-----------------------+-----------------------+
2902      0                       nparm                   nparm * 2 - 1
2903
2904      |-----------------------|-----------------------|
2905       文字(ハフマン木の葉)    ハフマン木の節の優先順位
2906       の優先順位
2907
2908
2909       例:
2910                  .     ... freq[4]
2911                 / \
2912                .   \   ... freq[3]
2913               /\    \
2914              a  b    c ... freq[0 .. 2]
2915
2916 ----------------------------------------------------------------------------
2917
2918 ここまでで、出現回数の低い2つの要素を取り出しその出現回数の和を 
2919 freq[k] に設定することになる。出現回数の和は heap[] に再設定され、
2920 downheap(1) で、優先待ち行列をまた再構築する。これは「葉を束ね節を作る」
2921 というハフマン木の構築アルゴリズムの実装だ。新しく作成した節が k でそ
2922 の最大値が最終的に avail-1 である。
2923
2924 最後に
2925
2926         left[k] = i;
2927         right[k] = j;
2928
2929 で、ハフマン木を構造 left[]、right[] で作成する。
2930
2931 この (E) を抽象度の高いコードで示してみよう。ハフマン木は
2932     struct huffman {
2933        ...
2934     } huff;
2935
2936 で表し、ハフマン木の1つの節は、
2937     make_huff(huff, node, left, right)
2938 で作成できるとする。また、優先順位つき待ち行列を heap とし、heap から
2939 要素を取り出す処理、要素を格納する処理をそれぞれ仮に
2940         n = delete_pqueue(heap)
2941         insert_pqueue(heap, n)
2942 とすると、
2943
2944     /* (E) */
2945     do {
2946         left = delete_pqueue(heap);
2947         right = delete_pqueue(heap);
2948
2949         node = avail++;
2950         freq[node] = freq[left] + freq[right];
2951
2952         insert_pqueue(heap, freq[node]);
2953
2954         make_huff(&huff, node, left, right);
2955     } while (heapsize > 1);
2956
2957 こんなところだろうか。元の処理ではヒープからの要素の取り出しと挿入で無
2958 駄な処理を無くすために少し複雑になっている。(そしてデータ構造に依存し
2959 た処理になっている)。どちらがよりすぐれているかは微妙な所だ。私は多少
2960 の処理の無駄も目をつぶってよりわかりやすい方を優先するのが好きなのだが。
2961 これはちょっと考えさせられるところだ。
2962
2963 ループを抜けた所で k (avail - 1) は、ハフマン木の根を表している。
2964 left[0:avail], right[0:avail] でハフマン木を表し、そのうち
2965 left[nparm...avail], right[nparm...avail] が節の子を示している。
2966 left[0...nparm], right[0...nparm] は使われないようだ。
2967
2968 ----------------------------------------------------------------------------
2969       例:
2970                  . -- k (= avail-1)
2971                 / \
2972    left[k] --  .   \
2973               /\    \
2974              a  b    c -- right[k]
2975              |   \
2976              |    right[left[k]]
2977           left[left[k]]
2978
2979 ----------------------------------------------------------------------------
2980
2981 これで、ハフマン木の構築は終りなのだが、ハフマン法の符号化ではハフマン
2982 木の葉から根に向かって木を辿る必要があるはずなのに、left[]、right[] の
2983 構造では根から葉に向かってしか木を辿ることができないはずだ。これはどう
2984 いうことだろう?make_tree() ではまだ処理が続いている。
2985
2986     /* (F) */
2987     sort = codeparm;
2988     make_len(k);
2989     make_code(nparm, lenparm, codeparm);
2990     return k;       /* return root */
2991
2992 どうやら、木構造の他にもなにやら構造を作成しているようだ。これは先程無
2993 視した if 文にも関連する。そしてこれは「アルゴリズム辞典」には載ってい
2994 ない部分だ。どうやら LHa なりの工夫があるようだ。
2995
2996 まず、maketree.c:make_len(root) から見てみようと思うが、その前にこの関
2997 数は maketree.c:count_len(root) という関数を呼び出している。こちらから
2998 先に見ることにした。
2999
3000 static void
3001 count_len(i)            /* call with i = root */
3002     int             i;
3003 {
3004     static unsigned char depth = 0;
3005
3006     if (i < n)
3007         len_cnt[depth < 16 ? depth : 16]++;
3008     else {
3009         depth++;
3010         count_len(left[i]);
3011         count_len(right[i]);
3012         depth--;
3013     }
3014 }
3015
3016 この関数に渡される i は、最初ハフマン木の根を指す値だ。この関数の全体
3017 を見れば、i が節や葉を示すことはすぐわかる。最初の if 文に出てくる n 
3018 は何かというとなんとこのファイル内の static 変数で、make_tree() の冒頭
3019 で nparm で初期化していた。先程は気にもとめなかったのだが、変数名の選
3020 び方がどうにもよろしくない。とにかく n は、nparm で、freqparm の最初の
3021 要素数で、文字の種類の数を表していたものだ。ここではハフマン木の節や葉
3022 となる i と比較していることから、i がハフマン木の節を示すか葉を示すか
3023 の判断に使用しているらしい。if 文の条件が真の場合(i < n)、i は葉である。
3024 偽の場合 i は節である。偽の場合は、depth を足して二つの子に対して再帰
3025 的にこの関数を呼び出している。で、結局この関数が何をしているかというと、
3026 先ほど構築したハフマン木に関して、ある深さの葉の数を数えているようだ。
3027
3028 len_cnt[1] は、深さ 1 (根の子)の葉の数で 0 〜 2 の値になる。len_cnt[2] 
3029 は、深さ 2 (根の孫)の葉の数で 0 〜 4 の値を持つだろう。そして、深さ 16 
3030 以上の層に関しては len_cnt[16] にすべて計上されるようだ。とにかくその
3031 ような処理だということでこの関数を終え、make_len() を見よう。
3032
3033 static void
3034 make_len(root)
3035     int             root;
3036 {
3037     int             i, k;
3038     unsigned int    cum;
3039
3040     /* (A) */
3041     for (i = 0; i <= 16; i++)
3042         len_cnt[i] = 0;
3043     count_len(root);
3044     /* (B) */
3045     cum = 0;
3046     for (i = 16; i > 0; i--) {
3047         cum += len_cnt[i] << (16 - i);
3048     }
3049 #if (UINT_MAX != 0xffff)
3050     cum &= 0xffff;
3051 #endif
3052     /* (C) */
3053     /* adjust len */
3054     if (cum) {
3055         fprintf(stderr, "17");
3056         len_cnt[16] -= cum; /* always len_cnt[16] > cum */
3057         do {
3058             for (i = 15; i > 0; i--) {
3059                 if (len_cnt[i]) {
3060                     len_cnt[i]--;
3061                     len_cnt[i + 1] += 2;
3062                     break;
3063                 }
3064             }
3065         } while (--cum);
3066     }
3067     /* (D) */
3068     /* make len */
3069     for (i = 16; i > 0; i--) {
3070         k = len_cnt[i];
3071         while (k > 0) {
3072             len[*sort++] = i;
3073             k--;
3074         }
3075     }
3076 }
3077
3078 ちょっと複雑だがゆっくり見ていこう。まず (A) の初期化部分は先ほどの 
3079 count_len() を呼び出すだけのものなのでもうよいだろう。
3080
3081     /* (A) */
3082     for (i = 0; i <= 16; i++)
3083         len_cnt[i] = 0;
3084     count_len(root);
3085
3086 これで、len_cnt[1..16] にはハフマン木の各層の葉の数が計上される。続いて (B)
3087
3088     /* (B) */
3089     cum = 0;
3090     for (i = 16; i > 0; i--) {
3091         cum += len_cnt[i] << (16 - i);
3092     }
3093 #if (UINT_MAX != 0xffff)
3094     cum &= 0xffff;
3095 #endif
3096
3097 これは、どういうことだろう?len_cnt[] は short の配列だが、とにかく以
3098 下のような計算(len_cnt[] の要素を 1 ビットずらしながら足す)をしている。
3099 最後に int 型のサイズが 2 でない場合 0xffff で論理積をしているので 2バ
3100 イトの符号なし整数を結果として欲しいらしい。
3101
3102 ----------------------------------------------------------------------------
3103                 f e d c b a 9 8 7 6 5 4 3 2 1 0  bit
3104   len_cnt[16]  |x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|   (下位 16ビット)
3105 + len_cnt[15]  |x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|0|   (下位 15ビット)
3106 + len_cnt[14]  |x|x|x|x|x|x|x|x|x|x|x|x|x|x|0|0|   (下位 14ビット)
3107 +     :                                                   :
3108 + len_cnt[ 2]  |x|x|0|0|0|0|0|0|0|0|0|0|0|0|0|0|   (下位 2 ビット)
3109 + len_cnt[ 1]  |x|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|   (下位 1 ビット)
3110 & 0xffff       |1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|
3111 ------------------------------------------------
3112 = cum           x x x x x x x x x x x x x x x x
3113 ----------------------------------------------------------------------------
3114
3115 ここで、len_cnt[] の各要素の値が各層の葉の数であることを考えると、各要
3116 素で使用するビット数との関連が見える。
3117
3118               取り得る
3119               値の範囲      使用ビット数
3120  -----------------------------------------
3121  len_cnt[16]  0.. 2^16以上  17ビット以上
3122  len_cnt[15]  0.. 2^15      16ビット
3123  len_cnt[14]  0.. 2^14      15ビット
3124      :
3125  len_cnt[ 3]  0.. 2^3        4 ビット
3126  len_cnt[ 2]  0.. 2^2        3 ビット
3127  len_cnt[ 1]  0.. 2^1        2 ビット
3128
3129 先の計算式では len_cnt[] の各要素で使用し得るビット数から 1 引いたビッ
3130 ト数が計算に使用されている。例えば根の子がすべて葉なら len_cnt[1] は、
3131 2 になり、2進で、00000000 00000010 だが、cum の計算にはこの下位 1 ビッ
3132 トしか使用されない。
3133
3134        /\
3135       a  b     .. len_cnt[1] = 00000000 00000010
3136                                                |
3137                                                v
3138                                          cum = x0000000 00000000
3139
3140 根の孫がすべて葉なら len_cnt[2] は、4 になり、2進で、00000000 00000100 
3141 だが、cum の計算にはこの下位 2 ビットしか使用されない。
3142
3143        / \     .. len_cnt[1] = 00000000 00000000
3144       /\ /\
3145      a b c d   .. len_cnt[2] =  00000000 00000100
3146                                                ||
3147                                                vv
3148                                          cum = xx000000 00000000
3149
3150 このようにある層のすべてが葉であるようなバランスのよいハフマン木に
3151 対しては先の計算結果 cum は 0 になるらしい。
3152
3153 また、
3154        /\
3155       a /\     .. len_cnt[1] = 00000000 00000001
3156         b c    .. len_cnt[2] =  00000000 00000010
3157                                                ||
3158                                                vv
3159                                          cum = xx000000 00000000
3160
3161 のような木に対しても計算結果はオーバーフローされ cum は 0 になる。
3162
3163        /\
3164       a /\       .. len_cnt[1] = 00000000 00000001
3165        b /\      .. len_cnt[2] =  00000000 00000001
3166         c  d     .. len_cnt[3] =   00000000 00000010
3167                                                  |||
3168                                                  vvv
3169                                            cum = xxx00000 00000000
3170
3171 も同様に cum は 0 だ。結局 cum が 0 にならない木とはある節が 1 つの葉
3172 しかもたないような場合であるらしい。
3173
3174        /\
3175       a /\       .. len_cnt[1] = 00000000 00000001
3176        b  \      .. len_cnt[2] =  00000000 00000001
3177            d     .. len_cnt[3] =   00000000 00000001
3178                                                  |||
3179                                                  vvv
3180                                            cum = 11100000 00000000
3181
3182 そして、ハフマン木の作り方からこのようなことは起こりえないのではないか
3183 と思える。
3184
3185 (C) では、if (cum) で、この起こりえないハフマン木の場合になにか処理を
3186 行っている。まったく謎であるが、まず、この (C) を特殊条件とみなして先
3187 に (D) の方を見ることにしよう。
3188
3189     /* (D) */
3190     /* make len */
3191     for (i = 16; i > 0; i--) {
3192         k = len_cnt[i];
3193         while (k > 0) {
3194             len[*sort++] = i;
3195             k--;
3196         }
3197     }
3198
3199 sort は何かというと、make_tree() の引数に渡された codeparm を指してい
3200 る。この配列の中には(ハフマン木を構築する際に設定されていたのだが)、出
3201 現頻度の低い順で平文の文字コードが入っている。make_tree() で、sort に
3202 値を設定する際、
3203
3204         if (j < n)
3205             *sort++ = j;
3206
3207 のように条件判断があったので、sort[] にはハフマン木の節は入っていない。
3208 そしてハフマン木はその構築の仕方から出現頻度の低い文字は木のより深い場
3209 所に位置づけられている。これらのことから make_len()で求めようとしてい
3210 るものが何なのかがわかる。make_len() は、
3211     len[文字] = ハフマン木の深さ
3212 といった対応表を作成する処理だ。さらに言うとハフマン木の深さは文字を符
3213 号化した結果のビット数を表すことから
3214     lenparm[文字] = 符号語のビット数
3215 といった対応表を作成する処理であると言った方が正解だろう。
3216 len[] は、make_tree() の冒頭で、lenparm を指すように設定された変数なの
3217 で、そのように置き換えておいた。
3218
3219 では、謎の (C) を見よう。その前に cum != 0 は起こりえないと書いたがよ
3220 く考えたら len_cnt[16] だけは深さ16以上の葉すべての数を計上しているた
3221 め、どのような値もあり得る。つまり、この (C) の処理はハフマン木が深さ 
3222 17 以上になったときに処理されるものだと言えそうだ。思い切って図示しよ
3223 う例えばこんな木は、(C)の処理対象となる。
3224
3225        /\
3226       a /\       .. len_cnt[ 1] = 0000000000000001
3227        b /\       .. len_cnt[ 2] = 0000000000000001
3228         c /\       .. len_cnt[ 3] = 0000000000000001
3229          d /\       .. len_cnt[ 4] = 0000000000000001
3230           e /\       .. len_cnt[ 5] = 0000000000000001
3231            f /\       .. len_cnt[ 6] = 0000000000000001
3232             g /\       .. len_cnt[ 7] = 0000000000000001
3233              h /\       .. len_cnt[ 8] = 0000000000000001
3234               i /\       .. len_cnt[ 9] = 0000000000000001
3235                j /\       .. len_cnt[10] = 0000000000000001
3236                 k /\       .. len_cnt[11] = 0000000000000001
3237                  l /\       .. len_cnt[12] = 0000000000000001
3238                   m /\       .. len_cnt[13] = 0000000000000001
3239                    n /\       .. len_cnt[14] = 0000000000000001
3240                     o /\       .. len_cnt[15] = 0000000000000001
3241                      p /\       .. len_cnt[16] = 0000000000000011
3242                       q  r                       ||||||||||||||||
3243                                                  vvvvvvvvvvvvvvvv
3244                                            cum = 0000000000000001
3245
3246 このような木は例えば各文字が以下の出現頻度となるファイルを作成すると起
3247 こる(実際には、LHA の場合、slide 辞書法の処理もあるのでこれほど単純で
3248 はない)。
3249
3250         文字    頻度        文字    頻度
3251         ------------        ------------
3252         r          1        i        256
3253         q          1        h        512
3254         p          2        g       1024
3255         o          4        f       2048
3256         n          8        e       4096
3257         m         16        d       8192
3258         l         32        c      16384
3259         k         64        b      32768
3260         j        128        a      65536
3261
3262 ところで、cum の値は何なのかというと、
3263
3264                                                         :
3265                                .. len_cnt[15] = 0000000000000001
3266                      p /\       .. len_cnt[16] = 0000000000000100
3267                       q /\                       ||||||||||||||||
3268                        r  s                      vvvvvvvvvvvvvvvv
3269                                            cum = 0000000000000010
3270
3271 この場合は cum = 2 だ。
3272                                                         :
3273                                .. len_cnt[15] = 0000000000000001
3274                      p /\       .. len_cnt[16] = 0000000000000101
3275                       q /\                       ||||||||||||||||
3276                        r /\                      vvvvvvvvvvvvvvvv
3277                         s  t               cum = 0000000000000011
3278
3279 この場合は cum = 3 だ。少なくともこの例では深さ 16 以上の葉の数 - 2に
3280 なるらしい(そうか、11111111 11111110 = -2 を足しているのだから当然だ)。
3281
3282 では、今度こそ (C) を見る。
3283
3284     /* (C) */
3285     /* adjust len */
3286     if (cum) {
3287         fprintf(stderr, "17");
3288         len_cnt[16] -= cum; /* always len_cnt[16] > cum */
3289         do {
3290             for (i = 15; i > 0; i--) {
3291                 if (len_cnt[i]) {
3292                     len_cnt[i]--;
3293                     len_cnt[i + 1] += 2;
3294                     break;
3295                 }
3296             }
3297         } while (--cum);
3298     }
3299
3300 である。いきなり fprintf() しているところがすごい。デバッグ用の出力が
3301 残っているのだろう。LHa for UNIX で 17 という出力を見たことがある人は
3302 教えて欲しい。
3303
3304 で・・・、結局この (C) の部分は見てもよくわからなかった。ここまで書い
3305 ておいてなんだが、気持ちよく無視することにした。
3306
3307 では、make_tree() から呼び出される最後の関数、maketree.c:make_code() 
3308 を見よう。make_code() は、make_tree() の(F) の部分で以下のように呼ばれ
3309 ていた。
3310
3311     make_code(nparm, lenparm, codeparm);
3312
3313 この引数のうち、lenparm[] は先ほどの make_len[] で値が作られたものだが、
3314     lenparm[文字] = 符号語のビット数
3315 という対応表だった。codeparm[] は、先ほども出たが make_tree() の中で設
3316 定されているものだった。出現頻度の低い順で平文の文字コードが入った配列
3317 だ。
3318
3319 void
3320 make_code(n, len, code)
3321     int             n;
3322     unsigned char   len[];
3323     unsigned short  code[];
3324 {
3325     unsigned short  weight[17]; /* 0x10000ul >> bitlen */
3326     unsigned short  start[17];  /* start code */
3327     unsigned short  j, k;
3328     int             i;
3329
3330     j = 0;
3331     k = 1 << (16 - 1);
3332     for (i = 1; i <= 16; i++) {
3333         start[i] = j;
3334         j += (weight[i] = k) * len_cnt[i];
3335         k >>= 1;
3336     }
3337     for (i = 0; i < n; i++) {
3338         j = len[i];
3339         code[i] = start[j];
3340         start[j] += weight[j];
3341     }
3342 }
3343
3344 # 後で気がついたことだが、あらかじめ設定していた codeparm[] の内容はこ
3345 # こでは使用されない。つまり、codeparm[] は先程はワーク用のバッファと
3346 # して利用されていたが、ここでの codeparm[] は処理結果を表すという二つ
3347 # の役割がある。これは、領域を節約するための変数の使い回しだ。
3348
3349 最初の for 文では、変数 i に対して、weight[i] が下のように設定される
3350
3351   weight[i=1..16] = 2^(16-i)
3352
3353 そして、start[i] は、
3354
3355   start[1] = 0
3356   start[n] = start[n-1] + weight[n-1] * len_cnt[n-1]   (n > 1)
3357
3358 という漸化式だ。start[] の添字 i は、len_cnt[i](深さ i の葉の数)の添字
3359 でもあることから、ハフマン木の深さを表している。start が実際にどのよう
3360 な値を取るかと言うと、例えば len_cnt[i] の各要素が Li であった場合、
3361
3362      i     len_cnt[i]   weight[i]   start[i]
3363  --------------------------------------------
3364      1         L1        2^15       0
3365      2         L2        2^14      2^15 * L1
3366      3         L3        2^13      2^15 * L1 + 2^14 * L2
3367      4         L4        2^12      2^15 * L1 + 2^14 * L2 + 2^13 * L3
3368
3369 こんな感じだ。これはいったい何だろうか?続く for 文を見てみよう。
3370
3371     for (i = 0; i < n; i++) {
3372         j = len[i];
3373         code[i] = start[j];
3374         start[j] += weight[j];
3375     }
3376
3377 ここでの i は、0...n の範囲であることから平文の文字を示す。紛らわしい
3378 ので、i は、c に書き換え、j を i にしよう。(これで、i は前の for 文の 
3379 i と同じ意味になる)
3380
3381     int c;
3382
3383     for (c = 0; c < n; c++) {
3384         i = len[c];
3385         code[c] = start[i];
3386         start[i] += weight[i];
3387     }
3388
3389 i = len[c] は文字 c のビット長で、ハフマン木の深さを示す。
3390 code[c] には、start[i] が設定されるが、一度 start[i] が参照されると
3391 start[i] は、weight[i] を足した値が次回使われる。例えば、ある文字
3392 a, b, c がそれぞれ以下のハフマン木で表現されたとする。
3393
3394        /\               a: 0
3395       a /\              b: 10
3396         b c             c: 11
3397
3398
3399               i     len_cnt[i]   weight[i]   start[i]
3400           --------------------------------------------
3401               1         1         2^15        0
3402               2         2         2^14       2^15
3403
3404   c  len[c]   i     len_cnt[i]   weight[i]   code[c]
3405  ---------------------------------------------------
3406   a     1     1         1            2^15       0
3407   b     2     2         2            2^14      2^15
3408   c     2     2         2            2^14      2^15 + 2^14
3409
3410 こんな感じになる。別のハフマン木の場合も見てみよう。
3411
3412         /\                a: 00
3413       /\  /\              b: 01
3414      a  b c d             c: 10
3415                           d: 11
3416
3417               i     len_cnt[i]   weight[i]   start[i]
3418           --------------------------------------------
3419               1         0         2^15        0
3420               2         4         2^14        0
3421               3         0         2^13       2^14 * 4
3422
3423   c  len[c]   i     len_cnt[i]   weight[i]   code[c]
3424  ---------------------------------------------------
3425   a     2     2         4            2^14      0
3426   b     2     2         4            2^14      2^14
3427   c     2     2         4            2^14      2^14 * 2
3428   d     2     2         4            2^14      2^14 * 3
3429
3430 これで、ピント来ると凄いのだが code[c] には文字 c に対応する符号化した
3431 ビット列が設定されるようになっていることに気づくだろうか?つまりは、
3432
3433   c  len[c]   i     len_cnt[i]   weight[i]   code[c]
3434  -----------------------------------------------------------
3435   a     2     2         4            2^14  00000000 00000000
3436   b     2     2         4            2^14  01000000 00000000
3437   c     2     2         4            2^14  10000000 00000000
3438   d     2     2         4            2^14  11000000 00000000
3439                                            ^^ <- ハフマン符号
3440
3441 だ。以降、code[] (実際には codeparm) を利用することで表引きで文字 c に
3442 対応するハフマン符号を得ることができるようになっている(code[c]のうち上
3443 位 len[c] ビットだけを見る)。符号化の際に木を辿る必要はなく、高速な符号
3444 化が可能になる(と期待される。どの程度効果があるかはいずれ検証してみた
3445 い。そういえば、葉から根に向かって木を辿るための情報が必要なかったこと
3446 もこれでわかった)。
3447
3448 結局 make_tree(nparm, freqparm, lenparm, codeparm) は、lenparm[c] と 
3449 codeparm[c] を作成する関数だったわけだ(ハフマン表とでも言うのだろう
3450 か?)。実は、このことは make_tree() を呼び出し、 codeparm を使用してい
3451 る箇所(huf.c)を見るまでまるでわからなかった。しかも、まだちゃんと理解
3452 したわけではない。
3453
3454 ふと思ったのだが、上記の表作成は文字コード順に依存している(コードの若
3455 い方が左の子になる)が、木を作成する場合はそのようなことはなかったはず
3456 だ。ハフマン木を辿った場合と表を参照した場合とでは得られる符号語は異な
3457 るのではないだろうか?ということは圧縮文に埋め込まれるのは木ではなくこ
3458 の表の方だということも想像がつく。木の構造を表す left[]、right[] はグ
3459 ローバル変数だが、実際には make_tree() 内でしか使われないのかも知れな
3460 い(少なくとも符号化に関してはそのようだ。huf.c を眺めるとどうやら復号
3461 時は left[]、right[]は使われるらしい)。
3462
3463 さらにふと思い付いた。謎の (C) のコードだが 深さ 17 以上の木が作成され
3464 た場合に木を再構築する処理だということがわかった。最初、len_cnt[] (あ
3465 る深さの葉の数) だけが、操作されていたからよくわからなかったのだ、
3466
3467     /* (C) */
3468     /* adjust len */
3469     if (cum) {
3470         fprintf(stderr, "17");
3471         len_cnt[16] -= cum; /* always len_cnt[16] > cum */
3472         do {
3473             for (i = 15; i > 0; i--) {
3474                 if (len_cnt[i]) {
3475                     len_cnt[i]--;
3476                     len_cnt[i + 1] += 2;
3477                     break;
3478                 }
3479             }
3480         } while (--cum);
3481     }
3482
3483 深さ i の葉の数を 1 つ減らして、その下の葉の数を 2 足している。
3484 これが、cum の数だけ繰り返される。例えば、前にも出た
3485
3486                                                      :
3487                    n /\       .. len_cnt[14] = 0000000000000001
3488                     o /\       .. len_cnt[15] = 0000000000000001
3489                      p /\       .. len_cnt[16] = 0000000000000011
3490                       q  r                       ||||||||||||||||
3491                                                  vvvvvvvvvvvvvvvv
3492                                            cum = 0000000000000001
3493
3494 の例では、最初に len_cnt[16] から cum {1} が引かれ、
3495
3496                                                      :
3497                    n /\       .. len_cnt[14] = 0000000000000001
3498                     o /\       .. len_cnt[15] = 0000000000000001
3499                      p /       .. len_cnt[16] = 0000000000000010
3500                       q
3501
3502 続いて、深さ 15 より上の葉のある節から 1 つ子を取り、
3503
3504                                                      :
3505                    n /\       .. len_cnt[14] = 0000000000000001
3506                       /\       .. len_cnt[15] = 0000000000000000
3507                      p /        .. len_cnt[16] = 0000000000000010
3508                       q
3509
3510 下の葉の数(この例では、len_cnt[16])を 2 足している。
3511
3512                 /   \
3513               n    /  \       .. len_cnt[14] = 0000000000000001
3514                  /\    /\      .. len_cnt[15] = 0000000000000000
3515                 o  r  p /       .. len_cnt[16] = 0000000000000100
3516                        q
3517
3518 cum は、この例では 0 になるので、これで木の平滑化は終る。テキストだと
3519 ちょっと見にくいが、そういう処理ということで間違いないだろう。
3520 lenparm[] の値はこの後の (D) で、この木を元に計算されている。
3521
3522 ところで、本当の所は以下のような文字の対応になる(表を作成するときに文
3523 字コード順になっているため)のだが、結果的に元の木から p を含む節を取り
3524 除き o の位置に挿入する処理になっている。なんだか面白い。
3525
3526                 /   \
3527               n    /  \       .. len_cnt[14] = 0000000000000001
3528                  /\    /\      .. len_cnt[15] = 0000000000000000
3529                 o  p  q  r      .. len_cnt[16] = 0000000000000100
3530
3531 文字から Huffman 符号が得られるようになったので、圧縮処理を行う道具は
3532 揃った。いよいよ Huffman 法による圧縮処理全般 (huf.c) を見ることにしよ
3533 う。
3534
3535 まず huf.c で定義されているデータ構造から確認しよう。データ構造がわかっ
3536 てしまえばアルゴリズムの 90% はわかったも同然だ(誇張)。
3537
3538 huf.c には以下の変数が定義されている。
3539
3540 unsigned short  left[2 * NC - 1], right[2 * NC - 1];
3541 unsigned char   c_len[NC], pt_len[NPT];
3542 unsigned short  c_freq[2 * NC - 1], c_table[4096], c_code[NC], p_freq[2 * NP - 1],
3543                 pt_table[256], pt_code[NPT], t_freq[2 * NT - 1];
3544
3545 static unsigned char *buf;
3546 static unsigned int bufsiz;
3547 static unsigned short blocksize;
3548 static unsigned short output_pos, output_mask;
3549 static          int   pbit;
3550 static          int   np;
3551
3552 使用されている定数も確認する lha_macro.h だ。
3553
3554 #define NP          (MAX_DICBIT + 1)
3555 #define NT          (USHRT_BIT + 3)
3556 #define PBIT        5       /* smallest integer such that (1 << PBIT) > * NP */
3557 #define TBIT        5       /* smallest integer such that (1 << TBIT) > * NT */
3558 #define NC          (UCHAR_MAX + MAXMATCH + 2 - THRESHOLD)
3559 /*      #if NT > NP #define NPT NT #else #define NPT NP #endif  */
3560 #define NPT         0x80
3561 #define CBIT                9   /* $\lfloor \log_2 NC \rfloor + 1$ */
3562
3563 たくさんある。たくさんありすぎてくじけそうだが(事実、くじけた)、現時点
3564 でわかる変数もある。left[] や right[] は Huffman 木を構築するのに使用
3565 した変数だった。そこから NC は文字種の最大数であることがわかる。NC に 
3566 MAXMATCH{256} 等の値が使用されているのは謎だが、現時点では無視しておこ
3567 う。
3568
3569 c_freq[] や c_len[], p_freq[], pt_len[] も make_tree() で出て来た変数
3570 名に似ている。おそらく make_tree() に渡す変数だろう。確認してみたとこ
3571 ろ huf.c から make_tree() の呼び出しを行っている部分を抜き出すと、
3572
3573     root = make_tree(NC, c_freq, c_len, c_code);
3574     root = make_tree(NT, t_freq, pt_len, pt_code);
3575     root = make_tree(np, p_freq, pt_len, pt_code);
3576
3577 の 3 箇所が出て来た。つまり、
3578
3579    文字種の数  文字の出現回数   符号化した文字  文字に対応する
3580                                 の bit 長       Huffman 符号表
3581    -----------------------------------------------------------
3582      NC         c_freq          c_len           c_code
3583      NT         t_freq          pt_len          pt_code
3584      np         p_freq          pt_len          pt_code
3585
3586 という関係のようだ。どうやら c_code、pt_code という 2 種類の
3587 Huffman 符号表を使用するらしい。
3588
3589 # あとでわかることだが実際は 3 種類の Huffman 符号表を作っており
3590 # pt_code は変数が使い回しされている。変数の使用領域を減らし
3591 # たかったのだろう。
3592
3593 その他の変数に関しても予想を立てたい所だが、もうくじけたので、処理内容
3594 から攻めることにした。
3595
3596 slide 辞書法の解読で Huffman 法に関連した処理の呼び出しがいくつかあっ
3597 た。
3598
3599     /* initialize */
3600     alloc_buf()
3601
3602     /* encoder */
3603     encode_set.encode_start()
3604     encode_set.output(c, off)
3605     encode_set.encode_end()
3606
3607     /* decoder */
3608     decode_set.decode_start()
3609     decode_set.decode_c()
3610     decode_set.decode_p()
3611
3612 以上だ。lh4, 5, 6, 7 では、上記のそれぞれは、huf.c の以下の関数の呼び
3613 出しに対応している。これは、slide.c の冒頭部分で定義されている。
3614
3615     /* encoder */
3616     encode_start() -> encode_start_st1()
3617     output()       -> output_st1()
3618     encode_end()   -> encode_end_st1()
3619
3620     /* decoder */
3621     decode_start() -> decode_start_st1()
3622     decode_c()     -> decode_c_st1()
3623     decode_p()     -> decode_p_st1()
3624
3625 このうちの圧縮処理にあたる部分 encode_start_st1(), output_st1(),
3626 encode_end_st1() を見ていこう。まずは、初期化処理である 
3627 encode_start_st1() から、
3628
3629 void
3630 encode_start_st1( /* void */ )
3631 {
3632     int             i;
3633
3634     if (dicbit <= 13) {
3635         pbit = 4;   /* lh4,5 etc. */
3636         np = 14;
3637     } else {
3638         pbit = 5;   /* lh6,7 */
3639         if (dicbit == 16)
3640             np = 17;
3641         else
3642             np = 16;
3643     }
3644
3645     for (i = 0; i < NC; i++)
3646         c_freq[i] = 0;
3647     for (i = 0; i < np; i++)
3648         p_freq[i] = 0;
3649     output_pos = output_mask = 0;
3650     init_putbits();
3651     buf[0] = 0;
3652 }
3653
3654 dicbit (これは辞書の bit サイズだった)によって、np, pbit の値が変わる。
3655 dicbit の違いというのは LHa の encoding メソッドの違いだ。それぞれ以下
3656 の対応になる。
3657
3658     method  dicbit  np  pbit
3659    --------------------------
3660     -lh4-   12      14  4
3661     -lh5-   13      14  4
3662     -lh6-   15      16  5
3663     -lh7-   16      17  5
3664
3665 np というのは、先程 make_tree() を呼び出している箇所の洗い出しで見かけ
3666 た変数だった。まだ、この関連はよくわからない。
3667
3668 処理の後半では、文字の出現頻度を表す c_freq[]、p_freq[] の初期化を
3669 行っている。さらに
3670
3671     output_pos
3672     output_mask
3673     buf[]
3674
3675 という初出の変数も 0 に初期化している。(buf は、buf[0] のみ初期化して
3676 いる) init_putbits() の呼び出しは bit 出力ルーチンの初期化処理だった。
3677 以降、putbits(), putcode() を使用できる。
3678
3679 次に output_st1(c, p) を見る。slide.c でこの関数は以下のように使用され
3680 ていた。
3681
3682         output_st1(c, 0)        文字 c を出力
3683         output_st1(len, off)    <len, off> のペアを出力
3684
3685 このことを踏まえた上で、処理内容を見てみよう。
3686
3687 void
3688 output_st1(c, p)
3689     unsigned short  c;
3690     unsigned short  p;
3691 {
3692     static unsigned short cpos;
3693
3694     /* (A) */
3695     output_mask >>= 1;
3696     if (output_mask == 0) {
3697         output_mask = 1 << (CHAR_BIT - 1);
3698         if (output_pos >= bufsiz - 3 * CHAR_BIT) {
3699             send_block();
3700             if (unpackable)
3701                 return;
3702             output_pos = 0;
3703         }
3704         cpos = output_pos++;
3705         buf[cpos] = 0;
3706     }
3707     /* (B) */
3708     buf[output_pos++] = (unsigned char) c;
3709     c_freq[c]++;
3710     /* (C) */
3711     if (c >= (1 << CHAR_BIT)) {
3712         buf[cpos] |= output_mask;
3713         buf[output_pos++] = (unsigned char) (p >> CHAR_BIT);
3714         buf[output_pos++] = (unsigned char) p;
3715         c = 0;
3716         while (p) {
3717             p >>= 1;
3718             c++;
3719         }
3720         p_freq[c]++;
3721     }
3722 }
3723
3724 (A) は、output_mask の値に応じて処理を行うようだ。初期化で output_mask 
3725 は 0 だから (A) の処理は最初から実行されるが、ひとまず無視しよう。
3726
3727 (B) は、buf に引数で渡された文字 c を格納し、c_freq[c] の値(文字の出現
3728 頻度)を足している。どうやら基本は渡された文字 c を延々と buf に格納し、
3729 後で圧縮を行う(おそらく (A) だろう)ようだ。
3730
3731 この buf のサイズはと言うと、これは alloc_buf() で割り当てられていた。
3732
3733 unsigned char  *
3734 alloc_buf( /* void */ )
3735 {
3736     bufsiz = 16 * 1024 *2;  /* 65408U; */ /* t.okamoto */
3737     while ((buf = (unsigned char *) malloc(bufsiz)) == NULL) {
3738         bufsiz = (bufsiz / 10) * 9;
3739         if (bufsiz < 4 * 1024)
3740             break;
3741     }
3742     return buf;
3743 }
3744
3745 bufsiz が buf のサイズらしい。これはできるだけ大きく取るようにしている
3746 が、大きく取れなければそれはそれで良いようだ。
3747
3748 さらに、(C) の処理を行うかどうかは、c >= (1 << CHAR_BIT) という条件で
3749 判断されている。この条件が真となる場合は何かと言うと c が「長さ」を表
3750 す場合だ。このとき引数 p で「位置」が渡されているのでこれも buf にセッ
3751 トしている。その具体的内容はというと、何やら cpos というこの関数内で 
3752 static な変数が使用されている。よくわからないが文字 c や <len,off> の
3753 ペアは、buf 上で以下のように表されるらしい。
3754
3755 ----------------------------------------------------------------------------
3756
3757 output_st1(c1, 0)
3758 output_st1(c2, 0)
3759 output_st1(len, off)
3760
3761 と呼び出した場合の buf の状態
3762
3763     +-----+-----+-----+-----+-----+
3764 buf | c1  | c2  | len |    off    |
3765     +-----+-----+-----+-----+-----+
3766
3767 ----------------------------------------------------------------------------
3768
3769 (C) の処理の最後の部分
3770
3771         c = 0;
3772         while (p) {
3773             p >>= 1;
3774             c++;
3775         }
3776         p_freq[c]++;
3777
3778 は、出現頻度 p_freq[] を求める処理だが、p_freq は、off 値の出現頻度を
3779 表しているらしい。ここでの c は、p (off) の bit 長になっている。off の
3780 値は大きい(辞書サイズだから最大(lh7)で、64KB)ので、代わりに bit 長を利
3781 用しているといったところか。そういえば、np という変数があり、
3782 make_tree() の第一引数に渡されることから、これは、p_freq[] の要素数を
3783 表す。p_freq[] の要素数とは、<off> の bit 長の最大値+1なので、lh7 で、
3784 64KB。つまり 16 bit + 1 が np になる。
3785
3786 ついでに言うと、「長さ」はそのまま c_freq[] で頻度が計上されていた。同
3787 じく make_tree() の第一引数に渡される NC の値が
3788
3789 #define NC          (UCHAR_MAX + MAXMATCH + 2 - THRESHOLD)
3790
3791 なのは、そういうことだ(長さを考えなければ文字の最大値{255}+1となるとこ
3792 ろだが、長さの最大値が、256 + MAXMATCH - THRESHOLD だから上の式になっ
3793 ているのだろうと思う。ちょっとわかりにくいが)
3794
3795 ここまでで、圧縮を行う処理は現われなかった。やはり (A) の部分が圧縮処
3796 理だ。
3797
3798     /* (A) */
3799     output_mask >>= 1;
3800     if (output_mask == 0) {
3801         output_mask = 1 << (CHAR_BIT - 1);
3802         if (output_pos >= bufsiz - 3 * CHAR_BIT) {
3803             send_block();
3804             if (unpackable)
3805                 return;
3806             output_pos = 0;
3807         }
3808         cpos = output_pos++;
3809         buf[cpos] = 0;
3810     }
3811
3812 最初、output_mask は、0 なので if 条件を満たす。そして、output_mask は、
3813 (1 << (CHAR_BIT - 1)) つまり、128 になる。(A) の冒頭で、>>= 1 している
3814 ので、output_mask は、128, 64, 32, ..., 1, 128 という値を取るらしい。そ
3815 して、本当の初期値は 128 だ。
3816
3817 次の条件
3818
3819   output_pos >= bufsiz - 3 * CHAR_BIT
3820
3821 というのは、buf が bufsiz - 24 よりも大きくなったときの値だ。ひとまず
3822 無視しよう。そして、cpos = output_pos++ として、buf[cpos] = 0 にセット
3823 されている。どうやら、先にこちらを見るべきだったようだ。cpos の値
3824 と output_pos++ が (A) で行われていることを踏まえてもう一度 (B)、(C)
3825 の処理を見ると、buf は以下のように使用されているらしい。
3826
3827 ----------------------------------------------------------------------------
3828
3829 output_st1(c1, 0)
3830 output_st1(c2, 0)
3831 output_st1(len, off)
3832
3833 と呼び出した場合の buf の状態
3834
3835
3836     +-----+-----+-----+-----+-----+-----+--
3837 buf |  32 | c1  | c2  | len |    off    |  ...
3838     +-----+-----+-----+-----+-----+-----+--
3839    cpos                                output_pos
3840
3841 ----------------------------------------------------------------------------
3842
3843 <len, off> のペアを出力するとき buf[cpos] には以下のような値が設定され
3844 ていたことも図に書いてある。
3845
3846         buf[cpos] |= output_mask;
3847
3848 もう少し注意深くこのあたりを考えよう。output_mask は、この関数が呼ばれ
3849 るたびに 128, 64, 32, ..., 1, 128, 64, ... という値になる。そして、buf 
3850 は、呼ばれるたびに c (1バイト)、あるいは <len, off> (3バイト)の値が設
3851 定されるが、output_mask が 128 になったときは、余分に 1 バイト空きがで
3852 きる(これは、buf[cpos]で示される)。この空きには <len,off> が設定される
3853 たびにその時点の output_mask 値が設定されるようだ。(A) が呼ばれるとき
3854 と言うのは、一番最初の output_mask = 0 の場合を除けば、
3855
3856 ----------------------------------------------------------------------------
3857
3858 output_mask  128  64   32             16   8              4    2    1
3859         +----+----+----+----+----+----+----+----+----+----+----+----+----+
3860 buf     | 40 | c1 | c2 |len |   off   | c4 |len |   off   | c6 | c7 | c8 |
3861         +----+----+----+----+----+----+----+----+----+----+----+----+----+
3862         cpos                                                            /
3863                                                                        /
3864                                                                output_pos
3865    buf[cpos] = 32 + 8
3866
3867 ----------------------------------------------------------------------------
3868
3869 このような状態になったときということだ。さらに、buf[cpos] には、
3870 <len,off> が格納されている位置を表している。この状態を 1 セグメントと呼
3871 ぶことにしよう。そしてこのセグメント単位に情報が buf に格納され、buf が
3872 いっぱいになったらこのセグメントの集まりを 1 ブロックとして (A) の処理
3873 の無視したif 文の中身で
3874
3875         if (output_pos >= bufsiz - 3 * CHAR_BIT) {
3876             send_block();
3877             if (unpackable)
3878                 return;
3879             output_pos = 0;
3880         }
3881
3882 のように send_block() が呼ばれるようになっているようだ。この if の条件
3883 で、3 * CHAR_BIT というのは <len, off> の格納バイト数を示している。
3884 (と思ったが、3 * CHAR_BIT ではビット数だ。一方、bufsiz はバイト数だ。
3885 計算に使用している単位が違う。何やらバグっぽい雰囲気があるが、バグだと
3886 してもバッファをちょっとだけ無駄にしているだけなので大したことはないの
3887 だろう)
3888
3889 # どうやらバグではないらしい。3 * CHAR_BIT というのは、1 セグメントが
3890 # CHAR_BIT のスロット(8 つのスロット)を持ち、1 セグメント内のスロットが
3891 # すべて <len,off> (3 bytes)の場合、最大 3 bytes * 8 となることを示して
3892 # いるようだ。
3893 # CHAR_BIT は、buf[cpos] のビット数を表している。
3894 #
3895 # 実際のところ 1 セグメントは、buf[cpos] の領域 1 byte が先頭に必要なの
3896 # で最大サイズは
3897 #       3 * CHAR_BIT + 1
3898 # となる。そういう意味では、
3899 #       if (buf の残りサイズ < 最大サイズ) {
3900 # という形式。つまり、
3901 #       if (bufsiz - output_pos < 3 * CHAR_BIT + 1) {
3902 # の方がわかりやすいように思う。
3903
3904 output_pos = 0 としていることからこの時点の buf の中身(セグメントの集ま
3905 り=1 ブロック)がすべて send_block() で圧縮されファイルに出力されるだろ
3906 うことが想像できる。
3907
3908 この 1 ブロックに満たない状態でファイルの終りが来た場合、後処理 
3909 encode_end_st1() で send_block() が呼ばれるであろうことも想像できる。
3910
3911 encode_end_st1( /* void */ )
3912 {
3913     if (!unpackable) {
3914         send_block();
3915         putbits(CHAR_BIT - 1, 0);   /* flush remaining bits */
3916     }
3917 }
3918
3919 思った通りである。putbits(7, 0) というは、bitbuf に残った bit を吐き出す
3920 ためであることは、bit 入出力ルーチンの解読で確認済みだ。
3921
3922 そういうわけで、send_block() が圧縮のメインルーチンである。
3923 send_block() の入力とは先に示した図の状態の buf だ。huf.c:send_block() 
3924 を見てみよう。
3925
3926 static void
3927 send_block( /* void */ )
3928 {
3929     unsigned char   flags;
3930     unsigned short  i, k, root, pos, size;
3931
3932     /* (A) */
3933     root = make_tree(NC, c_freq, c_len, c_code);
3934     size = c_freq[root];
3935     putbits(16, size);
3936     /* (B) */
3937     if (root >= NC) {
3938         count_t_freq();
3939         root = make_tree(NT, t_freq, pt_len, pt_code);
3940         if (root >= NT) {
3941             write_pt_len(NT, TBIT, 3);
3942         } else {
3943             putbits(TBIT, 0);
3944             putbits(TBIT, root);
3945         }
3946         write_c_len();
3947     } else {
3948         putbits(TBIT, 0);
3949         putbits(TBIT, 0);
3950         putbits(CBIT, 0);
3951         putbits(CBIT, root);
3952     }
3953     /* (C) */
3954     root = make_tree(np, p_freq, pt_len, pt_code);
3955     if (root >= np) {
3956         write_pt_len(np, pbit, -1);
3957     }
3958     else {
3959         putbits(pbit, 0);
3960         putbits(pbit, root);
3961     }
3962     /* (D) */
3963     pos = 0;
3964     for (i = 0; i < size; i++) {
3965         if (i % CHAR_BIT == 0)
3966             flags = buf[pos++];
3967         else
3968             flags <<= 1;
3969         if (flags & (1 << (CHAR_BIT - 1))) {
3970             encode_c(buf[pos++] + (1 << CHAR_BIT));
3971             k = buf[pos++] << CHAR_BIT;
3972             k += buf[pos++];
3973             encode_p(k);
3974         } else
3975             encode_c(buf[pos++]);
3976         if (unpackable)
3977             return;
3978     }
3979     /* (E) */
3980     for (i = 0; i < NC; i++)
3981         c_freq[i] = 0;
3982     for (i = 0; i < np; i++)
3983         p_freq[i] = 0;
3984 }
3985
3986 なかなか大きな関数であるが、それほど難しいことはない。まず、(A)
3987
3988     /* (A) */
3989     root = make_tree(NC, c_freq, c_len, c_code);
3990     size = c_freq[root];
3991     putbits(16, size);
3992
3993 make_tree() で Huffman 表 c_len[], c_code[] を構築する。戻り値の root 
3994 は、Huffman 木の根を示し、c_freq[root] は、文字の出現回数の総和である
3995 から、size は、平文の総バイト数だ(size に <off> の分のサイズは含まれな
3996 い。c_freq[] が、<off> の出現頻度を数えなかったから)。ファイルには、こ
3997 の size がまず書き出されている(そういえば、この bit 入出力ルーチンを使
3998 用するとバイトオーダーに関して考慮する必要がなくなる)。
3999
4000 ----------------------------------------------------------------------------
4001
4002       16bit
4003    |---------|
4004    +----+----+
4005    |  size   |
4006    +----+----+
4007
4008 ----------------------------------------------------------------------------
4009
4010 続いて、(B)
4011
4012     if (root >= NC) {
4013         count_t_freq();
4014         root = make_tree(NT, t_freq, pt_len, pt_code);
4015         if (root >= NT) {
4016             write_pt_len(NT, TBIT, 3);
4017         } else {
4018             putbits(TBIT, 0);
4019             putbits(TBIT, root);
4020         }
4021         write_c_len();
4022     } else {
4023         putbits(TBIT, 0);
4024         putbits(TBIT, 0);
4025         putbits(CBIT, 0);
4026         putbits(CBIT, root);
4027     }
4028
4029 root が NC よりも大きい場合を判断しているが、ハフマン木の根は必ず NC よ
4030 りも大きい(make_tree() の avail の初期値を確認しよう)。では、この
4031 条件を満たさない場合と言うのは何かと言うと、同じく make_tree() を確認すると、
4032
4033     if (heapsize < 2) {
4034         codeparm[heap[1]] = 0;
4035         return heap[1];
4036     }
4037
4038 という例外条件があった。これは、圧縮する文字がない、あるいは 1 種類しか
4039 ない場合の処理だ。圧縮する文字がない場合に send_block() が呼ばれること
4040 はないだろうから、(B) の処理の else は 1 ブロック中に圧縮する文字が 1 
4041 種類しかない場合の処理である(この 1 種類の文字とは、make_tree() の戻り
4042 値 root だ)。このとき以下のような出力になる。(TBIT{5}, CBIT{9} である)
4043
4044 ----------------------------------------------------------------------------
4045       TBIT    CBIT
4046    TBIT   CBIT
4047    |--|--|----|----|               TBIT: 5
4048    +--+--+----+----+               CBIT: 9
4049    | 0| 0|   0|root|
4050    +--+--+----+----+
4051
4052 ----------------------------------------------------------------------------
4053
4054 これが、1 ブロックに 1 種類しか文字がない場合の出力だ(off の情報はまだ
4055 含まない)。(B)の if が真のときがどうなるかは複雑そうなので後で見ること
4056 にしよう。
4057
4058 続いて (C)
4059
4060     root = make_tree(np, p_freq, pt_len, pt_code);
4061     if (root >= np) {
4062         write_pt_len(np, pbit, -1);
4063     }
4064     else {
4065         putbits(pbit, 0);
4066         putbits(pbit, root);
4067     }
4068
4069 p_freq[] を見ていることから今度は <off> の情報の Huffman 木を構築して
4070 いることがわかる。先程と同様に、<off> の値がすべて同じ場合は、else の
4071 条件になり、以下の出力が行われる。(pbit の値は、-lh7- の場合で、5 だ)
4072
4073 ----------------------------------------------------------------------------
4074
4075        pbit     pbit                method  pbit
4076    |---------|---------|            ----------
4077    +----+----+---------+            -lh4-   4
4078    |     0   |  root   |            -lh5-   4
4079    +----+----+---------+            -lh6-   5
4080                                     -lh7-   5
4081
4082 ----------------------------------------------------------------------------
4083
4084 ここまでに出力した情報が何を示すかわかるだろうか?Huffman 法の符号化処
4085 理は文字を bit 列に変換する。これを復号する場合は bit 列に対応する文字
4086 を知る必要がある。すなわち Huffman 木である(実際には Huffman 表)。図示
4087 したのは、Huffman 木を構築する必要がない(構築できない)場合の情報になる
4088 が、現在解読を飛ばしている処理は Huffman 表をファイルに出力している箇
4089 所であることは容易に想像がつく。ということは残りの (D) が本当の圧縮文
4090 を出力する箇所だ。
4091
4092     /* (D) */
4093     pos = 0;
4094     for (i = 0; i < size; i++) {
4095         if (i % CHAR_BIT == 0)
4096             flags = buf[pos++];
4097         else
4098             flags <<= 1;
4099         if (flags & (1 << (CHAR_BIT - 1))) {
4100             encode_c(buf[pos++] + (1 << CHAR_BIT));
4101             k = buf[pos++] << CHAR_BIT;
4102             k += buf[pos++];
4103             encode_p(k);
4104         } else
4105             encode_c(buf[pos++]);
4106         if (unpackable)
4107             return;
4108     }
4109
4110 size 数分ループしている。size は、<off> を除いた buf の文字数を示して
4111 いると前に書いたが、どうやら <len, off> を 1 文字と数えたときの buf の
4112 文字数を示していると考えた方が良さそうだ。
4113
4114 最初の if で、
4115
4116         if (i % CHAR_BIT == 0)
4117             flags = buf[pos++];
4118         else
4119             flags <<= 1;
4120
4121 これが真になる条件は buf[pos] が buf[cpos] である場合だ(output_mask が 
4122 128, 64, ..., 1 と 8 つの値を巡回していたことを思い出そう)。
4123 flags は、<len, off> の buf 上の位置を示す bit マスクになる。
4124
4125         if (flags & (1 << (CHAR_BIT - 1))) {
4126             encode_c(buf[pos++] + (1 << CHAR_BIT));
4127             k = buf[pos++] << CHAR_BIT;
4128             k += buf[pos++];
4129             encode_p(k);
4130         } else
4131             encode_c(buf[pos++]);
4132
4133 flags の 7 ビット目(128)が立っているとき buf[pos] は、<len, off> を指し、
4134
4135   encode_c(len + 256)
4136   encode_p(off)
4137
4138 で、圧縮を行うようだ。len に 256 を足しているのは、buf[] に len を格納
4139 するとき(output_st1() の (B) の処理)に
4140
4141     buf[output_pos++] = (unsigned char) c;
4142
4143 のように最上位 bit を捨てていたからだ。len は常に 256 以上なので、256 
4144 を足すことで元の len の値を圧縮ルーチンに渡している。
4145
4146 通常の文字は
4147
4148   encode_c(buf[pos])
4149
4150 で圧縮されている。encode_c() の処理内容は簡単なので見てみよう。
4151
4152 static void
4153 encode_c(c)
4154     short           c;
4155 {
4156     putcode(c_len[c], c_code[c]);
4157 }
4158
4159 c_len[], c_code[] が文字 c に対応する Huffman 符号の bit 長と符号を示
4160 しているので、それをそのまま出力している。簡単だ。
4161
4162 encode_p() はもう少し複雑だ。
4163
4164 static void
4165 encode_p(p)
4166     unsigned short  p;
4167 {
4168     unsigned short  c, q;
4169
4170     c = 0;
4171     q = p;
4172     while (q) {
4173         q >>= 1;
4174         c++;
4175     }
4176     putcode(pt_len[c], pt_code[c]);
4177     if (c > 1)
4178         putbits(c - 1, p);
4179 }
4180
4181 最初の while 文で、<off> の bit 長を求め、その bit 長の情報を
4182 Huffman 符号化している。その後、putbits() で、必要 bit 数だけ
4183 出力する。つまり、<off> は以下のように符号化される。
4184
4185 ----------------------------------------------------------------------------
4186 off = 64 の圧縮
4187
4188      |---- 16 bit -------|
4189      +----+----+----+----+
4190 off  |0000 0000 0100 0000|
4191      +----+----+----+----+
4192                  |-7 bit-|
4193
4194 この圧縮文は以下(長さが 7 bit であるという情報(Huffman符号化)と値のペア)
4195
4196                        |-6 bit-|
4197      +-----------------+-------+
4198      | 7 のHuffman符号 |00 0000|
4199      +-----------------+-------+
4200
4201 ----------------------------------------------------------------------------
4202
4203 ここで、値を 6 bit しか出力しない(putbits() で c-1 を渡している)のは、
4204 7 bit 目が 1 であることが自明だからである。最初にビット長を出力している
4205 ので、値の情報は1 bit 削減できるわけだ。したがって、off=1 のときは bit
4206 長が 1 という情報しか書き出さない。
4207
4208 最後の (E) は頻度表をクリアしているだけだ。
4209
4210     /* (E) */
4211     for (i = 0; i < NC; i++)
4212         c_freq[i] = 0;
4213     for (i = 0; i < np; i++)
4214         p_freq[i] = 0;
4215
4216 ここで、頻度表をクリアしているということは文字や位置の出現回数は 1 ブロック
4217 単位でしか計上しないことを表す。
4218
4219 # 一方、c_freq や p_freq は unsigned short であるから(16 bit だとし
4220 # て)65535 までしか数えられない。さらに、{c,p}_freq には Huffman 木の構
4221 # 築の過程で出現回数の総和がセットされる場合があることから 1 ブロックに
4222 # は 65535 文字 + 65535 個の位置までしか格納できない。
4223 # いや、位置は必ず長さとセットであることから。位置の出現回数は文字の出
4224 # 現回数(これは長さの出現回数を含む)に含まれるため 65535 スロットまでし
4225 # か buf を持てないことになる。これは blocksize (16 bit)とも一致する。
4226 #
4227 # buf の領域確保は以下のようになっていた。
4228 #
4229 #    unsigned char  *
4230 #    alloc_buf( /* void */ )
4231 #    {
4232 #       bufsiz = 16 * 1024 *2;  /* 65408U; */ /* t.okamoto */
4233 #       while ((buf = (unsigned char *) malloc(bufsiz)) == NULL) {
4234 #           bufsiz = (bufsiz / 10) * 9;
4235 #           if (bufsiz < 4 * 1024)
4236 #               break;
4237 #       }
4238 #       return buf;
4239 #    }
4240 #
4241 # これから、bufsiz は、16 * 1024 *2 = 2^4 * 2^10 * 2 = 2^15 である。
4242 # 1 セグメントの最小サイズがバイト数で(1*CHAR_BIT+1)であるから
4243 # この領域に格納できる最大セグメント数は、
4244 #   2^15 / (1*CHAR_BIT+1)
4245 # であり、1 セグメントは 8 スロットあるから、最大スロット数は
4246 #     8 * 2^15 / (1*CHAR_BIT+1)
4247 #   = 2^18 / 9
4248 #   = 29127.1111111111
4249 # となる。これは、頻度表の上限(スロット数の上限)
4250 #   65535=2^16-1
4251 # よりも小さいので問題はないことになる。
4252 #
4253 # なお、1 ブロックのサイズはこのバッファサイズにより決まるわけだが 1 ブ
4254 # ロックのサイズが大きければ大きいほどよいというわけではない。むしろ、
4255 # 文字の出現確率の変動に追随するためには小さい方がよいのだがそうすると
4256 # Huffman 木の格納回数が増えるので簡単には最適値は決まらない。
4257
4258 以上で、圧縮処理全体の概要がわかった。後は無視していた Huffman 表を出
4259 力する箇所だけだ。
4260
4261     /* (B) */
4262     if (root >= NC) {
4263         count_t_freq();
4264         root = make_tree(NT, t_freq, pt_len, pt_code);
4265         if (root >= NT) {
4266             write_pt_len(NT, TBIT, 3);
4267         } else {
4268             putbits(TBIT, 0);
4269             putbits(TBIT, root);
4270         }
4271         write_c_len();
4272
4273 ここでは、c_len[], c_code[] という Huffman 表を出力するだけのはずだが、
4274 さらに Huffman 表 pt_len[], pt_code[] の構築を行っている。これは、
4275 <off> の bit 長の Huffman 符号表でもあった変数だが、単に変数を使い回し
4276 ているだけだ。ここでの pt_len[], pt_code[] が何の符号表かは、
4277 count_t_freq() を見る必要がある。
4278
4279 static void
4280 count_t_freq(/*void*/)
4281 {
4282     short           i, k, n, count;
4283
4284     for (i = 0; i < NT; i++)
4285         t_freq[i] = 0;
4286     n = NC;
4287     while (n > 0 && c_len[n - 1] == 0)
4288         n--;
4289     i = 0;
4290     while (i < n) {
4291         k = c_len[i++];
4292         if (k == 0) {
4293             count = 1;
4294             while (i < n && c_len[i] == 0) {
4295                 i++;
4296                 count++;
4297             }
4298             if (count <= 2)
4299                 t_freq[0] += count;
4300             else if (count <= 18)
4301                 t_freq[1]++;
4302             else if (count == 19) {
4303                 t_freq[0]++;
4304                 t_freq[1]++;
4305             }
4306             else
4307                 t_freq[2]++;
4308         } else
4309             t_freq[k + 2]++;
4310     }
4311 }
4312
4313 最初に頻度表 t_freq[] を初期化する。続いて、
4314
4315     n = NC;
4316     while (n > 0 && c_len[n - 1] == 0)
4317         n--;
4318
4319 で、c_len[n] != 0 である最大の n を求めている。
4320
4321     i = 0;
4322     while (i < n) {
4323         k = c_len[i++];
4324         if (k == 0) {
4325             count = 1;
4326             while (i < n && c_len[i] == 0) {
4327                 i++;
4328                 count++;
4329             }
4330             if (count <= 2)
4331                 t_freq[0] += count;
4332             else if (count <= 18)
4333                 t_freq[1]++;
4334             else if (count == 19) {
4335                 t_freq[0]++;
4336                 t_freq[1]++;
4337             }
4338             else
4339                 t_freq[2]++;
4340         } else
4341             t_freq[k + 2]++;
4342     }
4343
4344 c_len[i] は、文字 i の Huffman 符号での bit 長であった。この
4345 c_len[i] の値を以下の場合分けで t_freq[] に頻度計算している。
4346 count は、c_len[i] が連続で何回 0 であるかの数だ。
4347
4348   c_len[i]    count     t_freq[]
4349   -------------------------------------------
4350      0        1 .. 2    t_freq[0] += count
4351      0        3 ..18    t_freq[1]++
4352      0        19        t_freq[0]++, t_freq[1]++
4353      0        20以上    t_freq[2]++
4354    0以外                t_freq[c_len[i]+2]++;
4355
4356 これがどういう理屈であるかはよくわからないが、とにかく頻度計算を行う場
4357 合に t_freq[0], t_freq[1], t_freq[2] を特別扱いしている。そして、頻度
4358 計算の対象が c_len[] であることから (B) の処理は、c_len[] に関して 
4359 Huffman 符号化を行う処理のようだ。
4360
4361 そうして、make_tree() で、t_freq[] に関して Huffman 符号表を作成し、
4362 write_pt_len() で、この符号表(文字の Huffman 符号のビット長 c_len の 
4363 Huffman 符号のビット長) pt_len[] を出力する。
4364
4365 static void
4366 write_pt_len(n, nbit, i_special)
4367     short           n;
4368     short           nbit;
4369     short           i_special;
4370 {
4371     short           i, k;
4372
4373     while (n > 0 && pt_len[n - 1] == 0)
4374         n--;
4375     putbits(nbit, n);
4376     i = 0;
4377     while (i < n) {
4378         k = pt_len[i++];
4379         if (k <= 6)
4380             putbits(3, k);
4381         else
4382             putbits(k - 3, USHRT_MAX << 1);
4383         if (i == i_special) {
4384             while (i < 6 && pt_len[i] == 0)
4385                 i++;
4386             putbits(2, i - 3);
4387         }
4388     }
4389 }
4390
4391 最初に pt_len[] の要素数を nbit 出力し、続けて符号 bit 長 pt_len[] の
4392 要素を出力している。nbit は、n を格納するのに必要な bit 数を表している
4393 ようだ。ここでは、n (NT{19}) を出力するのに TBIT{5} bit 必要であるとい
4394 うことだ。
4395
4396 pt_len[] を出力するときは、その値が 6 より大きいかどうかで形式を変えて
4397 出力している。6 以下であればそのまま 3 bit で出力し、7 bit 以上であれ
4398 ば、bit 数で表現するらしい。例えば pt_len[i] == 7 なら、1110 となる。
4399 最初の 3 bit は必ず 1 になり、最初の形式と区別がつくようになっている。
4400
4401 さらに、i_special 番目の pt_len[i] を出力した後は、i_special ... 6 の範
4402 囲で pt_len[i] == 0 が続くことを 2 bit で、表現している。i_special は
4403 write_pt_len() の 3 番目の引数で、今回の場合は 3 だ。例えば
4404 pt_len[3..5] がすべて 0 なら pt_len[3..5] を出力せずに、i - 3 (= 3) を
4405 2 bit 出力する。つまり、11 が出力される。このようなことをしている意味は
4406 これまたよくわからない。ちょっと複雑なので図示してみた。
4407
4408 ----------------------------------------------------------------------------
4409 < pt_len[] の出力フォーマット >
4410
4411              0       TBIT{5}
4412              +-------+-----------+-----------+--   --+-----------+
4413              |   n   | pt_len[0] | pt_len[1] | ...    pt_len[n-1]|
4414              +-------+-----------+-----------+--   --+-----------+
4415
4416 pt_len[i] <= 6 の場合
4417
4418               0     3bit
4419               +-----+
4420     pt_len[i] | | | |
4421               +-----+
4422
4423 pt_len[i] >= 7 の場合
4424
4425               0             pt_len[i] - 3
4426               +----------------+
4427     pt_len[i] |1 1 1 1 ... 1 0 |
4428               +----------------+
4429
4430 pt_len[i_special-1] の直後は 2 bit の情報が付加される。この値を x とする
4431 と、pt_len[i_special .. x + 2] の範囲で 0 が続くことを意味する。x が 0
4432 なら pt_len[i_special] は 0 ではない。
4433
4434 ----------------------------------------------------------------------------
4435
4436 最後に、write_c_len() で、Huffman 符号のビット長 c_len[] (の Huffman 符
4437 号表 pt_code[]) を出力する。
4438
4439 static void
4440 write_c_len(/*void*/)
4441 {
4442     short           i, k, n, count;
4443
4444     n = NC;
4445     while (n > 0 && c_len[n - 1] == 0)
4446         n--;
4447     putbits(CBIT, n);
4448     i = 0;
4449     while (i < n) {
4450         k = c_len[i++];
4451         if (k == 0) {
4452             count = 1;
4453             while (i < n && c_len[i] == 0) {
4454                 i++;
4455                 count++;
4456             }
4457             if (count <= 2) {
4458                 for (k = 0; k < count; k++)
4459                     putcode(pt_len[0], pt_code[0]);
4460             }
4461             else if (count <= 18) {
4462                 putcode(pt_len[1], pt_code[1]);
4463                 putbits(4, count - 3);
4464             }
4465             else if (count == 19) {
4466                 putcode(pt_len[0], pt_code[0]);
4467                 putcode(pt_len[1], pt_code[1]);
4468                 putbits(4, 15);
4469             }
4470             else {
4471                 putcode(pt_len[2], pt_code[2]);
4472                 putbits(CBIT, count - 20);
4473             }
4474         }
4475         else
4476             putcode(pt_len[k + 2], pt_code[k + 2]);
4477     }
4478 }
4479
4480 前に、頻度を数えたときと同様の条件で出力形式が変わっている。処理内容は
4481 簡単なので、以下の図を示すだけにする(理屈はよくわからない)。
4482
4483 ----------------------------------------------------------------------------
4484 < c_len[] の出力フォーマット >
4485
4486              0       CBIT{9}
4487              +-------+-----------+-----------+--   --+-----------+
4488              |   n   |  c_len[0] |  c_len[1] | ...     c_len[n-1]|
4489              +-------+-----------+-----------+--   --+-----------+
4490
4491 c_len[i] == 0 の場合
4492
4493  0 が続く数を count とすると、
4494
4495  count == 1..2 の場合
4496
4497                 pt_len[0]
4498               <---------->
4499              +------------+
4500              | pt_code[0] |
4501              +------------+
4502
4503  count == 3..18 の場合
4504
4505                pt_len[1]    4 bit
4506               <----------> <------>
4507              +------------+-------+
4508              | pt_code[1] |count-3|
4509              +------------+-------+
4510
4511  count == 19 の場合
4512
4513                 pt_len[0]   pt_len[1]    4 bit
4514               <----------> <----------> <------>
4515              +------------+------------+-------+
4516              | pt_code[0] | pt_code[1] |count-3|
4517              +------------+------------+-------+
4518
4519   count >= 20 の場合
4520
4521                 pt_len[2]    CBIT{9}
4522               <----------> <------>
4523              +------------+--------+
4524              | pt_code[2] |count-20|
4525              +------------+--------+
4526
4527 c_len[i] != 0 の場合
4528
4529               pt_len[c_len[i]+2]
4530              +-------------------+
4531              |pt_code[c_len[i]+2]|
4532              +-------------------+
4533
4534 ----------------------------------------------------------------------------
4535
4536 こうして、文字の Huffman 符号表は、pt_len[] と pt_code[](pt_code[] は
4537 つまり c_len[] の Huffman 符号)を出力することで表現される。c_code[] が
4538 出力されてないと思うかもしれないが、おそらく、decode 処理が c_len[] の値
4539 から計算して求めているのではないかと思われる。これは decode 処理の解読
4540 で明らかになるだろう。
4541
4542 # 少し変なことを書いている。pt_code[] の出力は、c_len[] の Huffman 符号
4543 # を出力しているのであり、Huffman 木の情報として出力しているわけではな
4544 # い。つまり、c_code[] が出力されていないのと同様に pt_code[] も出力は
4545 # していない。
4546
4547 この後、send_block() は、(C) で、<off> の Huffman 符号表を出力するのだ
4548 が、
4549
4550         write_pt_len(np, pbit, -1);
4551
4552 これは、先の pt_len[] の出力フォーマットと同じなので詳細ははしょろう。
4553 ただし、今度の pt_len[] の出力では write_pt_len() の第三引数 i_special 
4554 が -1 で指定されていて、i_special 番目の pt_len[i_special..5] に関して
4555 特別扱いがなくなっているところが異なる。
4556
4557 np や pbit の意味もこの時点でわかるので一応説明しておく。np, pbit そし
4558 て、LHA の圧縮 method との関係は以下の表の通りなのだが、np は、<off> の
4559 最大 bit 長 + 1 だ。<off> の最大 bit 長はすなわち dicbit なので、np は、
4560 dicbit + 1 である。-lh4- のときに dicbit + 2 なのが不思議だが、これは歴
4561 史的な理由だろうと思われる。pbit は、値 np を出力するのに必要な bit 数
4562 なので表の通りになる。
4563
4564     method  dicbit  np  pbit
4565    --------------------------
4566     -lh4-   12      14  4
4567     -lh5-   13      14  4
4568     -lh6-   15      16  5
4569     -lh7-   16      17  5
4570
4571 まとめると LHA における圧縮ファイルの構造は以下の連続であると言えるよ
4572 うだ。
4573
4574 ----------------------------------------------------------------------------
4575 < LHA ファイルの構造(1 ブロック分) >
4576
4577     +-----------+
4578     | blocksize |
4579     +-----------+
4580        16bit
4581
4582     +-----+--------------------+
4583     | len |      pt_len        | c_lenのハフマン符号表
4584     +-----+--------------------+
4585       5bit        ?? bit
4586       TBIT
4587
4588     +-------+------------------+
4589     |  len  |     c_len        | 文字と長さのハフマン符号表
4590     +-------+------------------+
4591       9bit        ?? bit
4592       CBIT
4593
4594     +---------+--------------------+
4595     |   len   |   pt_len           | 位置のハフマン符号表
4596     +---------+--------------------+
4597      pbit         ?? bit
4598                                (pbit=4bit(lh4,5) or 5bit(lh6,7))
4599
4600     +---------------------+
4601     |  圧縮文             |
4602     +---------------------+
4603
4604 ----------------------------------------------------------------------------
4605
4606 ここまでの解読では細部をかなりはしょったが、decode 処理を見ればわかる
4607 こともあるであろうことを期待している。以降、decode 処理の内容を処理の
4608 流れを追うことで確認しよう。
4609
4610 では、いよいよ decode 処理の解読に入る。これが終れば LHA の処理の全貌
4611 を一応は網羅したことになるので、気合いを入れて進めよう。
4612
4613 decode 処理は以下の関数から成っている。これらは、slide.c の decode 関
4614 数から呼ばれている。
4615
4616 huf.c:decode_c_st1()          /* 文字、長さの decode 処理 */
4617 huf.c:decode_p_st1()          /* 位置の decode 処理 */
4618 huf.c:decode_start_st1()      /* decode 処理の初期化 */
4619
4620                         (実際には、struct decode_option の decode_c,
4621                         decode_p, decode_start を介して呼ばれる)
4622
4623 decode_start_st1() は、以下の通りだ。これは encode_start_st1() のとき
4624 と特別変わる所はない。特に説明の必要はないだろう。
4625
4626 void
4627 decode_start_st1( /* void */ )
4628 {
4629     if (dicbit <= 13)  {
4630         np = 14;
4631         pbit = 4;
4632     } else {
4633         if (dicbit == 16) {
4634             np = 17; /* for -lh7- */
4635         } else {
4636             np = 16;
4637         }
4638         pbit = 5;
4639     }
4640
4641     init_getbits();
4642     blocksize = 0;
4643 }
4644
4645 では、decode_c_st1() を見よう。
4646
4647 unsigned short
4648 decode_c_st1( /*void*/ )
4649 {
4650     unsigned short  j, mask;
4651
4652     if (blocksize == 0) {
4653         blocksize = getbits(16);
4654         read_pt_len(NT, TBIT, 3);
4655         read_c_len();
4656         read_pt_len(np, pbit, -1);
4657     }
4658     blocksize--;
4659     j = c_table[bitbuf >> 4];
4660     if (j < NC)
4661         fillbuf(c_len[j]);
4662     else {
4663         fillbuf(12);
4664         mask = 1 << (16 - 1);
4665         do {
4666             if (bitbuf & mask)
4667                 j = right[j];
4668             else
4669                 j = left[j];
4670             mask >>= 1;
4671         } while (j >= NC);
4672         fillbuf(c_len[j] - 12);
4673     }
4674     return j;
4675 }
4676
4677 blocksize == 0 の場合に
4678
4679     if (blocksize == 0) {
4680         blocksize = getbits(16);
4681         read_pt_len(NT, TBIT, 3);
4682         read_c_len();
4683         read_pt_len(np, pbit, -1);
4684     }
4685
4686 と、いろいろとやっているがこの部分はすぐ想像がつく。< LHA ファイルの構
4687 造 > のハフマン符号表を読み込んでいるのだろう。そうして、一度読み込んだ
4688 後は後続の処理で blocksize 分読み込むまで decode を行うだけだ。
4689
4690     blocksize--;
4691     j = c_table[bitbuf >> 4];
4692
4693 decode 処理はハフマン符号表を表引きするだけなので単純だ。bitbuf >> 4 
4694 は、bitbuf >> (16 - 12) と読み変えた方がわかりやすい。これは以前何度も
4695 出た形だが bitbuf の上位 12 bit を取り出している。そうしてその値(ハフ
4696 マン符号)を元に表引きした結果 j が復号した文字となる。なぜ 12 bit なのか
4697 はよくわからない後で考えよう。この後の部分で、
4698
4699     if (j < NC)
4700         fillbuf(c_len[j]);
4701     else {
4702         fillbuf(12);
4703         mask = 1 << (16 - 1);
4704         do {
4705             if (bitbuf & mask)
4706                 j = right[j];
4707             else
4708                 j = left[j];
4709             mask >>= 1;
4710         } while (j >= NC);
4711         fillbuf(c_len[j] - 12);
4712     }
4713     return j;
4714
4715 j < NC の場合は c_len[j] でハフマン符号のビット長分を fillbuf() してい
4716 る。つまり先程表引きした 12 bit のうち c_len[j] bit が本当のハフマン符
4717 号なのだが、表引きの際に実際のビット長を気にする必要がないのが特徴的だ。
4718
4719 else の部分は、j を求め直していることから、どうやら符号表からの表引き
4720 では復号できない場合を表しているらしい。この場合、表引きに使用した 12
4721 bit を捨て(fillbuf(12))、ハフマン木(left[], right[])を辿る事で、復号を
4722 行っている。この後、fillbuf(c_len[j] - 12) していることから、符号長は 
4723 12 bit 以上あるのだろう。
4724
4725 decode_c_st1() が decode する圧縮文の構造は図で表すと以下のようになる
4726
4727 ----------------------------------------------------------------------------
4728
4729 j < NC の場合 (c_len[j] < 12 の場合)
4730
4731          <-  c_len[j] ->
4732          <----- 12 ------->
4733         +--------------+----------
4734 圧縮文  | ハフマン符号 |
4735         +--------------+----------
4736
4737 j >= NC の場合 (c_len[j] > 12 の場合)
4738
4739          <------------ c_len[j] --------->
4740          <------ 12 ------>
4741         +------------------+--------------+--------
4742 圧縮文  |    root          | ハフマン符号 |
4743         +------------------+--------------+--------
4744
4745             root: ハフマン木の根
4746
4747 ----------------------------------------------------------------------------
4748
4749 はたして、圧縮処理のときにこのような構造を作った覚えはないのだがどうい
4750 うことだろう?課題を残しつつ今度は decode_p_st1() (位置の復号処理)を見
4751 る。
4752
4753 unsigned short
4754 decode_p_st1( /* void */ )
4755 {
4756     unsigned short  j, mask;
4757
4758     j = pt_table[bitbuf >> (16 - 8)];
4759     if (j < np)
4760         fillbuf(pt_len[j]);
4761     else {
4762         fillbuf(8);
4763         mask = 1 << (16 - 1);
4764         do {
4765             if (bitbuf & mask)
4766                 j = right[j];
4767             else
4768                 j = left[j];
4769             mask >>= 1;
4770         } while (j >= np);
4771         fillbuf(pt_len[j] - 8);
4772     }
4773     if (j != 0)
4774         j = (1 << (j - 1)) + getbits(j - 1);
4775     return j;
4776 }
4777
4778 先程と同じだ。今度は、bitbuf のうち 8 bit を使用して表引きを行い、
4779 j < np なら pt_len[j] を詰め、そうでなければハフマン木を辿っている。
4780 復号した j は位置を表す値の bit 長なので最後に
4781
4782         j = (1 << (j - 1)) + getbits(j - 1);
4783
4784 で、本当の位置の値を読んでいる(encode_p() がそういう処理だった事を思い
4785 出そう)。
4786
4787 decode_p_st1() が decode する圧縮文の構造は図で表すと以下のようになる
4788
4789 ----------------------------------------------------------------------------
4790
4791 j < np の場合 (pt_len[j] < 8 の場合)
4792
4793          <- pt_len[j] ->
4794          <------ 8 ------->
4795         +--------------+----------
4796 圧縮文  | ハフマン符号 |
4797         +--------------+----------
4798
4799 j >= np の場合 (pt_len[j] > 8 の場合)
4800
4801          <----------- pt_len[j] --------->
4802          <------ 8 ------->
4803         +------------------+--------------+----------+----------
4804 圧縮文  |      root        | ハフマン符号 | 位置の値 |
4805         +------------------+--------------+----------+----------
4806
4807             root: ハフマン木の根
4808
4809 ----------------------------------------------------------------------------
4810
4811 以上が、decode 処理の概要だ。ここまでの処理は別にどうという事もないだ
4812 ろう。decode 処理のキモは、圧縮文に埋め込んだハフマン符号表の読み込み
4813 にある。blocksize == 0 のときに、decode_c_st1() で呼ばれる 
4814 read_pt_len(), read_c_len() だ。これにより、decode 処理で使用するテーブル
4815
4816 c_table[]       ハフマン符号 -> 文字の変換テーブル
4817 c_len[]         ハフマン符号 -> ハフマン符号のビット長の対応
4818 pt_table[]      ハフマン符号 -> 位置のビット長の変換テーブル
4819 pt_len[]        ハフマン符号 -> ハフマン符号のビット長の対応
4820 left[]          ハフマン木(左のノード)
4821 right[]         ハフマン木(右のノード)
4822
4823 が構築される。この部分の方が decode 処理本体よりややこしそうだ。
4824 では、これらを見て行こう。
4825
4826     if (blocksize == 0) {
4827         blocksize = getbits(16);
4828         read_pt_len(NT, TBIT, 3);
4829         read_c_len();
4830         read_pt_len(np, pbit, -1);
4831     }
4832
4833 最初は、read_pt_len(NT, TBIT, 3) から
4834
4835 static void
4836 read_pt_len(nn, nbit, i_special)
4837     short           nn;
4838     short           nbit;
4839     short           i_special;
4840 {
4841     int           i, c, n;
4842
4843     n = getbits(nbit);
4844     if (n == 0) {
4845         c = getbits(nbit);
4846         for (i = 0; i < nn; i++)
4847             pt_len[i] = 0;
4848         for (i = 0; i < 256; i++)
4849             pt_table[i] = c;
4850     }
4851     else {
4852         i = 0;
4853         while (i < n) {
4854             c = bitbuf >> (16 - 3);
4855             if (c == 7) {
4856                 unsigned short  mask = 1 << (16 - 4);
4857                 while (mask & bitbuf) {
4858                     mask >>= 1;
4859                     c++;
4860                 }
4861             }
4862             fillbuf((c < 7) ? 3 : c - 3);
4863             pt_len[i++] = c;
4864             if (i == i_special) {
4865                 c = getbits(2);
4866                 while (--c >= 0)
4867                     pt_len[i++] = 0;
4868             }
4869         }
4870         while (i < nn)
4871             pt_len[i++] = 0;
4872         make_table(nn, pt_len, 8, pt_table);
4873     }
4874 }
4875
4876 実際、大した事はない。< pt_len[] の出力フォーマット > にしたがって、
4877 pt_len[] を読み直しているだけだ。read_c_len() も見よう。
4878
4879 static void
4880 read_c_len( /* void */ )
4881 {
4882     short           i, c, n;
4883
4884     n = getbits(CBIT);
4885     if (n == 0) {
4886         c = getbits(CBIT);
4887         for (i = 0; i < NC; i++)
4888             c_len[i] = 0;
4889         for (i = 0; i < 4096; i++)
4890             c_table[i] = c;
4891     } else {
4892         i = 0;
4893         while (i < n) {
4894             c = pt_table[bitbuf >> (16 - 8)];
4895             if (c >= NT) {
4896                 unsigned short  mask = 1 << (16 - 9);
4897                 do {
4898                     if (bitbuf & mask)
4899                         c = right[c];
4900                     else
4901                         c = left[c];
4902                     mask >>= 1;
4903                 } while (c >= NT);
4904             }
4905             fillbuf(pt_len[c]);
4906             if (c <= 2) {
4907                 if (c == 0)
4908                     c = 1;
4909                 else if (c == 1)
4910                     c = getbits(4) + 3;
4911                 else
4912                     c = getbits(CBIT) + 20;
4913                 while (--c >= 0)
4914                     c_len[i++] = 0;
4915             }
4916             else
4917                 c_len[i++] = c - 2;
4918         }
4919         while (i < NC)
4920             c_len[i++] = 0;
4921         make_table(NC, c_len, 12, c_table);
4922     }
4923 }
4924
4925 こちらも、< c_len[] の出力フォーマット > にしたがって、c_len[] を読み
4926 直しているだけだ。
4927 # このあたりになると解析がかなり雑になっている(当時疲れていたのだろう)。
4928 # 最終的には後述の「LHA ファイルの構造(まとめ)」にて、再度検討しなおし
4929 # ているのでそちらを見て欲しい。不明だった部分もここですべて明らかにし
4930 # ている。
4931 結局キモとなるのは、make_table() にあるらしい。この関数により、読み込ん
4932 だ pt_len[], c_len[] から pt_table[], c_table[] (そして、ハフマン木
4933 left[], right[])を構築しているのだろう。
4934
4935 結局、decode 処理 read_c_len(), read_pt_len() を読んでもなぜこのような
4936 符号化を行っているのかよくわからなかった。何か統計的な根拠でもあるのだ
4937 ろうか?それとも LHA にとって歴史的な事情でもあるのか?これに関しては
4938 別途検証の必要があるだろう。
4939
4940 では、最後の関数 make_table() を解読しよう。これは、maketbl.c で定義さ
4941 れている。
4942
4943 void
4944 make_table(nchar, bitlen, tablebits, table)
4945     short           nchar;
4946     unsigned char   bitlen[];
4947     short           tablebits;
4948     unsigned short  table[];
4949 {
4950     unsigned short  count[17];  /* count of bitlen */
4951     unsigned short  weight[17]; /* 0x10000ul >> bitlen */
4952     unsigned short  start[17];  /* first code of bitlen */
4953     unsigned short  total;
4954     unsigned int    i, l;
4955     int             j, k, m, n, avail;
4956     unsigned short *p;
4957
4958     /* (A) */
4959     avail = nchar;
4960
4961     /* initialize */
4962     for (i = 1; i <= 16; i++) {
4963         count[i] = 0;
4964         weight[i] = 1 << (16 - i);
4965     }
4966
4967     /* (B) */
4968     /* count */
4969     for (i = 0; i < nchar; i++)
4970         count[bitlen[i]]++;
4971
4972     /* (C) */
4973     /* calculate first code */
4974     total = 0;
4975     for (i = 1; i <= 16; i++) {
4976         start[i] = total;
4977         total += weight[i] * count[i];
4978     }
4979     if ((total & 0xffff) != 0)
4980         error("make_table()", "Bad table (5)\n");
4981
4982     /* (D) */
4983     /* shift data for make table. */
4984     m = 16 - tablebits;
4985     for (i = 1; i <= tablebits; i++) {
4986         start[i] >>= m;
4987         weight[i] >>= m;
4988     }
4989
4990     /* (E) */
4991     /* initialize */
4992     j = start[tablebits + 1] >> m;
4993     k = 1 << tablebits;
4994     if (j != 0)
4995         for (i = j; i < k; i++)
4996             table[i] = 0;
4997
4998     /* (F) */
4999     /* create table and tree */
5000     for (j = 0; j < nchar; j++) {
5001         k = bitlen[j];
5002         if (k == 0)
5003             continue;
5004         l = start[k] + weight[k];
5005         if (k <= tablebits) {
5006             /* code in table */
5007             for (i = start[k]; i < l; i++)
5008                 table[i] = j;
5009         }
5010         else {
5011             /* code not in table */
5012             p = &table[(i = start[k]) >> m];
5013             i <<= tablebits;
5014             n = k - tablebits;
5015             /* make tree (n length) */
5016             while (--n >= 0) {
5017                 if (*p == 0) {
5018                     right[avail] = left[avail] = 0;
5019                     *p = avail++;
5020                 }
5021                 if (i & 0x8000)
5022                     p = &right[*p];
5023                 else
5024                     p = &left[*p];
5025                 i <<= 1;
5026             }
5027             *p = j;
5028         }
5029         start[k] = l;
5030     }
5031 }
5032
5033 順に見て行こう。
5034
5035     /* (A) */
5036     avail = nchar;
5037
5038     /* initialize */
5039     for (i = 1; i <= 16; i++) {
5040         count[i] = 0;
5041         weight[i] = 1 << (16 - i);
5042     }
5043
5044 avail はおそらく maketree.c:make_tree() でそうであったように、木の節の
5045 初期値だろうと予想しておく。count[], weight[] も、maketree.c での 
5046 len_cnt[] weight[] と同義だろう(すなわち、count[i] は、木の深さ i 番目
5047 の葉の数、weight[i] は重み)
5048
5049     /* (B) */
5050     /* count */
5051     for (i = 0; i < nchar; i++)
5052         count[bitlen[i]]++;
5053
5054 count[] を求めている。bitlen[i] は、文字 i のハフマン符号での bit 長で
5055 あった。やはり count[] は予想通りだ。
5056
5057     /* (C) */
5058     /* calculate first code */
5059     total = 0;
5060     for (i = 1; i <= 16; i++) {
5061         start[i] = total;
5062         total += weight[i] * count[i];
5063     }
5064     if ((total & 0xffff) != 0)
5065         error("make_table()", "Bad table (5)\n");
5066
5067 これは、maketree.c:make_code() の前半部分とまったく同じだ。これにより、
5068 深さ i に対して、以下の対応表ができる(これは前にも書いた。Li は、
5069 count[i] の値を表している)。
5070
5071      i     count[i]   weight[i]   start[i]
5072  --------------------------------------------
5073      1         L1        2^15       0
5074      2         L2        2^14      2^15 * L1
5075      3         L3        2^13      2^15 * L1 + 2^14 * L2
5076      4         L4        2^12      2^15 * L1 + 2^14 * L2 + 2^13 * L3
5077
5078 これが何を表すかと言うと深さ i の符号(つまり bit 長 i の符号)は、
5079 start[i] から start[i+1]-1 の範囲の値を持つと言う事を意味する。再度、
5080 例で示そう。
5081
5082        /\               a: 0
5083       a /\              b: 10
5084         b c             c: 11
5085
5086      i     count[i]   weight[i]   start[i]
5087  --------------------------------------------
5088      1         1        2^15       0
5089      2         2        2^14      2^15
5090      3         0        2^13      2^15 + 2^14 * 2
5091
5092 これより、深さ 1 の葉 a は、start[1] .. start[2]-1 つまり、
5093 00000000 00000000 .. 01111111 11111111 の範囲の符号となる。
5094 深さ 2 の葉 b, c は、start[2] .. start[3]-1 つまり、
5095 10000000 00000000 ... 11111111 11111111 となる。
5096
5097     /* (D) */
5098     /* shift data for make table. */
5099     m = 16 - tablebits;
5100     for (i = 1; i <= tablebits; i++) {
5101         start[i] >>= m;
5102         weight[i] >>= m;
5103     }
5104
5105 理由はわからないが、作成するテーブルを引数で渡された bit 数のテーブル
5106 になるよう写像している。つまり、値の範囲の初期値 start[] と weight[]
5107 を 16 - tablebits でシフトすることで、
5108
5109          01111111 11111111
5110
5111 というテーブルの値は(tablebits が 12 であるとすると)、
5112
5113          00000111 11111111
5114          <--> (16 - tablebits{12}) = 4 bit シフト
5115
5116 の値にする。encode するときは、16 bit のテーブルをそのまま使用していた
5117 にも関わらず decode のときにはテーブルの bit 数を減らしているのだ。まっ
5118 たく理由がわからない。
5119
5120 当然、encode で使用していたときのテーブルより情報量が減っているので、
5121 すべてをテーブル参照で decode することはできない。そこで、足りない部分
5122 は後の処理で木を作ることで補っているようだ。
5123
5124 bit 数を減らす理由を考えてみる。まず表引きの考え方は、
5125
5126        /\               a: 0
5127       a /\              b: 10
5128         b c             c: 11
5129
5130 という Huffman 木について、
5131
5132      00: c_table[00] = a
5133      01: c_table[01] = a
5134      10: c_table[10] = b
5135      11: c_table[11] = c
5136
5137 というテーブル、つまり
5138
5139          c_table[Huffman符号]=復号語
5140
5141 を作成することで Huffman 符号化したビット列から復号語を得られるようにす
5142 る。そして、Huffman 符号から文字を得る必要があるので、c_table[] のイン
5143 デックスには、Huffman 符号が取りうる最大値を指定できなければならない。
5144 16 bit のHuffman 符号の最大値は 65535 であるから表引きに必要な変数は、
5145
5146   unsigned short c_table[65535+1]; (unsigned short >= NC-1)
5147
5148 となる。おそらく表の bit 数を減らしているのはこの大きなテーブルを作りた
5149 くないからではないかと思われる。c_table[] の要素数を最低限必要な数に抑
5150 えようとすると以下のようになるが、
5151
5152       0: c_table[ 0=0] = a
5153      10: c_table[10=2] = b
5154      11: c_table[11=3] = c
5155
5156 この例の場合、c_table[1] が空であるようなテーブルを作成しなければならな
5157 い。ハッシュ表を作れば可能だがそうはせず、c_table[] の要素数を減らし、
5158 表引きで表せない部分は木で補っているということだ。
5159
5160 実際の c_table[] の定義は、
5161
5162   unsigned short c_table[4095+1];
5163
5164 となっており、12 ビット(2^12=4096)の Huffman 符号について表引きが可能と
5165 なっている。そして、復号語は、0...NC の範囲であるから、
5166     c_table[ビット列] < NC
5167 の場合は、そのまま表引きで
5168     c_table[ビット列] >= NC
5169 の場合は、木のルートノードを示し、その値から木を辿って復号できるように
5170 なっている。図示すると
5171
5172 ----------------------------------------------------------------------------
5173        .
5174       / \
5175      a   .           ... 階層:1   `.
5176         / \                        |
5177         b            ... 階層:2    `> 表引き
5178                            :       '
5179                            :       |
5180             .        ... 階層:11   |
5181            / \                     |
5182           x   y      ... 階層:12  .' <- 表引きの結果 y が(>=NC)の場合、
5183              / \                        木の続きがあることを示す
5184             z   .    ... 階層:13  `.
5185                                    |
5186                            :        >   left[]/right[]で表現 (階層12がroot)
5187                      ... 階層:16  .'
5188 ----------------------------------------------------------------------------
5189
5190 階層 12 より下(13 以上の bit 数の Huffman 符号)の Huffman 木について
5191 left[], right[] で表しているようだ。
5192
5193 上の例で、13 ビットの Huffman 符号で符号化される復号語 z については、
5194 y にその木のルートノードの値(y > NC)が割り当てられる。
5195
5196 図から明らかだが、復号語そのものを指す 12 bit の符号(たとえば x)と、木
5197 のルートノードを指す 12 bit の符号(たとえば y)が同じ符号となることはな
5198 い。これは Huffman 符号が語頭条件を満たすからである。
5199
5200 では、y に割り当てる値はどのように決まるかというとおそらく NC 以上の数
5201 値を連番か何かで割り当てているのではないかと思われる。これについてはこ
5202 れ以降で確認する。
5203
5204 なお、階層が深い Huffman 木とは、bit 長が長い符号であり、bit 長が長いと
5205 いうことは出現頻度が低いはずであるから、left[], right[] で木を辿る回数
5206 は少ないはずである。これは理にかなっている。
5207
5208 あと、この木に必要なサイズはいくつであるかという点が気になる。これは、
5209 階層 13 以上のノードの数となるが、階層 n の二分木のノード(ここでは、葉
5210 および節をノードと呼ぶことにしている。「ノード」と「節」は本来同じ意味
5211 なのだがもう書いてしまったので仕方ない)の数が 2^(n+1) - 1 である(階層
5212 n の葉の数は 2^n で、節の数は 2^n-1)から、
5213
5214     階層 12 のノードの数は、(2^(12+1)-1) = 8191
5215     階層 16 のノードの数は、(2^(16+1)-1) = 131071
5216
5217 となり、その差は 131071 - 8191 = 122880 となる。単純に考えると、最悪の
5218 場合を想定して left[], right[] 合計で 122880 個の領域が必要であることに
5219 なる。そうすると領域を節約するという目的からは元も子もないこととなる。
5220
5221 しかしよくよく考えるとその心配はないようだ。実際のところはハフマン木全
5222 体の葉の数は文字種の数 NC しか存在しないため(ハフマン木構築アルゴリズム
5223 及びエンコード処理を確認) 2*NC-1がハフマン木全体のノード数である。そし
5224 て、
5225
5226     階層 12 の葉の数の最小値 12
5227
5228 であることから、階層13以下のノード数は
5229
5230     木で表現する最大の葉の数 NC-12
5231
5232 となり木のノード数の最大値は 2*(NC-12)-1 となる。さらに、この木の葉は
5233 left right の添え字にならないのでその分の領域は厳密には不要である。
5234
5235 # 後の解析でわかることだが、left, right の添え字は NC 以上であるため
5236 # 0...NC の範囲の領域は(c_len については)使われない。また、変数 left
5237 # right はエンコード処理でハフマン木を作る際に使用した変数を流用してい
5238 # るため領域が足りないということはない。
5239 #
5240 # 表引きを行わずに Huffman 木を完全に読み込むことを考えると、left,
5241 # right は、c_len については、
5242 #   NC ... 2*NC-1
5243 # が使用され、位置(p_len)に対する Huffman 木の構築時には
5244 #   np ... 2*np-1
5245 # の範囲が使用される。
5246 #
5247 # c_len, p_len の Huffman 木は同時に存在するので、left, right ともに
5248 # 下図の領域が同時に使用されることになる。
5249 # (t_len は c_len の読み込みが完了した時点で不要となるため、この図には
5250 # 表していない)
5251 #
5252 #   0         np     2*np-1      NC                            2*NC-1
5253 #   +---------+-------+----------+------------------------------+
5254 #   |未使用域 |使用域 | 未使用域 | 使用域                       |
5255 #   +---------+-------+----------+------------------------------+
5256 #
5257 #                                                         np: 14〜17
5258 #                                                         NC: 510
5259 #
5260 # ソースを見てみても、このことはなかなか気がつきにくい。やはり、left,
5261 # right は各 Huffman 符号用に領域を割り当てた方がわかりやすいだろう。
5262
5263 続きを見ていこう、(E) の処理
5264
5265     /* (E) */
5266     /* initialize */
5267     j = start[tablebits + 1] >> m;
5268     k = 1 << tablebits;
5269     if (j != 0)
5270         for (i = j; i < k; i++)
5271             table[i] = 0;
5272
5273 table[] の初期値として 0 を設定している。
5274
5275 Huffman 符号である j には、start[tablebits + 1] つまり、ビット長
5276 tablebits の最大の Huffman 符号+1が設定される。(繰り返しになるが、ビッ
5277 ト長 i の Huffman 符号は、start[i] から start[i+1]-1の範囲の符号である)
5278 m で右シフトしているのは、(D) では、tablebits までの start[i] しか 右
5279 シフトしていないためである。
5280
5281 k は、1 << tablebits から tablebits + 1 番目のビットが立った数値となる。
5282 この値はつまりは tablebits ビットの Huffman 符号の最大値 + 1 である。
5283
5284 そして、start[j...k] の範囲について 0 を設定している。結局 tablebits の
5285 ビット長の範囲で割り当てられることがない Huffman 符号に対して 0 で初期
5286 化しているということのようだ。
5287 割り当てられることがない Huffman 符号ということは 0 で初期化しているの
5288 はこの後の処理で木のノードになる予定の領域だと思われる。
5289
5290 ちょっと疑問なのだが、if (j != 0) は必要なのだろうか?つまり、j = 0 と
5291 して、table[] の全要素について 0 で初期化しても何も問題はないのではない
5292 か?そしてそれなら make_table() に table[] のサイズ(sizeof)を渡し
5293 memset() にて 0 で初期化した方が(機械語の命令数が少なくなるので)速いの
5294 ではないだろうか?まあ、速さは良いとしても memset() の方がよりわかりや
5295 すいと思う。
5296
5297 次に (F) の処理。少し大きいので下記の通り (F.1), (F.2) と細分化してみた。
5298
5299     /* (F) */
5300     /* create table and tree */
5301     for (j = 0; j < nchar; j++) {
5302         k = bitlen[j];
5303         if (k == 0)
5304             continue;
5305         /* (F.1) */
5306         l = start[k] + weight[k];
5307         if (k <= tablebits) {
5308             /* code in table */
5309             for (i = start[k]; i < l; i++)
5310                 table[i] = j;
5311         }
5312
5313         /* (F.2) */
5314         else {
5315             /* code not in table */
5316             p = &table[(i = start[k]) >> m];
5317             i <<= tablebits;
5318             n = k - tablebits;
5319             /* make tree (n length) */
5320             while (--n >= 0) {
5321                 if (*p == 0) {
5322                     right[avail] = left[avail] = 0;
5323                     *p = avail++;
5324                 }
5325                 if (i & 0x8000)
5326                     p = &right[*p];
5327                 else
5328                     p = &left[*p];
5329                 i <<= 1;
5330             }
5331             *p = j;
5332         }
5333         start[k] = l;
5334     }
5335
5336 この部分は一時変数の量が多い、i,j,k,l,m,n,p まで使われている。
5337 (しかも、前の処理までと変数の用途が違ってたりするので、複雑になっている)
5338
5339 一旦、以下に整理してみた。(どの変数の添え字に使われているかなどからひと
5340 めで判断してみた結果である。そして、n, p についてはひとめではわからなかっ
5341 た)。
5342
5343     i: Huffman 符号
5344     j: 復号文字
5345     k: j の Huffman 符号のビット長(i のビット長)
5346     l: ビット長 k に対するHuffman 符号の終値 (start[k] <= Huffman(k) < l)
5347     m: Huffman 符号表をshiftするビット数
5348     n: ??
5349     p: ??
5350
5351 これを踏まえて処理内容を見てみよう。まず、(F)全体について
5352
5353     /* (F) */
5354     /* create table and tree */
5355     for (j = 0; j < nchar; j++) {
5356         k = bitlen[j];
5357         if (k == 0)
5358             continue;
5359         /* (F.1) */
5360         /* (F.2) */
5361
5362         start[k] = l;
5363     }
5364
5365 復号文字 j = 0 ... nchar をループしている(j が復号文字であることは、
5366 bitlen[] の添え字であることからわかる)。
5367
5368 そして、k = bitlen[j] が 0 であれば、(Huffman 符号化されていなければ)そ
5369 の文字はスキップしている。ここは、良いだろう。
5370
5371 (F.1), (F.2) は、Huffman符号化されている文字 j について
5372 の処理となる。(最後の start[k] = l は後で考えよう)
5373
5374 処理の目的から大雑把に
5375
5376     (F.1) k <= tablebits の場合、表引き可能な範囲なので、
5377     table[i] に復号文字をセット。
5378
5379     (F.2) k > tablebits の場合、表引き不可能な範囲なので、
5380     left[],right[]に復号文字をセット。
5381
5382 といった処理をしていることが予想できる。では、(F.1) を見てみる。
5383
5384         /* (F.1) */
5385         l = start[k] + weight[k];
5386         if (k <= tablebits) {
5387             /* code in table */
5388             for (i = start[k]; i < l; i++)
5389                 table[i] = j;
5390         }
5391
5392 ビット長 k <= tablebits の場合、ビット長 k が取りうる範囲の Huffman 符号
5393 について、文字 j を割り当てている。
5394 ビット長 k が取りうる範囲の Huffman 符号とは
5395
5396     start[k] <= Huffman符号 i < l
5397
5398 である。例で示すと(簡便化のために、最大の Huffman 符号長を 2 とする)
5399
5400        /\               a: 0
5401       a /\              b: 10
5402         b c             c: 11
5403
5404 文字 a は、1 ビットで、00...10 の範囲(つまり、00 と 01)
5405 文字 b は、2 ビットで、10...11 の範囲(つまり、10)
5406
5407 となる。ここで、文字 c については文字 b と同じ符号が割り当てられるかのように
5408 見えるが、実際は(F) のループの末尾に出てきた
5409
5410         start[k] = l;
5411
5412 によって、ビット長 k に対する初期値 start[k] が変更されるため、その心配
5413 はないようだ。
5414
5415 次に、(F.2)
5416
5417         /* (F.2) */
5418         else {
5419             /* code not in table */
5420             p = &table[(i = start[k]) >> m];
5421             i <<= tablebits;
5422             n = k - tablebits;
5423             /* make tree (n length) */
5424             while (--n >= 0) {
5425                 if (*p == 0) {
5426                     right[avail] = left[avail] = 0;
5427                     *p = avail++;
5428                 }
5429                 if (i & 0x8000)
5430                     p = &right[*p];
5431                 else
5432                     p = &left[*p];
5433                 i <<= 1;
5434             }
5435             *p = j;
5436         }
5437
5438 ちょっと複雑だが難しいことはない。まず、
5439
5440     p = &table[(i = start[k]) >> m];
5441
5442 の部分は、
5443
5444     i = start[k];
5445     p = &table[i >> m]
5446
5447 であり、i は Huffman 符号の初期値である。i が m で右シフトされているが、
5448 初期化部分の (D) では、tablebits までのインデックスに対する値
5449 (start[1..tablebits])しかシフトしておらず、この (F.2) の部分は k >
5450 tablebits であるから start[k] は m でシフトされていない Huffman 符号と
5451 なっている。
5452
5453 p は、Huffman符号 i の table 上の位置を指す。後で、*p には木のルート位
5454 置を記録することが予想される。
5455
5456 次に、
5457
5458     i <<= tablebits;
5459
5460 i を tablebits でシフトすることで、Huffman 符号 i は最上位の tablebits
5461 を除いた残りのビット部分になる。この残りのビット部分を木で表す。
5462
5463 次に
5464
5465     n = k - tablebits;
5466
5467 n は、書き換えた i のビット長を示す。木には深さ n の階層が作成されるの
5468 だろう。
5469
5470 ----------------------------------------------------------------------------
5471                         k: Huffman 符号 i のビット長
5472                 |----------------|
5473
5474                  tablebits (表引きで復号する部分)
5475                 |-----------|
5476                 +-----------+----+
5477   Huffman 符号 i|           |xxxx|
5478                 +-----------+----+
5479                             |----|
5480                               n: 木を辿ることで復号する部分
5481
5482 tablebits ビットシフトにより、書き換えた i は、xxxx の部分が最上位ビッ
5483 トとなる。
5484
5485                 +----+-----------+
5486   Huffman 符号 i|xxxx|           |
5487                 +----+-----------+
5488                 |----|
5489                   n: 木を辿ることで復号する部分
5490
5491 ----------------------------------------------------------------------------
5492
5493 そうして、書き換えた i について、木を構築する。
5494
5495             /* make tree (n length) */
5496             while (--n >= 0) {
5497                 /* (F.2.1) */
5498                 if (*p == 0) {
5499                     right[avail] = left[avail] = 0;
5500                     *p = avail++;
5501                 }
5502                 /* (F.2.2) */
5503                 if (i & 0x8000)
5504                     p = &right[*p];
5505                 else
5506                     p = &left[*p];
5507                 i <<= 1;
5508             }
5509             /* (F.2.3) */
5510             *p = j;
5511
5512 (F.2.1) *p が 0 であれば、*p に avail として、新たな根を割り当てその右
5513 と左の子は 0 で初期化しておく。
5514
5515 (F.2.2) Huffman 符号(の tablebits 以降のビット) i について最上位ビット
5516 が立っていれば右の子 right、ビットが立っていなければ左の子 left を作成
5517 (領域を p で予約)する。
5518
5519 ループによってビットパターンに沿って *p には、avail++ が順に割り当てられる。
5520
5521 avail の初期値は nchar なので、*p >= nchar (c_table[] なら NC)である。
5522
5523 この while ループを抜けた後に(F.2.3)にて
5524
5525     *p = j;
5526
5527 が設定され、木の葉には、*p < nchar である値が設定されている。(そしてこ
5528 れが求める復号文字である。)
5529
5530 (F.2.1) で、if (*p == 0) としている理由は、
5531
5532         .
5533         \
5534          .  <- p
5535         /
5536
5537 と p が既に割り当てられており、その節に
5538
5539         .
5540         \
5541          .  <- p
5542         / \
5543
5544 と右の子が追加される場合を想定している。この時点で (E) にて table を 0
5545 で初期化している理由がわかった。木の根が割り当てられているかどうかの判
5546 定が必要だから初期化で未割り当てにしておく必要があるということだ。もち
5547 ろん (E) で書いた通り table 全体を 0 で初期化しても問題はないしその方が
5548 わかりやすいと思った。
5549
5550 ここまで、c_table[] を作成する場合だけを見てきたが、pt_table の場合も考
5551 え方は同じなので解析の必要はないだろう。
5552
5553 ただ、この段階で表引きのビット数が c_table については 12 が p_table につ
5554 いては 8 が選ばれている理由が不明である。これは、想像だが pt_table が符号化する
5555 文字種は少ないので pt_table については 8 bit のテーブルを使っても表引きの確率が
5556 高いのではないだろうか。つまり、領域(pt_table のサイズ)の節約を優先しているので
5557 はないかと思う。
5558
5559 \f
5560 LHA ファイルの構造(まとめ)
5561 --------------------------
5562
5563 以上で LHa for UNIX についての一通りの処理を見たことになる。ここからは
5564 実装については極力触れずに LHA ファイルの構造(圧縮形式)についてまとめて
5565 みよう。また、ここまで細部についてなぞのままとしていた部分があるのでこ
5566 れらも再検討して明らかにしよう。
5567
5568 ----------------------------------------------------------------------------
5569 < LHA ファイルの構造(1 ブロック分) >
5570
5571     +-----------+
5572     | blocksize |
5573     +-----------+
5574        16bit
5575
5576  t_len: c_len のハフマン木           |
5577     +-----+--------------------+     |  +-----+-----+
5578     | len |       t_len        |     |  |  0  |root |
5579     +-----+--------------------+     |  +-----+-----+
5580       TBIT        ?? bit             |  TBIT  TBIT
5581                                      |
5582  c_len: 文字と長さのハフマン木       |
5583     +-------+------------------+     |  +-------+-------+
5584     |  len  |     c_len        |     |  |  0    | root  |
5585     +-------+------------------+     |  +-------+-------+
5586       CBIT        ?? bit             |   CBIT    CBIT
5587                                      |
5588  p_len: 位置の(ビット長の)ハフマン木 |
5589     +-----+--------------------+     |  +-----+-----+
5590     | len |      p_len         |     |  |  0  |root |
5591     +-----+--------------------+     |  +-----+-----+
5592      pbit         ?? bit             |
5593
5594  圧縮文(文字と長さ、位置のビット長のハフマン符号と位置の値)
5595     +---------------------+
5596     |  圧縮文             |
5597     +---------------------+
5598
5599
5600         method  maxmatch  dicsiz   dicbit   np(dicbit+1) pbit(np <= 2^pbit)
5601         ------------------------------------------------------------------
5602         -lh4-        256  2^12     12       14 (or 13?)  4 (14 <= 2^4)
5603         -lh5-        256  2^13     13       14           4 (14 <= 2^4)
5604         -lh6-        256  2^15     15       16           5 (16 <= 2^5)
5605         -lh7-        256  2^16     16       17           5 (17 <= 2^5)
5606
5607         threashold 3
5608         NC         256+maxmatch-threshold+1{510}
5609         CBIT       9 (NC <= 2^9)
5610
5611         MAXDEPTH   16
5612         NT         MAXDEPTH+1
5613         TBIT       4 (NT <= 2^4)
5614 ----------------------------------------------------------------------------
5615
5616 # ソース上 pt_len と書いていた部分は、以降の説明のために p_len, t_len
5617 # と名前を変更している。変数 pt_len は単に領域の節約のために使い回され
5618 # ていただけでありいわば実装の都合であるからだ。
5619
5620 上記は、LHA ファイルの構造(ヘッダを除く)を表したものである。LHA ファイ
5621 ルはヘッダと上記ファイル構造の集まりで 1 ファイルの圧縮ファイルとなり、
5622 それが複数集まってアーカイブを構成する。また、約束事としてアーカイブの
5623 最後は 1 バイトの 0 が付加されることとなっている。
5624
5625 圧縮形式は method によってパラメータが変化する。ここでは、method
5626 lh4,5,6,7 の形式しか触れないので method の違いは slide 辞書の辞書サイズ
5627 の違いしかない。ただし、辞書のサイズの違いに連動して圧縮形式に影響を与
5628 える変数があるのでこれも上記にまとめている。(小文字はその変数、大文字は
5629 定数を意味する)
5630
5631 図中 t_len, c_len, p_len は、Huffman 木の情報を格納した領域を表し、「圧
5632 縮文」が平文を圧縮した本体となる。
5633
5634 また、3 つのハフマン木の出力形式についてハフマン木が構築できない場合の
5635 形式を右側に併記した。これは、復号語が 1 種類しかない場合を示しており
5636 root がその復号語となる。例えば、c_len がこの形式で書かれていた場合は圧
5637 縮文を見ずに blocksize 分 root を出力すれば復号となる。なお、c_len がこ
5638 の形式の場合 c_len のハフマン木を示す t_len も右側の形式になるが、この
5639 ときの t_len の root の値は 0 にする事になっているようだ(ただ、復号処理
5640 はこの値に依存しない方が良いだろう)。
5641
5642 圧縮文は、文字 c(0 .. 255)と <長さ len, 位置 off> を Huffman 木で符号化
5643 した Huffman 符号の並び(と位置の値)で表される。
5644
5645 <len, off> は、slide 辞書法での圧縮結果であり、辞書上の off 位置の len
5646 文字をこの形式で表している。辞書とは、現在復号を開始している位置から辞
5647 書サイズ分遡った復号済みの平文を指す。
5648
5649 位置 off は、0 が「1文字前」を現す。従って辞書サイズ dicsiz が 2^3 の場
5650 合を例に考えると位置の値が 2^3-1 の場合では、2^3 文字前を示すことになる。
5651
5652          辞書サイズ
5653          (復号済みの平文)
5654         |-------------|
5655         8 7 6 5 4 3 2 1 x
5656         ^                \
5657         |                 復号開始位置
5658         x から 8 文字前の位置
5659
5660 従って、off の値の範囲は 0 ... 2^dicbit である。
5661
5662 長さ len の値は 256 の場合に文字列長 3 バイトを示す。つまり、len に対し
5663 て len-256+3 が実際の長さを示す。(ただし、-lzs- の場合は len-256+2 が実
5664 際の長さとなる)
5665
5666 len の値が 256 から始まるのは、1 バイトの文字 c の範囲 0..255 と重なら
5667 ないようにするためである。(長さは、文字と同じ Huffman 木で符号化される)
5668
5669 実際の長さが 3 から始まるのは、<len,off> の組が 4 バイトで表されるため、
5670 マッチ長として 2 文字以下を <len,off> の形式で表すと圧縮ではなく伸長に
5671 なってしまうからである。
5672
5673 # 長さが 3 の場合も同じように思えるがなぜだろうか?
5674 # <len,off> はより正確には 3 バイトと 1 ビットである。というのも len が
5675 # 256 から始まるから len の情報は 9 ビット必要である。len が 256...510
5676 # の範囲なので 8 ビットのようにも思えるが文字 c との判別のための情報と
5677 # して 1 ビット余計に必要なのである。これは予想だが、当時 -lh5- の辞書
5678 # サイズであれば位置は 13 ビットしか使用しないため、<len,off> は 22 ビッ
5679 # トと見なせる。従って 3 バイトは <len,off> の形式にした方が良いと判断
5680 # したのではないだろうか?では、-lh7- の場合は、長さの最小値は 4 にした
5681 # 方が良いのだろうか?おそらく位置の値に 16 ビットすべて使用する確率が
5682 # 低く多くの場合は効果がないのではないかと思う。
5683
5684 また、maxmatch{256}が最大マッチ長であり、このときの len の値は
5685 256+maxmatch-3{509=NC-1} である。
5686
5687              len(復号語)            実際の長さ
5688              ----------------------------------
5689              256..256+maxmatch-3    3..maxmatch
5690
5691 これらを圧縮した形式「圧縮文」は Huffman 符号の連続(および位置の値。後
5692 述)である。圧縮文のサイズは上図の blocksize で表され、blocksize 数の文
5693 字数の情報が出力されている。ここで、「文字数」は <長さ, 位置> の組も 1
5694 文字としてカウントされる。文字なのか、<長さ, 位置> の組なのかの判別は、
5695
5696     復号した1文字 >= 256 の場合
5697       長さを示す。(そしてその直後に位置の Huffman 符号がある)
5698
5699     復号した1文字 < 256 の場合
5700       文字を示す。
5701
5702 となっている。そして、文字と長さの Huffman 符号は、Huffman 木の情報を示
5703 す c_len により復号され、位置の Huffman 符号は、p_len により復号される。
5704
5705 位置の Huffman 符号は位置の情報の上位ビットが 0 である部分を除いたビッ
5706 ト長を符号化したものであり位置そのものの符号ではない。従って位置の符号
5707
5708
5709           +------------------------------+----------+
5710           | 位置のビット長の Huffman 符号| 位置の値 |
5711           |  (p_lenから復号)             |          |
5712           +------------------------------+----------+
5713
5714 と Huffman 符号に続いて実際の値を示すビット列が出力されている。例で示す
5715 と以下の通りだ。
5716
5717 ----------------------------------------------------------------------------
5718 位置(off)の出力例
5719
5720 off = 64 の場合
5721
5722      |---- 16 bit -------|
5723      +----+----+----+----+
5724 off  |0000 0000 0100 0000|
5725      +----+----+----+----+
5726                  |-7 bit-|
5727
5728 この圧縮文は以下(長さが 7 bit であるという情報(Huffman符号化)と値のペア)
5729
5730                        |-6 bit-|
5731      +-----------------+-------+
5732      | 7 のHuffman符号 |00 0000|
5733      +-----------------+-------+
5734
5735 この例で 必要ビットである 7 bit 目は必ず 1 であるため値の部分は 6 bit
5736 出力すればよい。
5737
5738 off = 1 の場合
5739
5740      |---- 16 bit -------|
5741      +----+----+----+----+
5742 off  |0000 0000 0000 0001|
5743      +----+----+----+----+
5744                        |-|
5745                         1 bit
5746
5747 この圧縮文は以下(長さが 1 bit であるという情報のみ)
5748
5749      +-----------------+
5750      | 1 のHuffman符号 |
5751      +-----------------+
5752
5753 off = 0 の場合
5754
5755      |---- 16 bit -------|
5756      +----+----+----+----+
5757 off  |0000 0000 0000 0000|
5758      +----+----+----+----+
5759                         ||
5760                          0 bit
5761
5762 この圧縮文は以下(長さが 0 bit であるという情報は値が 0 と見なされる)
5763
5764      +-----------------+
5765      | 0 のHuffman符号 |
5766      +-----------------+
5767 ----------------------------------------------------------------------------
5768
5769 # 位置を直接 Huffman 符号化しない理由は位置を指す値は slide 辞書上の任
5770 # 意の位置を指すためデータの範囲が広く(-lh7- の場合で 0 ... 2^16)
5771 # Huffman 符号化による圧縮の効果が期待できないためだと思われる。一方、
5772 # 位置情報は辞書上の比較的近い位置にマッチしやすいはずであるから Huffman
5773 # 符号化の対象であるビット長は小さい値に偏りやすいはずだ。(偏りがある
5774 # と Huffman 符号化の効果が高い)
5775
5776 Huffman 木の情報をファイルに出力する際 p_len, c_len は、それぞれに適し
5777 た形式で圧縮して出力される。特に c_len は、さらに、t_len により
5778 Huffman 符号化することで圧縮を行う。
5779
5780 ハフマン木 {p,c,t}_len はどの復号語がハフマン木のどの階層にあるかの情報
5781 により表されている。これだけの情報だと木を一意に表すことができないよう
5782 に思えるが、実際は
5783
5784          Huffman 木1     Huffman 木2
5785               .             .
5786              / \           / \
5787             .   a         a   .
5788            / \               / \
5789           c   b             b   c
5790
5791 Huffman 木1 と Huffman 木2 は枝の伸び方と葉に文字を振る順番の違いでしか
5792 ない。そこで、LHA では、以下の規則を設けることで、{p,c,t}_len の情報だ
5793 けで木を構築できるようにしている。
5794
5795        o 右の木を優先して作成する。(右の枝をビット 1 とする)
5796        o 同じ階層の復号語の割り当てはコード順に左から割り当てる。
5797
5798 例えば、
5799
5800     c_len['a'] = 2
5801     c_len['b'] = 1
5802     c_len['c'] = 3
5803     c_len['d'] = 3
5804
5805 という情報が書かれている場合、
5806
5807     階層 1 に 1 個の葉(値が 1 である c_len[] が 1 個)
5808     階層 2 に 1 個の葉(値が 2 である c_len[] が 1 個)
5809     階層 3 に 2 個の葉(値が 3 である c_len[] が 2 個)
5810
5811 があるので以下の Huffman 木の形に決まる(右の木を優先して作成する)。
5812
5813             .
5814            / \
5815           .   .       -- 階層1
5816              / \
5817             .   .     -- 階層2
5818                / \
5819               .   .   -- 階層3
5820
5821 そして各階層毎に文字をコード順に割り当てると
5822
5823             .
5824            / \
5825           b   .       -- 階層1
5826              / \
5827             a   .     -- 階層2
5828                / \
5829               c   d   -- 階層3
5830
5831
5832 と一意に定まることとなる。(「階層毎のコード順」の意味が正確に伝わるよう
5833 あえて、b と a を逆にしてみた。)
5834
5835 なお、{p,c,t}_len の値はある文字の階層の位置を示すが、これはつまりある
5836 文字を Huffman 符号化したときの符号長(bit 長)を示していることになる。以
5837 降、{p,c,t}_len は添え字が復号語、値が符号長を表す配列であるものとして
5838 説明を行う。この配列のサイズは圧縮対象の文字集合の要素数であり、各要素
5839 は 0..16 の値を持つ。(LHA の Huffman 木のルールで木の階層は 16 までとなっ
5840 ている)そして、値 0 はその文字が平文に現れないことを示す。
5841
5842 ----------------------------------------------------------------------------
5843         Huffman 木    要素数   値の範囲  要素数のビット長
5844         (配列)                  (符号長)
5845         p_len          np       0..16     pbit
5846         c_len          NC       0..16     CBIT
5847         t_len          NT       0..16     TBIT
5848
5849                       要素数は圧縮対象の文字集合の数
5850                       c_len[x]=0 の場合、文字 x は複合した結果に現れない
5851                       要素数のビット長は要素数を表現するのに必要なビット
5852                       長(この後で出てくる)
5853 ----------------------------------------------------------------------------
5854
5855 では、Huffman 木の情報の出力形式について整理しよう。
5856
5857 まず、p_len[] の出力フォーマットを下記に示す。
5858
5859 ----------------------------------------------------------------------------
5860 < p_len[] の出力フォーマット >
5861
5862              0      pbit{4 or 5}
5863              +-------+-----------+-----------+--   --+-----------+
5864              |   n   |  p_len[0] |  p_len[1] | ...     p_len[n-1]|
5865              +-------+-----------+-----------+--   --+-----------+
5866
5867 p_len[i] <= 6 の場合
5868
5869               0     3bit
5870               +-----+
5871     p_len[i]  | | | |
5872               +-----+
5873
5874 p_len[i] >= 7 の場合
5875
5876               0              p_len[i] - 3
5877               +----------------+
5878     p_len[i]  |1 1 1 1 ... 1 0 |
5879               +----------------+
5880
5881 p_len[n...np] は、0 となる。
5882
5883 ----------------------------------------------------------------------------
5884
5885 先にも書いた通り p_len は位置の値の必要ビット長を Huffman 符号化したと
5886 きの木の情報である。スライド辞書のサイズは dicsiz であり -lh7- の場合で
5887 16 bit で位置を指すことができるから、p_len は位置のビット長 0 .. 16 の
5888 17個(np個)の値を Huffman 符号化した結果(の符号長)である。
5889
5890 そして p_len 自体の出力については、0 .. 6 の値(Huffman 符号長)について
5891 は 3 bit で、それ以上の値については 0 が現れるまでのビット数で表すよう
5892 になっている。
5893
5894 なお p_len は np 個の固定長配列だが、出力形式としては先頭に pbit 幅
5895 の p_len の要素数を出力している。これはなぜかというと p_len の後方の値
5896 (p_len[n...np])が 0 である場合にその要素を出力しないで済むようにするた
5897 めである。これは他の Huffman 符号についても同様である。
5898
5899 # なぜ、このような形式になっているか考えてみた。
5900 #
5901 # p_len の値の範囲は、0 .. 16 であるから 1 要素は 6 bit で表現可能であり、
5902 # 単純に出力した場合を考えると 6 * 17 = 102 bit で表現可能である。
5903 #
5904 # 一方、p_len の出力形式の場合、LHA の Huffman 木の階層は最大 16 階層であ
5905 # ることから、最悪すべての p_len[] についてビット数の形式(p_len[] >= 7)
5906 # が使われた場合 1 つの p_len[] に 16-3 bit * 17 = 221 bit 使うことにな
5907 # り最悪の使用領域は大きくなってしまうように思えてしまう。
5908 #
5909 # しかし、実際にはそうはならない。というのも np が 17 であるから
5910 # Huffman 木の葉の数は最大でも 17 にしかならない。そして葉の数が np で
5911 # 最も Huffman 木が深くなるのは
5912 #
5913 #        .
5914 #       / \
5915 #      .   .        --  1 階層目
5916 #         / \
5917 #        .   .      --  2 階層目
5918 #           / \
5919 #          .   .    --  3 階層目
5920 #              :
5921 #              .
5922 #             / \
5923 #            .   .  -- 16 階層目
5924 #
5925 # となる場合で、この場合の p_len の出力ビット長は
5926 #
5927 #    2*(16-3) + (15-3) + ... (7-3) + 6*3
5928 #  = 2*13 + 12 +  ... 4 + 18
5929 #  = 167 bit
5930 #
5931 # である。単純に出力する場合に比べると 65 bit 増える。また、1 階層減ら
5932 # した場合は、
5933 #
5934 #        .
5935 #       / \
5936 #      .   .        --  1 階層目
5937 #         / \
5938 #        .   .      --  2 階層目
5939 #           / \
5940 #          .   .    --  3 階層目
5941 #              :
5942 #             / \
5943 #            .   .    -- 13 階層目
5944 #               / \
5945 #             .     .
5946 #            / \   / \
5947 #           .   . .   .  -- 15 階層目
5948 #
5949 #    4*(15-3) + (13-3) + ... (7-3) + 6*3
5950 #  = 4*12 + 10 + ... 4 + 18
5951 #  = 115 bit
5952 #
5953 # である。なお、この木の場合も葉は np 個ある。もう 1 階層減らしてみよう。
5954 #
5955 #        .
5956 #       / \
5957 #      .   .        --  1 階層目
5958 #         / \
5959 #        .   .      --  2 階層目
5960 #           / \
5961 #          .   .    --  3 階層目
5962 #              :
5963 #           /     \
5964 #          .       .        -- 12 階層目
5965 #         / \     /  \
5966 #        .   .   .     .    -- 13 階層目
5967 #               / \   / \
5968 #              .   . .   .  -- 14 階層目
5969 #
5970 #
5971 #    4*(14-3) + 2*(13-3) + (11-3) + ... + (7-3) + 6*3
5972 #  = 4*11 + 2*10 + 8 + ... 4 + 18
5973 #  = 112 bit
5974 #
5975 # この調子で、さらに減らすと
5976 #
5977 # 13 階層
5978 #    4*(13-3) + 2*(12-3) + (10-3) + ... + (7-3) + 6*3
5979 #  = 4*10 + 2*9 + 7 + ... + 4 + 18
5980 #  = 98
5981 #
5982 # 13 階層目で単純に出力するよりも小さくなった。感覚的には、あまり効果は
5983 # 期待できないように見える。これは恐らくは p_len については符号長が 6
5984 # 以下になる場合が多いのであろう(すべてが 6 以下であれば、3 * 17 = 51
5985 # bit であり 51 bit 削減できる)。階層が 6 である Huffman 木の葉の数は最
5986 # 大 2^6 {64} であるから NP{17} 種類の平文を格納する率が高いのであろう。
5987
5988 続いて c_len[] の出力フォーマットを示す。
5989
5990 ----------------------------------------------------------------------------
5991 < c_len[] の出力フォーマット >
5992
5993              0       CBIT{9}
5994              +-------+-----------+-----------+--   --+-----------+
5995              |   n   |  c_len[0] |  c_len[1] | ...     c_len[n-1]|
5996              +-------+-----------+-----------+--   --+-----------+
5997
5998 c_len[i] == 0 の場合
5999
6000  0 が続く数を count とすると、
6001
6002  count == 1 の場合
6003
6004                 t_len[0]
6005               <---------->
6006              +------------+
6007              |  t_code[0] |
6008              +------------+
6009
6010  count == 2 の場合
6011
6012                 t_len[0]     t_len[0]
6013               <----------> <---------->
6014              +------------+------------+
6015              |  t_code[0] |  t_code[0] |
6016              +------------+------------+
6017
6018  count == 3..18 の場合
6019
6020                 t_len[1]    4 bit
6021               <----------> <------>
6022              +------------+-------+
6023              |  t_code[1] |count-3|
6024              +------------+-------+
6025
6026  count == 19 の場合
6027
6028                  t_len[0]    t_len[1]    4 bit
6029               <----------> <----------> <------>
6030              +------------+------------+-------+
6031              |  t_code[0] |  t_code[1] |count-3|
6032              +------------+------------+-------+
6033
6034   count >= 20 の場合
6035
6036                  t_len[2]    CBIT{9}
6037               <----------> <------>
6038              +------------+--------+
6039              |  t_code[2] |count-20|
6040              +------------+--------+
6041
6042 c_len[i] > 0 の場合
6043
6044                t_len[c_len[i]+2]
6045               <----------------->
6046              +-------------------+
6047              | t_code[c_len[i]+2]|
6048              +-------------------+
6049
6050 c_len[n...NC] は、0 となる。
6051
6052 ----------------------------------------------------------------------------
6053
6054 c_len[] の値はある程度 0 が連続する場合が多いことが期待できる。
6055 c_len[i]=0 の場合というのはその文字(i)が平文に現れない場合を示す。
6056 ASCII テキストファイルの圧縮なら 0..127 の範囲のコードしか使われないた
6057 めそれ以外は 0 になるなどである。そして c_len を単純に出力することを考
6058 えたとき c_len[i]=0 である情報を多数出力することになり領域が無駄である
6059 (c_len は NC{255+256+2-3=510} 個の要素を持ちその中で未使用文字や未使用
6060 長が多数あることを想定している)。そこで 0 が連続して現れる特徴を生かし
6061
6062     o c_len[]=0 が連続で1個
6063     o c_len が連続で 3〜18 個
6064     o c_len が連続で20個以上(20〜NC{510})
6065
6066 をそれぞれ一つの文字と見なして Huffman 符号化することで c_len 自体の出
6067 力サイズを小さくしている。これは 0 の出現頻度を単純に Huffman 符号化す
6068 るよりも効果が期待できる。
6069
6070 上図で t_code は c_len を Huffman 符号化したときの符号表を示しており
6071
6072     o t_code[0] ... c_len[i] は 0 が 1 個
6073     o t_code[1] ... c_len[i] は 0 が 3〜18 個(続く4 ビットのビット列で
6074                     個数がわかる)
6075     o t_code[2] ... c_len[i] は 0 が 20〜NC-1個(続く CBIT ビットのビット
6076                     列で個数がわかる)
6077     o t_code[x] ... c_len[i]=x-2  (x>2)
6078
6079 と復号することになる。c_len[i] = 0 が 2 個あるいは 19 個続く場合は
6080
6081     t_code[0] が 2 個
6082     t_code[0] と t_code[1] が 1 個ずつ
6083
6084 で出力されている。
6085
6086 最後に、t_len[] の出力フォーマットを示す。
6087
6088 ----------------------------------------------------------------------------
6089 < t_len[] の出力フォーマット >
6090
6091                                              2 bit
6092    0      TBIT{5}                           |--|
6093    +-------+----------+----------+----------+--+----------+-    -+-----------+
6094    |   n   | t_len[0] | t_len[1] | t_len[2] | x|t_len[x+3]| ...  | t_len[n-1]|
6095    +-------+----------+----------+----------+--+----------+-    -+-----------+
6096
6097 t_len[i] <= 6 の場合
6098
6099               0     3bit
6100               +-----+
6101     t_len[i]  | | | |
6102               +-----+
6103
6104 t_len[i] >= 7 の場合
6105
6106               0             t_len[i] - 3
6107               +----------------+
6108     t_len[i]  |1 1 1 1 ... 1 0 |
6109               +----------------+
6110
6111 t_len[2] の直後は 2 bit の情報が付加される。この値を x{0..3} とすると、
6112 t_len[3 .. x+2] の範囲で 0 が続くことを意味し、この 2 bit 以降は、
6113 t_len[x+3] が続くことになる。x が 0 の場合は、t_len[3] は 0 ではない。
6114
6115 t_len[n...NT] は、0 となる。
6116
6117 ----------------------------------------------------------------------------
6118
6119 基本的な考えは p_len[] の場合と同じである(t_len[] の要素数 NT は 19)。
6120 ただし t_len[3..5] について特別扱いされている。
6121
6122 まず、t_len[] と c_len[x] の値の対応を以下に整理し直す。
6123
6124       t_len[0]  c_len[x]=0 1 つを 1 文字とみなす
6125       t_len[1]  c_len[x]=0 が 3〜18 個連続している塊を 1 文字とみなす
6126       t_len[2]  c_len[x]=0 が 20〜NC{510} 個連続している塊を 1 文字とみなす
6127       t_len[3]  c_len[x]=1
6128       t_len[4]  c_len[x]=2
6129       t_len[5]  c_len[x]=3
6130       t_len[6]  c_len[x]=4
6131           :
6132       t_len[18] c_len[x]=16
6133
6134 t_len[3..5] の特別扱いについて考えると t_len[3..5] の範囲で 0 が連続す
6135 る場合に、2 ビットでそのことを表している。これはつまりこのような場合が
6136 多いのであろう。上で対応関係を示したとおり、t_len[3..5] が 0 である場合
6137 というのは、つまりビット長 c_len[x] が 1..3 の範囲の値を持たない場合を
6138 示す。
6139
6140 # c_len[x] が 1..3 の値を持つ場合というのは Huffman 木においてある 3 文字
6141 # の出現頻度が極端に多い場合を示す。このような場合はあまりないであると想
6142 # 定しているのだろう。
6143 #
6144 #            .
6145 #           / \
6146 #          a   .
6147 #             / \
6148 #            b   .
6149 #               / \
6150 #              c   .
6151 #                 / \
6152 #                .   .
6153 #               / \ / \
6154
6155 以上で LHA ファイルの構造についてひととおり説明したことになる。ただし、
6156 復号処理を考える場合に注意事項がある。これはこの後で説明しよう。
6157
6158 \f
6159 セキュリティバグの考察
6160 ----------------------
6161
6162 2006年8月 LHA の復号処理にセキュリティバグ(CVE-2006-4335,4337,4338)が発
6163 見された。この問題は LHA の実装において符号化については当然あらゆる入力
6164 ファイルを想定した処理となっているが復号については圧縮ファイルが正しい
6165 構造、値で作成されていることしか想定せずに処理が作られていたためである。
6166 つまり、不正な圧縮ファイルが与えられた場合の動作が不定だったのだ。
6167
6168 ここでは、LHA の復号において注意すべき点について考察する。また、ここま
6169 でに解読した LHa for UNIX ver.1.14i のソースはこのセキュリティバグが
6170 残っていたものなので、セキュリティバグの対策を行ったソースについても後
6171 で解析を行うこととする。
6172
6173 以下、LHA の構造を再掲し、各復号処理で注意すべき点を確認しよう。
6174 以降の説明では注意点毎に (1) (2) のように番号を振っている。最終的に
6175 全チェックポイントをチェックするように修正したソースを載せ、この番号で
6176 紐付けを行うこととする。
6177
6178 ----------------------------------------------------------------------------
6179 < LHA ファイルの構造(1 ブロック分) >
6180
6181     +-----------+
6182     | blocksize |
6183     +-----------+
6184        16bit
6185
6186  ハフマン木
6187     +-----------+-----------+-----------+
6188     | t_len     | c_len     | p_len     |
6189     +-----------+-----------+-----------+
6190
6191  圧縮文(文字と長さ、位置のビット長のハフマン符号と位置の値)
6192     +---------------------+
6193     |  圧縮文             |
6194     +---------------------+
6195
6196 ----------------------------------------------------------------------------
6197
6198 (1)
6199 blocksize の読み込みについてこの値は 1〜0xffff については正しいが 0 に
6200 なることはあり得ないので 0 の場合に不正と判断してもよいと思われる。
6201
6202 (2)
6203 「圧縮文」自体については、blocksize 数を頼りに読み込むので blocksize を
6204 越えて圧縮文が存在しても次の block として読まれるだけである。blocksize
6205 に満たない場合は、EOF を検知して早期に不正と判断するように処理した方が
6206 よいだろう。
6207
6208 圧縮文中の文字と長さについては
6209
6210     復号した1文字 >= 256 の場合
6211       長さを示す。(そしてその直後に位置の Huffman 符号がある)
6212
6213     復号した1文字 < 256 の場合
6214       文字を示す。
6215
6216 であり、文字としては 0 .. 255 すべてについて正しい値なので問題はない。
6217
6218 (3)
6219 長さは 256 ... NC{256+maxmatch-3+1} の範囲の値を取るのでこれを超える値
6220 を返す場合は不正と判断してもよい。ただし、この判定自体は c_len を読み込
6221 み Huffman 木を構築するときに行うこともできる。(実際、実装では
6222 Huffman 木にこの範囲内の復号語しか割り当てないのでバグでない限りは発生
6223 しないだろう)
6224
6225 位置については下図の通り位置のビット長の Huffman 符号と位置の値が書かれ
6226 ている。
6227
6228           +------------------------------+----------+
6229           | 位置のビット長の Huffman 符号| 位置の値 |
6230           +------------------------------+----------+
6231
6232 (4)
6233 位置の値としては 0 ... 2^dicbit の範囲の値を持つので Huffman 符号の復号
6234 結果が 0 ... np{dicbit+1} の範囲であれば位置の値部分についてチェックす
6235 る必要はない。従って、c_len と同じくハフマン木の構築の段階で不正な復号
6236 語を返さないようにしていればよい。
6237
6238 では、t_len について見てみる。
6239
6240 ----------------------------------------------------------------------------
6241 < t_len[] の出力フォーマット >
6242
6243                                              2 bit
6244    0      TBIT{5}                           |--|
6245    +-------+----------+----------+----------+--+----------+-    -+-----------+
6246    |   n   | t_len[0] | t_len[1] | t_len[2] | x|t_len[x+3]| ...  | t_len[n-1]|
6247    +-------+----------+----------+----------+--+----------+-    -+-----------+
6248
6249 t_len[i] <= 6 の場合
6250
6251               0     3bit
6252               +-----+
6253     t_len[i]  | | | |
6254               +-----+
6255
6256 t_len[i] >= 7 の場合
6257
6258               0             t_len[i] - 3
6259               +----------------+
6260     t_len[i]  |1 1 1 1 ... 1 0 |
6261               +----------------+
6262
6263 t_len[2] の直後は 2 bit の情報が付加される。この値を x{0..3} とすると、
6264 t_len[3 .. x+2] の範囲で 0 が続くことを意味し、この 2 bit 以降は、
6265 t_len[x+3] が続くことになる。x が 0 の場合は、t_len[3] は 0 ではない。
6266
6267 t_len[n...NT] は、0 となる。
6268
6269 ----------------------------------------------------------------------------
6270
6271 (5)
6272 t_len の出力フォーマットの先頭 TBIT{5} は 0 ... 2^5{32} の範囲の値を格
6273 納できるが t_len の領域サイズは NT{19} なので 0..19 の範囲を超える場合
6274 は不正と判断しなければならない。
6275
6276 (6)
6277 また、t_len[i] >= 7 の場合の形式は bit 0 を検出するまでのビット長が値と
6278 なるが t_len[i] の値はハフマン符号長なので 0 .. 16 の範囲でなければなら
6279 ない。t_len[i] >= 7 の形式の具体的な値の対応は
6280
6281       7: 1110
6282       8: 1111 0
6283       :
6284      15: 1111 1111 1110
6285      16: 1111 1111 1111 0
6286
6287 となっているので、16 の場合のビット長(1 が 12 bit 続く)よりビット長が長
6288 い場合は不正である。(最大 12 ビットまでしか見ないとすることも考えられるが、
6289 LHA の圧縮処理は上の例のように 16 の場合でもビット 0 を出力するので、そ
6290 のような読み方をすると正常な圧縮文を復号できなくなる。)
6291
6292 (7)
6293 さらに、t_len を読み込んだ後に構築した Huffman 木は Huffman 木として整合
6294 性が保たれなければならない。
6295 たとえば、LHA における Huffman 木は以下の性質が守られなければならないは
6296 ずだ。
6297
6298    o t_len[x] <= 16 (LHA の Huffman 木の階層は 16 までである)
6299
6300    o 各階層の葉の数は整合性が保たれなければならない。例えば、1 階層目の
6301      葉の数は最大 2 であり、このとき下位の階層の葉の数は 0 である。各節
6302      は必ず節か葉を持つ。など。
6303
6304 後で処理を解析する際にこの辺りを確認しよう。
6305 なお、1 点目については前述の通り t_len の読み込み時にチェックできる。
6306 2 点目については実装では非常にうまい方法でチェックしている。
6307
6308 続いて c_len[] の出力フォーマットについて考える。
6309
6310 ----------------------------------------------------------------------------
6311 < c_len[] の出力フォーマット >
6312
6313              0       CBIT{9}
6314              +-------+-----------+-----------+--   --+-----------+
6315              |   n   |  c_len[0] |  c_len[1] | ...     c_len[n-1]|
6316              +-------+-----------+-----------+--   --+-----------+
6317
6318 c_len[i] == 0 の場合
6319
6320  0 が続く数を count とすると、
6321
6322  count == 1 の場合
6323
6324                 t_len[0]
6325               <---------->
6326              +------------+
6327              |  t_code[0] |
6328              +------------+
6329
6330  count == 2 の場合
6331
6332                 t_len[0]     t_len[0]
6333               <----------> <---------->
6334              +------------+------------+
6335              |  t_code[0] |  t_code[0] |
6336              +------------+------------+
6337
6338  count == 3..18 の場合
6339
6340                 t_len[1]    4 bit
6341               <----------> <------>
6342              +------------+-------+
6343              |  t_code[1] |count-3|
6344              +------------+-------+
6345
6346  count == 19 の場合
6347
6348                  t_len[0]    t_len[1]    4 bit
6349               <----------> <----------> <------>
6350              +------------+------------+-------+
6351              |  t_code[0] |  t_code[1] |count-3|
6352              +------------+------------+-------+
6353
6354   count >= 20 の場合
6355
6356                  t_len[2]    CBIT{9}
6357               <----------> <------>
6358              +------------+--------+
6359              |  t_code[2] |count-20|
6360              +------------+--------+
6361
6362 c_len[i] > 0 の場合
6363
6364                t_len[c_len[i]+2]
6365               <----------------->
6366              +-------------------+
6367              | t_code[c_len[i]+2]|
6368              +-------------------+
6369
6370 c_len[n...NC] は、0 となる。
6371
6372 ----------------------------------------------------------------------------
6373
6374 (8)
6375 c_len の出力フォーマットの先頭 CBIT{9} は 0 ... 2^9(512) の範囲の値を
6376 格納できるが c_len の領域サイズは NC{510} なので 0..510 の範囲を超える
6377 場合は 不正と判断しなければならない。
6378
6379 (9)
6380 また、
6381  count >= 20 の場合
6382  count == 3..18 の場合
6383
6384 のそれぞれの形式において count の数だけ c_len[i] は 0 が続くがこれが
6385 c_len の*残り*サイズを越える場合も不正である。
6386
6387 (10)
6388 さらに、c_len[i] > 0 の場合の形式において、t_len[x] を復号した結果が
6389 c_len[i]{x}+2 の値でありc_len[i] の値はハフマン符号長なので 0 .. 16 の
6390 範囲でなければならない。これは、c_len, p_len と同じく t_len のハフマン
6391 木の構築の段階で不正な復号語を返さないようにしていればよい。
6392
6393 (11)
6394 もちろん、t_len のときと同様 c_len を読み込んだ後に構築した Huffman 木
6395 は Huffman 木として整合性が保たれなければならない。
6396
6397 続いて p_len[] の出力フォーマットについて考える。
6398
6399 ----------------------------------------------------------------------------
6400 < p_len[] の出力フォーマット >
6401
6402              0      pbit{4 or 5}
6403              +-------+-----------+-----------+--   --+-----------+
6404              |   n   |  p_len[0] |  p_len[1] | ...     p_len[n-1]|
6405              +-------+-----------+-----------+--   --+-----------+
6406
6407 p_len[i] <= 6 の場合
6408
6409               0     3bit
6410               +-----+
6411     p_len[i]  | | | |
6412               +-----+
6413
6414 p_len[i] >= 7 の場合
6415
6416               0              p_len[i] - 3
6417               +----------------+
6418     p_len[i]  |1 1 1 1 ... 1 0 |
6419               +----------------+
6420
6421 p_len[n...np] は、0 となる。
6422
6423 ----------------------------------------------------------------------------
6424
6425 (12)
6426 p_len の出力フォーマットの先頭 pbit{4 or 5} は 0 ... 2^4{16} or
6427 2^5{32} の範囲の値を格納できるが p_len の領域サイズは np{14..17} なので
6428 0..np の範囲を超える場合は不正と判断しなければならない。
6429
6430 ところで復習になるが np は、各圧縮メソッドの辞書のサイズで決まる。対応
6431 を以下に再掲するので確認してほしい。(-lh4- の場合の np はなぜか 13 では
6432 なく14 となっている。おそらく、LHA が実装された当時 -lh6-, -lh7- は存在
6433 せず np や pbit は固定値(定数 NP, PBIT)であったため、-lh4-, -lh5- の両
6434 方に対応できるよう変数の領域サイズを合わせただけであると思う。つまり、
6435 圧縮処理においては、-lh4- の np を 13 と想定しても問題は発生しないと思
6436 われる。逆に復号処理においては、(gzip のように)圧縮 method の情報が渡さ
6437 れない場合は np を 14 とするしかない。なお、このような(gzip のような)
6438 場合 -lh6,7- への対応はできない。最初の pbit 分の要素数の読み込みで 4
6439 ビット読めばよいのか 5 ビット読めばよいのかがわからないからである)
6440
6441         method  maxmatch  dicsiz   dicbit   np(dicbit+1) pbit
6442         -----------------------------------------------------------
6443         -lh4-        256  2^12     12       14 (or 13?)  4 (14<2^4)
6444         -lh5-        256  2^13     13       14           4 (14<2^4)
6445         -lh6-        256  2^15     15       16           5 (16<2^5)
6446         -lh7-        256  2^16     16       17           5 (17<2^5)
6447
6448 (13)
6449 また、t_len と同様に、p_len[i] >= 7 の形式では、1 が 12 bit より多く連
6450 続した場合に不正である。
6451
6452 (14)
6453 もちろん、p_len[] を読み込んだ後に構築した Huffman 木の整合性が保たれな
6454 ければならない点は他の Huffman 木と同じだ。
6455
6456 では、実際に復号処理のセキュリティ対応を行ったソースを基に実装を確認しよう。
6457
6458 \f
6459 以下は、
6460
6461     https://bugzilla.redhat.com/show_bug.cgi?id=204676
6462
6463 にて掲載されたパッチある。このパッチは gzip 用のパッチであったが内容は
6464 ほとんど同じである。(gzip は LHA とほとんど同じ復号処理のソースを含んで
6465 おり、LHA の圧縮形式を復号することができる。ただ、LHA ヘッダを読むこと
6466 はできないため lzh ファイルを展開できるわけではない。見たところ
6467 -lh4,lh5- にのみ対応している。)
6468
6469 diff -ru gzip-1.3.5.orig/unlzh.c gzip-1.3.5/unlzh.c
6470 --- gzip-1.3.5.orig/unlzh.c     1999-10-06 06:00:00.000000000 +0100
6471 +++ gzip-1.3.5/unlzh.c  2006-08-18 22:56:19.446997000 +0100
6472 @@ -149,13 +149,17 @@
6473      unsigned i, k, len, ch, jutbits, avail, nextcode, mask;
6474
6475      for (i = 1; i <= 16; i++) count[i] = 0;
6476 -    for (i = 0; i < (unsigned)nchar; i++) count[bitlen[i]]++;
6477 +    for (i = 0; i < (unsigned)nchar; i++) {
6478 +        if (bitlen[i] > 16)
6479 +        error("Bad table (case a)\n");
6480 +        else count[bitlen[i]]++;
6481 +    }
6482
6483 bitlen は、c_len, p_len, t_len であり、いずれの Huffman 木も最大の階層
6484 は 16 までであるからその範囲を超えたものがないかチェックしている。これ
6485 は、セキュリティバグの重要な改修点の1点目である。
6486
6487      start[1] = 0;
6488      for (i = 1; i <= 16; i++)
6489         start[i + 1] = start[i] + (count[i] << (16 - i));
6490 -    if ((start[17] & 0xffff) != 0)
6491 -       error("Bad table\n");
6492 +    if ((start[17] & 0xffff) != 0 || tablebits > 16) /* 16 for weight below */
6493 +       error("Bad table (case b)\n");
6494
6495      jutbits = 16 - tablebits;
6496      for (i = 1; i <= (unsigned)tablebits; i++) {
6497
6498 tablebits は、make_table() を呼び出すときに指定する引数で以下の通り固定
6499 値(8 or 12)である。従って必ずしも必要ではない。(あえてチェックするなら
6500 Bad table でなく Bug と表示するべきだろう)
6501
6502     make_table(nn, pt_len, 8, pt_table);
6503     make_table(NC, c_len, 12, c_table);
6504
6505 total & 0xffff はどの程度の不正テーブルを検出するのだろうか?
6506 該当の処理を以下に示そう。
6507
6508     for (i = 1; i <= 16; i++) count[i] = 0;
6509     for (i = 0; i < (unsigned)nchar; i++) count[bitlen[i]]++;
6510
6511     start[1] = 0;
6512     for (i = 1; i <= 16; i++)
6513         start[i + 1] = start[i] + (count[i] << (16 - i));
6514     if ((start[17] & 0xffff) != 0)
6515         error("Bad table\n");
6516
6517 これは、LHa for UNIX で以下のように書かれていた部分であり、ロジックとし
6518 てはまったく一緒である。
6519
6520     /* (A) */
6521     avail = nchar;
6522
6523     /* initialize */
6524     for (i = 1; i <= 16; i++) {
6525         count[i] = 0;
6526         weight[i] = 1 << (16 - i);
6527     }
6528
6529     /* (B) */
6530     /* count */
6531     for (i = 0; i < nchar; i++)
6532         count[bitlen[i]]++;
6533
6534     /* (C) */
6535     /* calculate first code */
6536     total = 0;
6537     for (i = 1; i <= 16; i++) {
6538         start[i] = total;
6539         total += weight[i] * count[i];
6540     }
6541     if ((total & 0xffff) != 0)
6542         error("make_table()", "Bad table (5)\n");
6543
6544 例えば、以下の Huffman 木では各階層の重み weight の値(gzip のソースで、
6545 (16 - i)の値)は
6546
6547        /\
6548       a /\              weight[1] = 0x8000
6549         b c             weight[2] = 0x4000
6550
6551 であり、葉の数に重みをかけた値の総和は
6552
6553 0x10000
6554
6555 となる。これは正しい Huffman 木について必ず成り立たなければならない必要
6556 十分条件である。
6557
6558 しかし、既存の処理では例えば 1 階層目の葉の数が 4 である場合に total が
6559 0x20000 となり 0xffff との論理積では異常を検知できない。
6560
6561 ここは、以下のようにするべきであろう(以下は、LHa for UNIXのソース)。
6562
6563     /* (C) */
6564     /* calculate first code */
6565     total = 0;
6566     for (i = 1; i <= 16; i++) {
6567         start[i] = total;
6568         total += weight[i] * count[i];
6569         if (total > 0x10000)
6570             error("make_table()", "Bad table\n");
6571     }
6572     if (total != 0x10000)
6573         error("make_table()", "Bad table (5)\n");
6574
6575 ループの中に total のチェックを入れているのは念のためである。もちろん、
6576 total の変数のサイズは 16 bit では足りないので 32 bit 整数にする必要が
6577 ある。(gzip のソースでは、total 変数の代わりに start[17] が使われている
6578 ので、LHa for UNIX のように total 変数にするか、start[] 自体を 32 bit
6579 整数に変える必要がある。)
6580
6581 なお、32 bit 整数にした場合でも、count[1] が 0x20000 の値以上であるとき
6582 total がオーバーフローする恐れがあるが以下の処理より count[] がnchar よ
6583 り大きくなることはない。
6584
6585     /* (B) */
6586     /* count */
6587     for (i = 0; i < nchar; i++)
6588         count[bitlen[i]]++;
6589
6590 そして、要素数が最も大きい c_len の場合で nchar は NC{510} であるから
6591 32 bit 整数の範囲をオーバーすることはないだろう。このことがはっきりして
6592 いればループ中に入れた
6593
6594         if (total > 0x10000)
6595             error("make_table()", "Bad table (5)\n");
6596
6597 は必ずしも必要ではない。あるいは、階層 n の葉の数が 2^n を越えないことの
6598 チェックに変えても良いだろう。
6599
6600     /* (C) */
6601     /* calculate first code */
6602     total = 0;
6603     for (i = 1; i <= 16; i++) {
6604         if (count[i] > (1<<i))
6605             error("make_table()", "Bad table\n");
6606         start[i] = total;
6607         total += weight[i] * count[i];
6608     }
6609     if (total != 0x10000)
6610         error("make_table()", "Bad table (5)\n");
6611
6612 これならば、total は最大でも 16 * 0x10000 = 0x100000 にしかならないこと
6613 が保証される。
6614
6615 @@ -169,15 +173,15 @@
6616
6617      i = start[tablebits + 1] >> jutbits;
6618      if (i != 0) {
6619 -       k = 1 << tablebits;
6620 -       while (i != k) table[i++] = 0;
6621 +       k = MIN(1 << tablebits, DIST_BUFSIZE);
6622 +       while (i < k) table[i++] = 0;
6623      }
6624
6625 tablebits は固定値なので、必ずしもこのチェックは必要ではない。
6626
6627      avail = nchar;
6628      mask = (unsigned) 1 << (15 - tablebits);
6629      for (ch = 0; ch < (unsigned)nchar; ch++) {
6630         if ((len = bitlen[ch]) == 0) continue;
6631 -       nextcode = start[len] + weight[len];
6632 +       nextcode = MIN(start[len] + weight[len], DIST_BUFSIZE);
6633         if (len <= (unsigned)tablebits) {
6634             for (i = start[len]; i < nextcode; i++) table[i] = ch;
6635         } else {
6636
6637 DIST_BUFSIZE は、c_table[] のバッファサイズで 1<<12 である。nextcode は、
6638 LHa for UNIX での該当変数は l で、Huffman 符号の(先頭 tablebits ビット
6639 の)取り得る最大値である。これは、理論上
6640
6641              tablebits  最大の Huffman 符号    m
6642     c_len    12         1111 1111 1111{4095}   4
6643     p_len     8              1111 1111{255}    8
6644     t_len     8              1111 1111{255}    8
6645
6646 であり、ループ脱出条件が不正でない限り、このチェックは不要であると思わ
6647 れる。(そもそも、念のためという意味でチェックしているのなら c_len の場
6648 合だけしか考慮してないのも変である)
6649
6650 ただし、先ほどの total チェックが不完全なままだと start[] の値が
6651 不正になりうるため、話が変わってくる。
6652 そういうわけで、これはセキュリティバグの重要な改修点の2点目となるが、
6653 本当は total のチェックの方が重要である。
6654
6655 さらに、木の形が正しくても
6656
6657   復号語の種類数 < 葉の数
6658
6659 である場合、葉に値が割り当てられない枝が発生してしまう。これもチェック
6660 しておいた方が良いだろう。復号語の数は nchar で葉の数は count[] の総和
6661 であるから
6662
6663     /* (B) */
6664     /* count */
6665     for (i = 0; i < nchar; i++)
6666         count[bitlen[i]]++;
6667
6668 より、このことは保証されているようだ(i >= nchar である bitlen[i] が設定
6669 されていても使われない。もちろん設定された時点で、エラーを検出する方が
6670 望ましいとは思う)
6671
6672 @@ -218,7 +222,7 @@
6673         for (i = 0; i < 256; i++) pt_table[i] = c;
6674      } else {
6675         i = 0;
6676 -       while (i < n) {
6677 +       while (i < MIN(n,NPT)) {
6678             c = bitbuf >> (BITBUFSIZ - 3);
6679             if (c == 7) {
6680                 mask = (unsigned) 1 << (BITBUFSIZ - 1 - 3);
6681
6682 n が t_len、p_len の領域の範囲内の値とは限らないのでそのチェックを行っ
6683 ている。これは、セキュリティバグの重要な改修点の3点目である。
6684
6685 気になるのは、
6686
6687 ・p_len, t_len の論理的な領域サイズでなく実装上の pt_len の領域サイズで
6688   チェックしている。そのため、バッファオーバーフローのセキュリティ対策
6689   としては十分だがエラーチェックとしては不完全である(エラーの検出が遅延
6690   される)。
6691
6692 ・不正な値をエラーとせず。処理を最大値で継続している。そのため、以下同文。
6693
6694 @@ -228,7 +232,7 @@
6695             pt_len[i++] = c;
6696             if (i == i_special) {
6697                 c = getbits(2);
6698 -               while (--c >= 0) pt_len[i++] = 0;
6699 +               while (--c >= 0 && i < NPT) pt_len[i++] = 0;
6700             }
6701         }
6702         while (i < nn) pt_len[i++] = 0;
6703
6704 i_special は 3 固定なので、このチェックは必ずしも必要というわけではない。
6705 (厳密には、実装上 i_special が -1 である場合があるが i が -1 になった時
6706 点でバグであり、その心配はなさそうだ)
6707
6708 @@ -248,7 +252,7 @@
6709         for (i = 0; i < 4096; i++) c_table[i] = c;
6710      } else {
6711         i = 0;
6712 -       while (i < n) {
6713 +       while (i < MIN(n,NC)) {
6714             c = pt_table[bitbuf >> (BITBUFSIZ - 8)];
6715             if (c >= NT) {
6716                 mask = (unsigned) 1 << (BITBUFSIZ - 1 - 8);
6717
6718 n が c_len の領域の範囲内の値とは限らないので、同上。
6719
6720 @@ -256,14 +260,14 @@
6721                     if (bitbuf & mask) c = right[c];
6722                     else               c = left [c];
6723                     mask >>= 1;
6724 -               } while (c >= NT);
6725 +               } while (c >= NT && (mask || c != left[c]));
6726             }
6727
6728 このループ部分の全体はパッチ前だと、
6729
6730             c = pt_table[bitbuf >> (BITBUFSIZ - 8)];
6731             if (c >= NT) {
6732                 mask = (unsigned) 1 << (BITBUFSIZ - 1 - 8);
6733                 do {
6734                     if (bitbuf & mask) c = right[c];
6735                     else               c = left [c];
6736                     mask >>= 1;
6737                 } while (c >= NT);
6738             }
6739             fillbuf((int) pt_len[c]);
6740
6741 となっており、
6742
6743   mask == 0 && c == left[c]
6744
6745 の条件を満たしてしまうような不正な木が構築されていると c の値は変化せず
6746 いつまでもループし続けることになってしまう。そこで、この条件が発生した
6747 ときにすぐにループを脱出するようその条件の否定である
6748
6749   !(mask == 0 && c == left[c])
6750
6751 つまりは、
6752
6753   (mask || c != left[c])
6754
6755 をループ継続の while 条件に加えているようだ。しかし、mask の初期値は
6756
6757   1 << (BITBUFSIZ{16} - 1 - 8) {0000 0000 1000 0000}
6758
6759 でありループ毎に
6760
6761   mask >>= 1;
6762
6763 しているのだから、mask == 0 になった時点で、8 bit(最初の表引きと合わせ
6764 て 16 bit)の Huffman 符号を読み込んでおり(くどいかも知れないが LHA にお
6765 ける Huffman 木の階層は最大 16)、mask == 0 を脱出条件に加えるだけで良い
6766 と思う。
6767
6768 もちろん、17 bit の符号を読み込んだ時点で不正なのだから
6769
6770                 mask = (unsigned) 1 << (BITBUFSIZ - 1 - 8);
6771                 do {
6772                     if (mask == 0) error("....");
6773
6774                     if (bitbuf & mask) c = right[c];
6775                     else               c = left [c];
6776                     mask >>= 1;
6777                 } while (c >= NT);
6778
6779 と異常終了しても問題ないと思う。以下のように for で書くこともできるがま
6780 あ結局は同じことだ。(こうすると見やすいだろうか?と考えてみただけである
6781 が、特に効果はないようだ。)
6782
6783                 for (mask = 1 <<(BITBUFSIZ-1-8); mask != 0; mask >>= 1) {
6784                     if (bitbuf & mask) c = right[c];
6785                     else               c = left[c];
6786
6787                     if (c < NT) break;
6788                 }
6789                 if (mask == 0) error(...);
6790
6791 しかし c の値と left[], right[] の領域サイズとの比較は良いのだろうか?
6792 (理論上は Huffman 木の構築時にチェックされていれば問題ないので、私は問
6793 題ないと思うのだが、それを言うとそもそも無限ループのチェックも不要であ
6794 ると思う。)
6795
6796             fillbuf((int) pt_len[c]);
6797             if (c <= 2) {
6798                 if      (c == 0) c = 1;
6799                 else if (c == 1) c = getbits(4) + 3;
6800                 else             c = getbits(CBIT) + 20;
6801 -               while (--c >= 0) c_len[i++] = 0;
6802 +               while (--c >= 0 && i < NC) c_len[i++] = 0;
6803             } else c_len[i++] = c - 2;
6804         }
6805         while (i < NC) c_len[i++] = 0;
6806
6807 c_len[] の範囲外の領域に値が設定されないようチェックしている。しかし、
6808 やはりエラーとして検出せずに処理を続行している。
6809
6810 @@ -292,7 +296,7 @@
6811             if (bitbuf & mask) j = right[j];
6812             else               j = left [j];
6813             mask >>= 1;
6814 -       } while (j >= NC);
6815 +       } while (j >= NC && (mask || j != left[j]));
6816      }
6817      fillbuf((int) c_len[j]);
6818      return j;
6819
6820 同上。
6821
6822 @@ -309,7 +313,7 @@
6823             if (bitbuf & mask) j = right[j];
6824             else               j = left [j];
6825             mask >>= 1;
6826 -       } while (j >= NP);
6827 +       } while (j >= NP && (mask || j != left[j]));
6828      }
6829      fillbuf((int) pt_len[j]);
6830      if (j != 0) j = ((unsigned) 1 << (j - 1)) + getbits((int) (j - 1));
6831
6832 同上。
6833
6834 @@ -356,7 +360,7 @@
6835      while (--j >= 0) {
6836         buffer[r] = buffer[i];
6837         i = (i + 1) & (DICSIZ - 1);
6838 -       if (++r == count) return r;
6839 +       if (++r >= count) return r;
6840      }
6841      for ( ; ; ) {
6842         c = decode_c();
6843 @@ -366,14 +370,14 @@
6844         }
6845         if (c <= UCHAR_MAX) {
6846             buffer[r] = c;
6847 -           if (++r == count) return r;
6848 +           if (++r >= count) return r;
6849         } else {
6850             j = c - (UCHAR_MAX + 1 - THRESHOLD);
6851             i = (r - decode_p() - 1) & (DICSIZ - 1);
6852             while (--j >= 0) {
6853                 buffer[r] = buffer[i];
6854                 i = (i + 1) & (DICSIZ - 1);
6855 -               if (++r == count) return r;
6856 +               if (++r >= count) return r;
6857             }
6858         }
6859      }
6860
6861 \f
6862 さて、これらセキュリティ対策は具体的にどのような場合を想定しているのだ
6863 ろう?
6864
6865 https://bugzilla.redhat.com/show_bug.cgi?id=204676
6866
6867 には、木の構築が不正になるシナリオとして以下が書かれている。
6868
6869 >  * Construct a pt_len[] such that pt_len[n] is 0.
6870 >  * Construct a pt_table[] such that pt_table[(code buffer) >> 16 - 8]
6871 is n (where n>2)
6872 >  * Now c_len[] is filled with (n-2), generating exceptionally high values in
6873 >    count[n-2].
6874
6875 しかし、これを読んでもよくわからなかったので一緒に掲載されていたサンプ
6876 ルの不正な符号を読んでみた。
6877
6878 > perl -e 'print "\x1f\xa0","\xab\xcd","\xf6\x40\x01\xc2\xcc\x36\x0c\x92\x00\x00\x00\x00","\xc8","\x00"x"2048"' | gzip -d
6879
6880 \x1f\xa0 はマジックナンバーで、gzip における LHA の圧縮形式を示す。そし
6881 て、LHA フォーマットは、\xab\xcd から始まる。これは、blocksize である。
6882
6883     blocksize = Oxabcd
6884
6885 そして、t_len の出力形式が続く。2 進数と合わせて下記に示す。
6886
6887     f6        40        01        c2        cc        36
6888     1111 0110 0100 0000 0000 0001 1100 0010 1100 1100 0011 0110
6889     <---->
6890     size of t_len
6891
6892     0c        92        00        00        00        00
6893     0000 1100 1001 0010 0000 0000 0000 0000 0000 0000 0000 0000,
6894
6895     c8         00
6896     1100 1000, 0000 * 2048
6897
6898 いきなり、t_len のサイズが不正(1111 0=0x1e(30) > NT{19})である。そして、各
6899 t_len[] を読み込むと以下のようになる
6900
6901     t_len[ 0] = 110   :6
6902     t_len[ 1] = 010   :2
6903     t_len[ 2] = 0 00  :0
6904                 00
6905     t_len[ 3] = 000   :0
6906     t_len[ 4] = 0 00  :0
6907     t_len[ 5] = 01 1  :3
6908     t_len[ 6] = 100   :4
6909     t_len[ 7] = 001   :1
6910     t_len[ 8] = 0 11  :3
6911     t_len[ 9] = 00 1  :1
6912     t_len[10] = 100   :4
6913     t_len[11] = 001   :1
6914     t_len[12] = 1 01  :5
6915     t_len[13] = 10 0  :4
6916     t_len[14] = 000   :0
6917     t_len[15] = 110   :6
6918     t_len[16] = 0 10  :2
6919     t_len[17] = 01 0  :2
6920     t_len[18] = 010   :2
6921     t_len[19] = 000   :0
6922     t_len[20] = 0 00  :0
6923     t_len[21] = 00 0  :0
6924     t_len[22] = 000   :0
6925     t_len[23] = 000   :0
6926     t_len[24] = 0 00  :0
6927     t_len[25] = 00 0  :0
6928     t_len[26] = 000   :0
6929     t_len[27] = 000   :0
6930     t_len[28] = 0 00  :0
6931     t_len[29] = 00, 1 :1
6932
6933 この t_len から Huffman 木の階層の葉の数は、以下の通りとなり
6934
6935     count[1] = 3
6936     count[2] = 4
6937     count[3] = 2
6938     count[4] = 3
6939     count[5] = 1
6940     count[6] = 2
6941
6942 以下のような木の形になるので、不正である(図中 X は、count の値が不正な
6943 ために余計に出来た葉を示す)。
6944
6945                      .
6946              /     /  \
6947             X     .    .          - 1 階層目
6948                  / \  / \
6949                 .   ..   .        - 2 階層目
6950                         / \
6951                        .   .
6952                        \  / \
6953                         X.   .
6954                             / \
6955                            .   .
6956                               / \
6957                              .   .  - 6 階層目
6958
6959 結果的に、c_len[] はというとこちらは gzip のソースを修正して実際に値を
6960 出力させてみたところ以下の通りすべての値が 5 になった。これはやはり不正
6961 である。(階層 5 の葉の数が 288 個ある)
6962
6963     size of c_len: 100 1000, 0 0x120(288)
6964
6965     c_len[0] = 5
6966     c_len[1] = 5
6967       :
6968     c_len[287] = 5
6969
6970
6971 ところで、本当はこうしたかったのではないだろうか?
6972
6973 perl -e 'print "\x1f\xa0","\xab\xcd","\x9e\x40\x01\xc2\xcc\x36\x0c\x92","\xc8","\x00"x"2048"' | gzip -d
6974
6975 これならば、t_len[] の領域サイズは超えないので、このチェックにはかから
6976 ない。
6977
6978     blocksize: 0xabcd
6979
6980     size of t_len: 0x13(19)
6981
6982     t_len[ 0]: 6
6983     t_len[ 1]: 2
6984     t_len[ 2]: 0
6985     t_len[ 3]: 0
6986     t_len[ 4]: 0
6987     t_len[ 5]: 3
6988     t_len[ 6]: 4
6989     t_len[ 7]: 1
6990     t_len[ 8]: 3
6991     t_len[ 9]: 1
6992     t_len[10]: 4
6993     t_len[11]: 1
6994     t_len[12]: 5
6995     t_len[13]: 4
6996     t_len[14]: 0
6997     t_len[15]: 6
6998     t_len[16]: 2
6999     t_len[17]: 2
7000     t_len[18]: 2
7001
7002 そして、先ほどと同じ count[] の結果となり不正な木ができる。
7003
7004     count[1] = 3
7005     count[2] = 4
7006     count[3] = 2
7007     count[4] = 3
7008     count[5] = 1
7009     count[6] = 2
7010
7011 結果、c_len[] が不正となる。
7012
7013     size of c_len: 0x190 (400)
7014
7015     c_len[0] = 5
7016     c_len[1] = 5
7017       :
7018
7019 結局、この問題は木の形の不正を検出できてないために発生している。調べて
7020 みたところ、この例では total が 0x30000 になるために、
7021
7022     (total & 0xffff) != 0
7023
7024 で検出できていないようである。従って、先に示したとおり total を 32 bit
7025 変数にして、この条件を
7026
7027     (total != 0x10000)
7028
7029 とすることで解決できるように思う。
7030
7031 # ちなみに、この条件は「クラフトの不等式」を整数に変形したものであるらしい。
7032 # 階層 16 以下のハフマン木であれば、
7033 #
7034 #   total == 2^16(0x10000)
7035 #
7036 # は必ず成り立ち、また、
7037 #
7038 #   total < 2^16
7039 #
7040 # であれば、この木は冗長な符号を割り当てていることを示すそうだ。そして、
7041 #
7042 #   total > 2^16
7043 #
7044 # は、一意な復号ができないことを示す。
7045
7046 念を押しておくが、セキュリティパッチが間違っているわけではない。セキュ
7047 リティパッチとしてはバッファオーバフローを防げば良いので単にそのための
7048 チェックを入れただけである。ただし、プログラマの立場としてはセキュリティ
7049 パッチは対症療法な修正でしかなく、根本的な問題点を解決していない場合が
7050 あるということを覚えておく必要がある。と思う。
7051
7052 \f
7053 このセキュリティバグの報告を受けて、gzip としてどのように修正しているか
7054 を確認してみた。以下は、問題が発見された gzip-1.3.5 とその修正が行われ
7055 た gzip-1.3.6 との差分(unlzh.c のみ)である。
7056
7057 --- gzip-1.3.5/unlzh.c  1999-10-06 14:00:00.000000000 +0900
7058 +++ gzip-1.3.6/unlzh.c  2006-11-20 17:40:34.000000000 +0900
7059 @@ -4,7 +4,7 @@
7060   */
7061  
7062  #ifdef RCSID
7063 -static char rcsid[] = "$Id: unlzh.c,v 1.2 1993/06/24 10:59:01 jloup Exp $";
7064 +static char rcsid[] = "$Id: unlzh.c,v 1.4 2006/11/20 08:40:34 eggert Exp $";
7065  #endif
7066  
7067  #include <config.h>
7068 @@ -69,11 +69,7 @@ local void make_table OF((int nchar, uch
7069  #define NT (CODE_BIT + 3)
7070  #define PBIT 4  /* smallest integer such that (1U << PBIT) > NP */
7071  #define TBIT 5  /* smallest integer such that (1U << TBIT) > NT */
7072 -#if NT > NP
7073 -# define NPT NT
7074 -#else
7075 -# define NPT NP
7076 -#endif
7077 +#define NPT (1 << TBIT)
7078
7079
7080 恐らく、NT を超える値が圧縮文に埋め込まれてもバッファオーバーフローしな
7081 いようにするために pt_len のバッファサイズを大きくしたのだろう(重要な改
7082 修点の3点目を別な方法で解決している。個人的にはこのような対処は好みで
7083 ない。意図がわかりにくいからだ)
7084
7085 c_len の領域サイズ(NC)はというと、gzip では元々 c_len のバッファサイズ
7086 は NC よりも大きい(8192 or 16384)。どうやら他の変数を使い回ししているた
7087 めにこのようになっているようだ。
7088
7089  /* local ush left[2 * NC - 1]; */
7090  /* local ush right[2 * NC - 1]; */
7091 @@ -155,7 +151,7 @@ local void make_table(nchar, bitlen, tab
7092      for (i = 1; i <= 16; i++)
7093         start[i + 1] = start[i] + (count[i] << (16 - i));
7094      if ((start[17] & 0xffff) != 0)
7095 -       error("Bad table\n");
7096 +      gzip_error ("Bad table\n");
7097
7098 ここの判定(木の構築が正しいかどうか)は変えなかったようだ。
7099
7100      jutbits = 16 - tablebits;
7101      for (i = 1; i <= (unsigned)tablebits; i++) {
7102 @@ -179,6 +175,8 @@ local void make_table(nchar, bitlen, tab
7103         if ((len = bitlen[ch]) == 0) continue;
7104         nextcode = start[len] + weight[len];
7105         if (len <= (unsigned)tablebits) {
7106 +           if ((unsigned) 1 << tablebits < nextcode)
7107 +             gzip_error ("Bad table\n");
7108             for (i = start[len]; i < nextcode; i++) table[i] = ch;
7109         } else {
7110             k = start[len];
7111
7112 代わりに、table[] の領域範囲を超える符号が現れた場合にエラーになるよう
7113 にしている(重要な改修点の2点目。ちゃんと、c_len だけでなく、pt_len の
7114 場合もチェックされることになる)。
7115
7116 @@ -223,6 +221,8 @@ local void read_pt_len(nn, nbit, i_speci
7117             if (c == 7) {
7118                 mask = (unsigned) 1 << (BITBUFSIZ - 1 - 3);
7119                 while (mask & bitbuf) {  mask >>= 1;  c++;  }
7120 +               if (16 < c)
7121 +                 gzip_error ("Bad table\n");
7122             }
7123             fillbuf((c < 7) ? 3 : c - 3);
7124             pt_len[i++] = c;
7125
7126 p_len, t_len について Huffman 符号長より大きい値が復号語となる場合にエ
7127 ラーとしている。(重要な改修点の1点目)
7128
7129 パッチでは、make_table() 内でチェックしていたところを実際に値を読み込む
7130 箇所に移したのだろう。そして、c_len の場合は、t_len の復号で木の構築
7131 チェックにてエラーが検出されなければ問題ないとみなしているのだろうと思
7132 う。
7133
7134 どうやら方針としては最低限のチェックで済ませているらしい。さすがにアル
7135 ゴリズムを熟知した上での修正のように見えるが、これで良いのかいまひとつ
7136 確信が持てない。まあ、gzip についてはこれ以上は触れないでおこう。
7137
7138 \f
7139 # Local Variables:
7140 # mode : indented-text
7141 # indent-tabs-mode: nil
7142 # End: