OSDN Git Service

global variable unpackable, compsize and origsize was removed
[lha/olha.git] / encode.txt
1 #-*- indent-tabs-mode: nil -*-
2
3 まず、グローバル変数の洗い出し
4
5 text
6 childcount
7 pos
8 matchpos
9 avail
10 position
11 parent
12 prev
13 next
14 remainder
15 matchlen
16 level
17
18 これらをすべて理解すれば、ソース解析は完了と思ってよい。
19 この段階では、変数名とソースの概観から木を使ってるっぽい程度に認識する。
20 アルゴリズムの予想がある程度立っている場合は、変数名からその用途を予想しても
21 良い。私はわからなかったのでとっとと次に進むことにする。
22
23 次に、処理をトップダウンで解析する。
24
25 encode(void)
26 {
27     int lastmatchlen;
28     node lastmatchpos;
29
30     (A)
31     allocate_memory();
32     (B)
33     init_slide();
34
35     (C)
36     huf_encode_start(opts.method);
37
38     (D)
39     remainder = fread_crc(&text[DICSIZ], DICSIZ + MAXMATCH, infile);
40
41     (E)
42     if (outfile != stdout && opts.quiet < 1) {
43         putc('.', stdout);
44     }
45
46     (F)
47     matchlen = 0;
48     pos = DICSIZ;
49     insert_node();
50
51     (G)
52     if (matchlen > remainder)
53         matchlen = remainder;
54
55     (H)
56     while (remainder > 0 && !unpackable) {
57         lastmatchlen = matchlen;
58         lastmatchpos = matchpos;
59         get_next_match();
60         if (matchlen > remainder)
61             matchlen = remainder;
62         if (matchlen > lastmatchlen || lastmatchlen < THRESHOLD)
63             output(text[pos - 1], 0);
64         else {
65             output(lastmatchlen + (UCHAR_MAX + 1 - THRESHOLD),
66                    (pos - lastmatchpos - 2) & (DICSIZ - 1));
67             while (--lastmatchlen > 0)
68                 get_next_match();
69             if (matchlen > remainder)
70                 matchlen = remainder;
71         }
72     }
73
74     (I)
75     huf_encode_end();
76 }
77
78 注目すべきところに適当に記号を割り振る。
79
80
81 (A) allocate_memory() を見ると以下のようなメモリ割り当てを行っている。
82 (B) の初期化に該当する部分もある程度(適当に)図示した。
83
84
85                              dicsiz                dicsiz*2
86                             /                     /
87      +---------------------+---------------------+----+
88 text |                     |                     |    |
89      +---------------------+---------------------+----+
90                                                  |----|
91                                                   maxmatch
92
93                             dicsiz(8192)
94                            /
95      +---------------------+---+
96 level|                     |111|
97      +---------------------+---+
98                            |--|
99                             UCHAR_MAX(255) + 1
100
101
102                             dicsiz
103                            '
104 position                   |
105      +---------------------+---+
106      |                     |NIL|
107      +---------------------+---+
108                            |---|
109                             UCHAR_MAX(8) + 1
110
111 parent
112      +---------------------+---------------------+
113      |                     |   NIL               |
114      +---------------------+---------------------+
115
116
117      +---------------------+---------------------+
118 prev |                     |                     |
119      +---------------------+---------------------+
120
121                              DICSIZ                DICSIZ*2             max_hash_val + 1
122                             /                     /                          /
123      +---------------------+---------------------+---------------------+----+
124 next | 234...           NIL|                     | NIL                 |    |
125      +---------------------+---------------------+---------------------+----+
126
127
128 なお、サイズは適当だ。ざっと上記のようにまとめて次に移る
129
130 (C) huf_encode_start() は、Huffman coding の初期化であることを
131 既にLHa for UNIXの解析で知っているので、次に進む。
132
133 (D) 圧縮対象のファイルを読み込んで text の右半分に書き込んでいる。
134
135 (E) 単に圧縮の進行状況を画面に出力する(ために、最初の "."を出力している)
136 だけ、無視する。
137
138 (F) matchlen, pos を初期化して insert_node() を呼んでいる。
139 insert_node() をざっと見ても内容はすぐにわからなさそうなので、
140 いったん無視して次に進む。
141
142 (G) if (matchlen > remainder) という判定を行っている。(F)の段階で、
143 matchlen = 0 の設定を行っているので、insert_node() にて、matchlen
144 が変更される場合があるのだろう程度に考える。おそらくは、insert_node()
145 実行後の例外条件に対応しているのではないかと予想しつつ次に進む。
146
147 (H) ちょっと長いが、LHa for UNIX の解析により以下の通り要約できるのでは
148 ないかと予想する。
149
150
151     /* ファイルの残りがある間ループ */
152     while (remainder > 0 && !unpackable) {
153
154         /* 前回のマッチを記録 */
155         lastmatchlen = matchlen;
156         lastmatchpos = matchpos;
157
158         /* 次のマッチを行う */
159         get_next_match();
160
161
162         /* 例外条件 */
163         if (matchlen > remainder)
164             matchlen = remainder;
165
166         /* 次のマッチした長さが前回よりも大きいか、前回のマッチが
167         THRESHOLDよりも小さい場合は、前回のマッチを出力する */
168         if (matchlen > lastmatchlen || lastmatchlen < THRESHOLD)
169             output(text[pos - 1], 0);
170         else {
171             /* 前回のマッチの方が最適であるなら、マッチ長と位置を出力する */
172             output(lastmatchlen + (UCHAR_MAX + 1 - THRESHOLD),
173                    (pos - lastmatchpos - 2) & (DICSIZ - 1));
174
175             /* 前回のマッチ長分のマッチを繰り返す。 */
176             while (--lastmatchlen > 0)
177                 get_next_match();
178
179             /* 例外条件 */
180             if (matchlen > remainder)
181                 matchlen = remainder;
182         }
183     }
184
185 おおよそ、LHa for UNIX のスライド辞書のアルゴリズムと同様の処理が
186 見える。
187
188 (I) huf_encode_end() にて、Huffman coding の後処理を行う。
189
190 以上から、スライド法の出力ループは(H)が、単語のマッチをinsert_node()
191 と get_next_match() が実行していることがわかる。
192 また、insert_node() が最初のループの直前だけしか呼ばれていないことから
193 insert_node() と get_next_match() の処理は似ているのではないかと
194 勘で予想する。
195
196 とにかく、insert_node() および get_next_match() の解析を進める
197 べきであることがわかる。
198
199 前の予想があっているかどうか、insert_node() および get_next_match()
200 を並べて比べてみる。
201
202 ・・・見たところ、insert_node() の処理は大きい、また get_next_match()
203 は予想以上に小さく、どうやら勘ははずれてこの二つの処理は違うことを行っ
204 ているらしい。
205
206 予想に反して get_next_match() の処理内容は短いので、こちらを先に
207 解析することにした。
208
209 static void
210 get_next_match(void)
211 {
212     int n;
213
214     (A)
215     remainder--;
216
217     (B)
218     if (++pos == DICSIZ * 2) {
219         memmove(&text[0], &text[DICSIZ], DICSIZ + MAXMATCH);
220         n = fread_crc(&text[DICSIZ + MAXMATCH], DICSIZ, infile);
221         remainder += n;
222         pos = DICSIZ;
223         if (outfile != stdout && opts.quiet < 1) {
224             putc('.', stdout);
225         }
226     }
227
228     (C)
229     delete_node();
230     (D)
231     insert_node();
232 }
233
234 (A) 残りサイズを縮めている。一文字読み進んでいるということだろう。
235
236 (B) pos を一つ進めている。remainder と対応しているのだろう。
237 なお、if 分の中身は例外条件(pos が端に到達した場合)であるので、
238 無視することにする。(ざっと見ればファイルを読み込んで各変数を
239 再度初期化しているだけであることはわかる)
240
241 (C), (D) ここがこの処理のメインである。delete_node() して
242 insert_node() している。
243 結局、マッチ処理は処理の冒頭にて最初に insert_node() していることから
244 このinsert_node()の部分ではないか?また、delete_node() は「次」を
245 行うのに必要な処理なのではないかとなんとなく考える。
246
247 ここで、insert_node() と delete_node() のいずれを読むべきか?
248 insert_node() は処理が複雑なので、delete_node() の方を読み進めることと
249 した。
250
251 static void
252 delete_node(void)
253 {
254 #if PERCOLATE
255     node q, r, s, t, u;
256 #else
257     node r, s, t, u;
258 #endif
259
260     if (parent[pos] == NIL)
261         return;
262     r = prev[pos];
263     s = next[pos];
264     next[r] = s;
265     prev[s] = r;
266     r = parent[pos];
267     parent[pos] = NIL;
268     if (r >= DICSIZ || --childcount[r] > 1)
269         return;
270 #if PERCOLATE
271     t = position[r] & ~PERC_FLAG;
272 #else
273     t = position[r];
274 #endif
275     if (t >= pos)
276         t -= DICSIZ;
277 #if PERCOLATE
278     s = t;
279     q = parent[r];
280     while ((u = position[q]) & PERC_FLAG) {
281         u &= ~PERC_FLAG;
282         if (u >= pos)
283             u -= DICSIZ;
284         if (u > s)
285             s = u;
286         position[q] = (s | DICSIZ);
287         q = parent[q];
288     }
289     if (q < DICSIZ) {
290         if (u >= pos)
291             u -= DICSIZ;
292         if (u > s)
293             s = u;
294         position[q] = s | DICSIZ | PERC_FLAG;
295     }
296 #endif
297     s = child(r, text[t + level[r]]);
298     t = prev[s];
299     u = next[s];
300     next[t] = u;
301     prev[u] = t;
302     t = prev[r];
303     next[t] = s;
304     prev[s] = t;
305     t = next[r];
306     prev[t] = s;
307     next[s] = t;
308     parent[s] = parent[r];
309     parent[r] = NIL;
310     next[r] = avail;
311     avail = r;
312 }
313
314
315 なかなか複雑である。が、このまま読み進める(insert_node()よりはマシだか
316 ら) ここまで、PERCOLATEの部分は無視してきたが、PERCOLATEは未定義である
317 として読み進めることにする。PERCOLATE の部分はアルゴリズムの改善を行っ
318 ている部分のように思えるので、より複雑である可能性が高いからまずは基本
319 部分の理解を優先するためである。
320
321
322 PERCOLATE を未定義であるとして、処理を再掲する。
323
324 static void
325 delete_node(void)
326 {
327     node r, s, t, u;
328
329     (A)
330     if (parent[pos] == NIL)
331         return;
332     (B)
333     r = prev[pos];
334     s = next[pos];
335     next[r] = s;
336     prev[s] = r;
337
338     (C)
339     r = parent[pos];
340     parent[pos] = NIL;
341
342     (D)
343     if (r >= DICSIZ || --childcount[r] > 1)
344         return;
345
346     (E)
347     t = position[r];
348
349     (F)
350     if (t >= pos)
351         t -= DICSIZ;
352
353     (G)
354     s = child(r, text[t + level[r]]);
355
356     (H)
357     t = prev[s];
358     u = next[s];
359     next[t] = u;
360     prev[u] = t;
361
362     (I)
363     t = prev[r];
364     next[t] = s;
365     prev[s] = t;
366
367     (J)
368     t = next[r];
369     prev[t] = s;
370     next[s] = t;
371
372     (K)
373     parent[s] = parent[r];
374     parent[r] = NIL;
375     next[r] = avail;
376     avail = r;
377 }
378
379 これだけ削るだけでも、なんとか解析できそうな気がしてくる。モチベーショ
380 ンを上げることは重要である。
381
382 まずは、(A)
383
384     if (parent[pos] == NIL)
385         return;
386
387 例外条件なので、無視する。ただし、変数pos(これは現在読んでいる位置であ
388 るようだ)を使用している。これはこの関数の入力であるらしいことは意識する。
389 (入力と出力が何であるかは常に意識すべき)
390
391 (B)
392
393     r = prev[pos];
394     s = next[pos];
395     next[r] = s;
396     prev[s] = r;
397
398 これはつまり
399
400     next[r] = next[pos];
401     prev[s] = prev[pos];
402
403 である。ただし、r と s はそれぞれ右辺値であることから
404
405     next[prev[pos]] = next[pos];
406     prev[next[pos]] = prev[pos];
407
408 である。なんとなくわかりそうな気がする。日本語に訳すと
409     pos の prev の next は、pos の next である。
410     pos の next の prev は、pos の prev である。
411 ということを書いている。図示してみる。
412
413
414                prev<--+
415                 ^     next
416                 |     |
417                pos    |
418                 |     |
419                 |     |
420                 v     prev
421               next<---+
422
423 つまりは、双方向リンクリストで pos を中心に環状にリンクを作っている。
424 なんとなくわかったので、次に進む
425
426     (C)
427     r = parent[pos];
428     parent[pos] = NIL;
429
430 pos の親を取得し、pos の親を NIL で上書きしている。
431 pos の親を「取り出している」と読めばよいのか?
432
433 (D)
434
435     if (r >= DICSIZ || --childcount[r] > 1)
436         return;
437
438 例外条件であろう。無視する。
439
440 (E)
441
442     t = position[r];
443
444 r つまり、pos の親の position を取得している。変数名から pos の親のマッ
445 チ位置ではなかろうか?
446
447 (F)
448
449     if (t >= pos)
450         t -= DICSIZ;
451
452 例外条件であろうか?t つまり、posの親の position が pos より大きい
453 場合これは text バッファにおいて、posより右側の位置を t が指している
454 ことを示しているのだろう。このような場合があるのかどうかはわからないが
455 やはり例外条件である。無視する。
456
457 (G)
458
459     s = child(r, text[t + level[r]]);
460
461 r つまり、pos の親と text[t + level[r]] つまり、pos の親の位置 t + level[r]
462 を引数に child() 関数を呼んでいる。わかりそうなわからなさそうな・・・
463 次に進む
464
465 (H)
466
467     t = prev[s];
468     u = next[s];
469     next[t] = u;
470     prev[u] = t;
471
472 これは前に出てきたのと同じである。つまり、
473
474     next[prev[s]] = next[s];
475     prev[next[s]] = prev[s];
476
477 で、child()の戻り値についてリンクトリストを構築している。
478
479 (I)
480     t = prev[r];
481     next[t] = s;
482     prev[s] = t;
483
484 r つまり、pos の親の prev について
485
486     next[prev[parent[pos]]] = child()
487     prev[child()] = prev[parent[pos]]
488
489 としている。親の前の次と子を、子の前を親の前と繋げている。
490
491 (J) (I) と同様である。
492     t = next[r];
493     prev[t] = s;
494     next[s] = t;
495
496 (K)
497     parent[s] = parent[r];
498     parent[r] = NIL;
499     next[r] = avail;
500     avail = r;
501
502 parent[r] つまり、posの親の親とparent[child()]
503 を繋げている。
504
505 なんだかわかりそうなわからなさそうな・・・まだ情報が足りない。
506
507 キモとしては、next[], prev[], parent[] を理解すれば、データ構造が見えて
508 くるであろうことから、この辺りの操作をしている小さめの関数を探してみる
509 ことにする。
510
511 ざっと見て、child(), makechild(), split() が見つかる。これらは
512 ほとんど、insert_node() で呼ばれている。
513
514 get_next_match()
515   delete_node()
516     child()
517   insert_node()
518     child()
519     split()
520       makechild()
521     makechild()
522
523 といったコールグラフとなっている。insert_node()が複雑であることから
524 やはり、これらの小さな関数群をまず調べるべきであろう。
525
526 ボトムアップ的にまずは、makechild() から
527
528 static void
529 makechild(node q, uchar c, node r)
530         /* Let r be q's child for character c. */
531 {
532     node h, t;
533
534     h = HASH(q, c);
535     t = next[h];
536     next[h] = r;
537     next[r] = t;
538     prev[t] = r;
539     prev[r] = h;
540     parent[r] = q;
541     childcount[q]++;
542 }
543
544 コメントから q の子を r につなげる処理らしい。
545
546 まず、q と文字 c からハッシュ値を求める。
547
548     h = HASH(q, c);
549
550 そのハッシュ値の next を取得する
551
552     t = next[h];
553
554 そして以下でリストに挿入している
555
556     next[h] = r;
557     next[r] = t;
558
559 つまり、
560
561     h.next --> t
562
563 だったところに
564
565     h.next --> r
566                r.next --> t
567 と r を挿入している。
568
569
570 prev も同様である。
571
572     prev[t] = r;
573     prev[r] = h;
574
575 以下のようなイメージとなる。
576
577     h <--- r.prev
578            r <----- t.prev
579
580
581 r がちゃんと挿入されていることがわかる。
582 さらに、
583
584     parent[r] = q;
585
586 r の parent として、q を設定している。
587 next[], prev[] が q と c のHASH値をさしているのに対し、
588 parent[] は、q そのものを指しているらしい。
589
590     childcount[q]++;
591
592 q の子を繋げたので、q の子の数を足しているらしい。
593
594 次に child() を見てみる
595
596 static node
597 child(node q, uchar c)
598         /* q's child for character c (NIL if not found) */
599 {
600     node r;
601
602     r = next[HASH(q, c)];
603     parent[NIL] = q;            /* sentinel */
604     while (parent[r] != q)
605         r = next[r];
606     return r;
607 }
608
609 q の子を返すらしい。
610
611 親は、直接 parent[] で取得できるが、子を取得するには、
612 リンクをたどる必要があることを示すようだ。
613
614 q の next を取得し、parent[] が q である r を探している。見つからなけれ
615 ば sentinel として設定してある NIL を返すようだ。
616
617 ここまでで言えることは、
618
619 ・q と c のHASH値により、next[], prev[] が指す先は q の子の候補である。
620
621 というところか。
622
623 では、split()を見てみる
624
625 void
626 split(node old)
627 {
628     node new, t;
629
630     new = avail;
631     avail = next[new];
632     childcount[new] = 0;
633
634     t = prev[old];
635     prev[new] = t;
636
637     next[t] = new;
638     t = next[old];
639
640     next[new] = t;
641     prev[t] = new;
642
643     parent[new] = parent[old];
644     level[new] = matchlen;
645     position[new] = pos;
646     makechild(new, text[matchpos + matchlen], old);
647     makechild(new, text[pos + matchlen], pos);
648 }
649
650 ここで、ようやく元々の処理目的であるマッチ位置、マッチ長を指す変数が
651 現れている。
652
653 ・new に avail を退避し
654 ・avail を新たに next[new] に設定
655 ・new の childcount は 0
656
657   どうやら avail は空きの位置を示しているらしく、新たな node の割り当て
658   は、
659      new = avail;
660   で行い、
661      avail = next[new]
662   で次の空きをavailにセットしているようだ。avail, next[avail], next[next[avail]] は
663   空き領域のリストとなるらしい。そういえば、初期化の処理で
664
665         avail = 1;
666         for (i = 1; i < DICSIZ - 1; i++)
667             next[i] = i + 1;
668         next[DICSIZ - 1] = NIL;
669
670   というのがあったが、next の領域(下図)のうち、左、1/3 は、この空きリス
671   トをさすらしい(そして、そのリンクは、next[DICSIZ-1]でNILとなる。
672   なお、next[0] は使われておらず、next[1] -> 2 から始まるらしい。
673
674 -------------------------------------------------------------------------------------------
675
676                              DICSIZ                DICSIZ*2             max_hash_val + 1
677                             /                     /                          /
678      +---------------------+---------------------+---------------------+----+
679 next | 234...           NIL|                     | NIL                 |    |
680      +---------------------+---------------------+---------------------+----+
681
682 -------------------------------------------------------------------------------------------
683
684 ・old の prev は、new の prev に設定(あわせて next も再設定)
685 ・new の parent を old の parent に
686
687 これで、old は切り離された、
688
689 ・newの level に matchlen を設定
690 ・newの position に pos を設定
691 ・new の子として old および pos を設定
692
693 注目すべきは、最後の makechild() 2連発である。
694 最初の makechild() により、matchpos + matchlen の文字を old として、
695 子に連結し、次の makechild() により、新たな pos + matchlen を
696 pos として、子に連結している。
697
698 ここで、matchpos, matchlen は前回のマッチを、pos, matchlen は
699 今回のマッチを指し、new の子の浅い方がより最近のマッチを指すことを
700 示すようだ。
701
702 ここまでで、どうやらおおよその構造の使い方はつかめて来たが、まだまだよ
703 くわからない。結局、 insert_node() を読み解くまでは全貌はつかめないよう
704 だ。
705
706 insert_node() を読み進めることにする。例によって、PERCOLATE は未定義として
707 見ることにする。
708
709 static void
710 insert_node(void)
711 {
712     node q, r, j, t;
713     uchar c, *t1, *t2;
714
715     (A)
716
717     if (matchlen >= 4) {
718         matchlen--;
719         r = (matchpos + 1) | DICSIZ;
720         while ((q = parent[r]) == NIL)
721             r = next[r];
722         while (level[q] >= matchlen) {
723             r = q;
724             q = parent[q];
725         }
726
727         t = q;
728         while (t < DICSIZ) {
729             position[t] = pos;
730             t = parent[t];
731         }
732
733     }
734     (B)
735     else {
736         q = text[pos] + DICSIZ;
737         c = text[pos + 1];
738         if ((r = child(q, c)) == NIL) {
739             makechild(q, c, pos);
740             matchlen = 1;
741             return;
742         }
743         matchlen = 2;
744     }
745
746     for (;;) {
747     (C)
748         if (r >= DICSIZ) {
749             j = MAXMATCH;
750             matchpos = r;
751         }
752     (D)
753         else {
754             j = level[r];
755             matchpos = position[r] & ~PERC_FLAG;
756         }
757     (E)
758         if (matchpos >= pos)
759             matchpos -= DICSIZ;
760     (F)
761         t1 = &text[pos + matchlen];
762         t2 = &text[matchpos + matchlen];
763     (G)
764         while (matchlen < j) {
765             if (*t1 != *t2) {
766                 split(r);
767                 return;
768             }
769             matchlen++;
770             t1++;
771             t2++;
772         }
773     (H)
774         if (matchlen >= MAXMATCH)
775             break;
776     (I)
777         position[r] = pos;
778     (J)
779         q = r;
780         if ((r = child(q, *t1)) == NIL) {
781             makechild(q, *t1, pos);
782             return;
783         }
784     (K)
785         matchlen++;
786     }
787     (L)
788     t = prev[r];
789     prev[pos] = t;
790     next[t] = pos;
791     (M)
792     t = next[r];
793     next[pos] = t;
794     prev[t] = pos;
795
796     (N)
797     parent[pos] = q;
798     parent[r] = NIL;
799     next[r] = pos;              /* special use of next[] */
800 }
801
802
803 最初に(A) matchlen >= 4 の場合を扱っている一旦無視することにする。
804
805 (B) matchlen < 4 の場合である。
806
807         q = text[pos] + DICSIZ;
808         c = text[pos + 1];
809
810 ここで、pos の位置の文字と pos + 1 の位置の文字を取得している。
811 pos には DICSIZ を足しているがそのことは一旦無視する。
812
813         if ((r = child(q, c)) == NIL) {
814             makechild(q, c, pos);
815             matchlen = 1;
816             return;
817         }
818
819 1文字目の q と 2文字目の c を使って child() を呼び出している。つまり、
820 q の次に c が続く文字を木から探しているようだ。見つからない場合(NIL)は、
821 makechild() により、この2文字を木に登録し、matchlen = 1 としてこの関数
822 を抜けている。matchlen は実際のマッチ長が 1 の場合は、見つからなかった
823 場合を指しているらしい。
824
825         matchlen = 2;
826
827 見つかった場合は、最低 2 文字はマッチしているということから
828 matchlen を 2 に設定して次の処理に進む。
829
830     for (;;) {
831     (C)
832         if (r >= DICSIZ) {
833             j = MAXMATCH;
834             matchpos = r;
835         }
836
837 r つまり、q の子が DICSIZ以上であれば、j を MAXMATCHに matchpos を r に
838 設定している。child() の戻り値 r は pos を示すようだ。
839
840     (D)
841         else {
842             j = level[r];
843             matchpos = position[r] & ~PERC_FLAG;
844         }
845
846 DICSIZ より小さい場合は、j を level[r] に設定し、
847 matchpos を position[r] の位置にしている。position[r] も位置を示すらしい。
848
849     (E)
850         if (matchpos >= pos)
851             matchpos -= DICSIZ;
852
853 matchpos が pos 以上を指す場合は、matchpos から DICSIZ を引いている。
854 スライド前の位置を差す場合を想定しているのか?
855
856     (F)
857         t1 = &text[pos + matchlen];
858         t2 = &text[matchpos + matchlen];
859
860 t1 が現在の文字(の末尾)、t2がマッチ文字(の末尾)を指す。
861
862     (G)
863         while (matchlen < j) {
864             if (*t1 != *t2) {
865                 split(r);
866                 return;
867             }
868             matchlen++;
869             t1++;
870             t2++;
871         }
872
873 matchlen が j (最大マッチ長)となるまで、*t1 と *t2 を比較し、
874 一致する間先に進めている(matchlen++)そして、一致しない部分を
875 見つけたときに、split(r) により、木を分割している。
876 以前、split(r) を見たときに詳細は不明だが、新しいマッチを
877 木に登録する処理のように見えた。なんとなく理解できる。
878
879     (H)
880         if (matchlen >= MAXMATCH)
881             break;
882
883 matchlen == j となったとき、matchlen が MAXMATCH より大きければ
884 break して for() ループを抜ける。
885
886     (I)
887         position[r] = pos;
888
889 matchlen が MAXMATCH に達しない間は、新しい pos を position[r] に
890 登録する。
891
892     (J)
893         q = r;
894         if ((r = child(q, *t1)) == NIL) {
895             makechild(q, *t1, pos);
896             return;
897         }
898
899 q を r にし、q と t1 について木を探索する
900 見つからなければ、新たな q, t1, pos について makechild() して
901 木に登録して、終了。
902
903     (K)
904         matchlen++;
905     }
906
907 みつかるようなら matchlen を伸ばして、より長いマッチがあるか探索する。
908
909     (L)
910     t = prev[r];
911     prev[pos] = t;
912     next[t] = pos;
913     (M)
914     t = next[r];
915     next[pos] = t;
916     prev[t] = pos;
917
918 (L) 以降は for() を抜けた後の処理である。つまり、matchlen が MAXMATCH
919 以上となった場合。このとき、木を更新している。
920 r を取り除き、pos とすり替えている。
921
922     (N)
923     parent[pos] = q;
924     parent[r] = NIL;
925     next[r] = pos;              /* special use of next[] */
926
927 pos の親を q とし、r の親をNILに、r の next は pos としているが、
928 先ほど木から切り離していることおよび、コメントから木とは関係ない
929 用途に利用しているらしい。
930
931 nextやprev、parentに対して似たような処理が何度か現れていた
932 これを関数に切り出せるか考えてみる
933
934 。。。。
935
936 ここまで読み進めてもはっきり言ってよくわからない
937 なぜかというとデータ構造がまだよくわからないからである
938
939 そこで今度は各変数を中心に処理を追うことにする
940
941 なお現時点でわかった情報を変数の一覧に書き込んでおく
942
943 (1) 以下は本処理の入出力となる
944 text・・・読み込んだテキストのバッファ兼辞書 
945 remainder・・・読み込んでいるデータのバッファ上の残りサイズ
946 pos・・・現在読み込んでいる位置
947 matchpos・・・pos位置の文字とマッチした辞書上の位置
948 matchlen・・・pos位置の文字とマッチした辞書上のマッチ長
949
950 (2) 以下により木を現している。ざっと処理をみてトライ木なのではないかと予想
951 parent
952 prev
953 next
954
955 (3) 以下は上の木に格納されている補足情報らしい
956 level・・・マッチ長
957 position・・・文字に対する位置
958
959 (4) 以下は木に関するその他の情報
960 childcount・・・子の個数
961 avail・・・nextの空きを指すリストのトップ
962
963 ここで、不明確なのは(2)なので、この構造について追求してみる。
964
965 まず、型 next, prev, parent (ついでに、position) はいずれも node 型(の配列)である。
966 その添え字はというと、next を中心に利用している箇所を上から順に探してみると
967
968 child(node q, uchar c)
969         /* q's child for character c (NIL if not found) */
970 {
971     node r;
972
973     r = next[HASH(q, c)];
974     parent[NIL] = q;            /* sentinel */
975     while (parent[r] != q)
976         r = next[r];
977     return r;
978 }
979
980 next の添え字は、HASH(q, c) である。その後の
981 parent[r], next[r] の記述を見るとどうやら添え字も node である。
982
983 node とは何を表すのかが重要である。
984
985 insert_node() の冒頭
986
987         q = text[pos] + DICSIZ;
988         c = text[pos + 1];
989         if ((r = child(q, c)) == NIL) {
990             makechild(q, c, pos);
991             match->len = 1;
992             return;
993         }
994         match->len = 2;
995
996 child() の第一引数は、pos 位置の文字 text[pos] に DICSIZ を足している。
997 第二引数は、pos の次の位置の文字である。
998
999 child() は、next[HASH(第一引数, 第二引数)] を実行するのであった。
1000 ここで、HASH関数がどうなっているか見てみると
1001
1002 #define HASH(p, c) ((p) + ((c) << (DICBIT - 9)) + DICSIZ * 2)
1003
1004 つまり、
1005     p
1006   + ((c) << (DICBIT - 9))
1007   + DICSIZ * 2
1008
1009 である。HASH()の値はnext[]の添え字であり、常に DICSIZ * 2 以上である(c
1010 はunsigned charなので、<< により、負になることはない)。
1011 したがって、下図 *1 の部分の範囲がHASH()の値である。
1012
1013 さらに言うと、HASH(p, c) の p は、text[pos] + DICSIZ なので、
1014 (この部分に関して言えば)、*2 の部分を返すことになる。
1015
1016 -------------------------------------------------------------------------------------------
1017
1018                              DICSIZ                DICSIZ*2             max_hash_val + 1
1019                             /                     /                          /
1020      +---------------------+---------------------+---------------------+----+
1021 next | 234...           NIL|                     | NIL                 |    |
1022      +---------------------+---------------------+---------------------+----+
1023
1024                                                  |----------- *1 -----------|
1025                                                                        |-*2-|
1026
1027 -------------------------------------------------------------------------------------------
1028
1029 ざっと、HASH() を使用している箇所を探してみると、他に makechild() がある
1030 これも、makechild()の第一、第二引数がそのままHASH()の引数となる。
1031 では、child(), makechild() を使用している箇所を探すと
1032
1033 split() で、使用する new (avail で空きリストが割り当てられる箇所)がmakechild()
1034 の第一引数となる場合がある、*1 の範囲がHASH()の戻りのようだ。
1035
1036 HASH() の ((c) << (DICBIT - 9)) の部分を考えてみる c は、text[] の値
1037 (つまり、文字)を示すので、c の値は、0 〜 255 である。そうすると
1038 DICBIT は、とりあえず13(-lh5)であるからビット列であらわすと
1039
1040
1041             15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
1042            +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1043            | 0| 0| 0| 0| 1| 1| 1| 1| 1| 1| 1| 1| 0| 0| 0| 0|
1044            +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1045                                                |-----------|
1046                                                 (DICBIT-9)
1047
1048 と11ビット目から4ビット目までを使用した値の範囲となる。
1049 ここでは、node 型(実体はunsigned short)の範囲を想定している。
1050 なお、short が16bitであることも前提となっているように思う。
1051
1052 なお、2 * DICSIZ は、(DICSIZ は、1 << DICBITであるので) 以下である。
1053
1054             15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
1055            +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1056            | 0| 1| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0|
1057            +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1058
1059 p はというと、今のところ 1 から 255 + DICSIZ の範囲なので、
1060
1061             15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
1062            +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1063            | 0| 0| 1| 0| 0| 0| 0| 0| 1| 1| 1| 1| 1| 1| 1| 1|
1064            +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1065
1066 これらを足すということは、
1067
1068             15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
1069            +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1070 p          | 0| 0| 1| 0| 0| 0| 0| 0| 1| 1| 1| 1| 1| 1| 1| 1|
1071 c<<4       | 0| 0| 0| 0| 1| 1| 1| 1| 1| 1| 1| 1| 0| 0| 0| 0|
1072 2*DICSIZ   | 0| 1| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0|
1073            +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1074 max          0  1  1  1  0  0  0  0  1  1  1  0  1  1  1  1
1075
1076 最大でも、max の値にしかならない、これは、next の領域サイズに使われている
1077
1078 #define MAX_HASH_VAL (3 * DICSIZ + (DICSIZ / 512 + 1) * UCHAR_MAX)
1079
1080 と照らし合わせると
1081
1082
1083 MAX_HASH_VAL
1084             15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
1085            +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1086            | 0| 1| 1| 1| 0| 0| 0| 0| 1| 1| 1| 0| 1| 1| 1| 1|
1087            +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1088               `-----'  `-----------------------------------'
1089                 /                       \
1090            3 * DICSIZ                    (DICSIZ / 512 + 1) * UCHAR_MAX{255}
1091
1092 DICSIZ は、2^13、512 は、2^9 であるから、DICSIZ / 512 は、2^4 である)
1093
1094 見事一致している。なにか納得いかない同じ値を示すのに、どうして違う計算式を
1095 使うのだろう?もう一度、HASH()とMAX_HASH_VALを列挙してみよう。
1096
1097 #define HASH(p, c) ((p) + ((c) << (DICBIT - 9)) + DICSIZ * 2)
1098 #define MAX_HASH_VAL (3 * DICSIZ + (DICSIZ / 512 + 1) * UCHAR_MAX)
1099
1100 HASH() の式の最大値を考えると
1101 p = DICSIZ + UCHAR_MAX
1102 c = UCHAR_MAX
1103 であるから
1104
1105 #define HASH(p, c) ((DICSIZ+UCHAR_MAX) + ((UCHAR_MAX) << (DICBIT - 9)) + DICSIZ * 2)
1106
1107 DICSIZを切り出すと
1108
1109 #define HASH(p, c) (3 * DICSIZ + UCHAR_MAX + ((UCHAR_MAX) << (DICBIT - 9)))
1110
1111 << をべき乗(^ はここでは、べき乗)であらわすと(x << n)は、x * 2^(n)であるから
1112
1113 #define HASH(p, c) (3 * DICSIZ + UCHAR_MAX + ((UCHAR_MAX)* 2^(DICBIT - 9)))
1114
1115 UCHAR_MAX を切り出すと
1116
1117 #define HASH(p, c) (3 * DICSIZ + UCHAR_MAX * (1 + 2^(DICBIT - 9)))
1118
1119 x^(a-b) は、x^a / x^b であるから
1120
1121 #define HASH(p, c) (3 * DICSIZ + UCHAR_MAX * (1 + 2^DICBIT / 2^9))
1122
1123 これで、MAX_HASH_VALと同じになった。やはり、MAX_HASH_VALは基のHASH()関数の計算式を保存して、
1124
1125 #define MAX_HASH_VAL ((DICSIZ+UCHAR_MAX) + ((UCHAR_MAX) << (DICBIT - 9)) + DICSIZ * 2)
1126
1127 とするか、むしろ
1128
1129 #define MAX_HASH_VAL HASH(DICSIZ + UCHAR_MAX, UCHAR_MAX)
1130
1131 とした方がわかりやすい。どうせ定数の計算式だからコンパイラが計算してくれるのである。
1132
1133 横道にそれた、HASH()関数についてもう一度再考する。
1134
1135
1136             15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
1137            +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1138 p          | 0| 0| 1| 0| 0| 0| 0| 0| 1| 1| 1| 1| 1| 1| 1| 1|
1139 c<<4       | 0| 0| 0| 0| 1| 1| 1| 1| 1| 1| 1| 1| 0| 0| 0| 0|
1140 2*DICSIZ   | 0| 1| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0|
1141            +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1142
1143 2*DICSIZ は、値の範囲をnext[]の領域下図(再掲)の*1の部分に押し上げるためのものである。
1144
1145 -------------------------------------------------------------------------------------------
1146
1147                              DICSIZ                DICSIZ*2             max_hash_val + 1
1148                             /                     /                          /
1149      +---------------------+---------------------+---------------------+----+
1150 next | 234...           NIL|                     | NIL                 |    |
1151      +---------------------+---------------------+---------------------+----+
1152
1153                                                  |----------- *1 -----------|
1154                                                                        |-*2-|
1155
1156 -------------------------------------------------------------------------------------------
1157
1158 では、2*DICSIZ を除いて、13ビット目をx その他をyであらわすと
1159
1160             15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
1161            +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1162 p          | 0| 0| x| 0| 0| 0| 0| 0| y| y| y| y| y| y| y| y|
1163 c<<4       | 0| 0| 0| 0| y| y| y| y| y| y| y| y| 0| 0| 0| 0|
1164            +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1165                    x  y  y  y  y  y  y  y  y  y  y  y  y  y
1166
1167 13ビット目が立っている場合は、*2 の範囲を表しており、*2 の範囲はほぼDICSIZと同じであることが
1168 わかる。(正確にはMAX_HASH_VAL+1であるから、yyyyyyyyyyyyy は、1000011110000 半分よりちょっと
1169 多いサイズである。
1170
1171 そして、13ビット目が立っていない場合は、*1 の領域は、やはりyyyyyyyyyyyyyであるつまり、
1172 DICSIZの領域をすべて使っているわけではないのである図を少し訂正しよう。
1173
1174 -------------------------------------------------------------------------------------------
1175
1176                              DICSIZ                DICSIZ*2              DICSIZ*3
1177                             /                     /                     /         max_hash_val + 1
1178      +---------------------+---------------------+---------------------+----------+
1179 next | 234...           NIL|                     |       NIL           |    NIL   |
1180      +---------------------+---------------------+---------------------+----------+
1181
1182                                                  |--- *1 ---|          |--- *2 ---|
1183
1184
1185 -------------------------------------------------------------------------------------------
1186
1187 *1 と *2 は同じである。max_hash_val + 1 - DICSIZ*3 は、*2 + 1 なので、
1188 + 1 の分だけ、図の通りではない。
1189
1190 *1 と *2 は、DICSIZ/2 より少し大きい
1191
1192 もう少し、HASH()について考える。13 bit目をとりあえず無視して
1193 p ・・・text[]の1文字目
1194 c ・・・text[]の2文字目
1195 であるが、c<<4 としている理由は、
1196
1197
1198             15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
1199            +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1200 p          | 0| 0| x| 0| 0| 0| 0| 0| y| y| y| y| y| y| y| y|
1201 c<<4       | 0| 0| 0| 0| y| y| y| y| y| y| y| y| 0| 0| 0| 0|
1202            +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1203                    x  y  y  y  y  y  y  y  y  y  y  y  y  y
1204
1205
1206 p, c の2文字(2*8bit)の情報を、12bitに写像(桁上がりも考慮)しており、p の
1207 上位4bitとcの下位4bit を使っている。これを行う理由ははっきりとはまだわ
1208 からないが、HASH()が指す node は、引数の組み合わせにより同じ値を持つ場
1209 合があることがわかる。
1210
1211 c<<4 はc<<(DICBIT-9)であるが、DICBITが大きくなるとbitをずらす範囲が広がる。
1212 c<<(DICBIT-9) は、2<<DICBIT つまり13bit目を上書きしない程度に2文字目の情報を
1213 ずらしているためであるから、DICBIT-9の意味は、DICBIT-8-1(8 は1文字のbit数で、
1214 DICBITから8bit+桁上がりの1bit引いて、何bit分左シフト可能かを表しているようだ。
1215
1216 必然的に、辞書サイズがたとえば、DICBIT{16} の場合(これは-lh7- の場合)、
1217
1218 DICSIZ は、2^16 であり、
1219
1220
1221             18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
1222            +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1223 p          | 0| 0| x| 0| 0| 0| 0| 0| 0| 0| 0| y| y| y| y| y| y| y| y|
1224 c<<7       | 0| 0| 0| 0| y| y| y| y| y| y| y| y| 0| 0| 0| 0| 0| 0| 0|
1225            +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1226                    x  y  y  y  y  y  y  y  y  y  y  y  y  y  y  y  y
1227
1228 1文字目と2文字目は1bitのみしか重ならないため、同じHASH値が衝突する確率
1229 は減る。ただし、HASHの戻り値の型は 18bit (0〜17bit目まで)で、 16bit整数
1230 では収まらないので32bit整数にする必要がある。
1231
1232 HASH()の戻り値については、next[]の添え字でしか使っていないので、まあ今時の
1233 コンピュータなら大丈夫である。
1234
1235 node 型はというと、next[] が格納する値は、next[]の添え字である。
1236
1237 つまり、next[] は、常に以下の図の領域のいずれかの位置(NILはリンク先がな
1238 いの意味)を指している。ということは、node型は MAX_HASH_VAL の値の範囲が
1239 必要であり、これは 32bit 整数なのである。
1240
1241 下図で一つ一つの要素は32bitあるが、使うのは18bitだけとはちょっとさすがに
1242 もったいない。
1243
1244 どのくらいもったいないかというと、DICBIT{16}のとき(辞書サイズ64K)の
1245 MAX_HASH_VALは、229503 であり、その一要素が32bit とすると約896Kである。
1246 next だけで、1M近い領域をmalloc()することになる。
1247
1248 しかし、HASH値は前にも述べたとおり、下図の *1 *2 の範囲しか使用しない。
1249
1250 -------------------------------------------------------------------------------------------
1251                              DICSIZ                DICSIZ*2              DICSIZ*3
1252                             /                     /                     /         max_hash_val + 1
1253      +---------------------+---------------------+---------------------+----------+
1254 next | 234...           NIL|                     |       NIL           |    NIL   |
1255      +---------------------+---------------------+---------------------+----------+
1256                                                  |--- *1 ---|          |--- *2 ---|
1257
1258
1259 -------------------------------------------------------------------------------------------
1260
1261 -lh7- 対応は考える必要がありそうだ。
1262