[Lha-users] The Hacking of LHa for UNIX (1st draft)

Back to archive index

Koji Arai JCA02****@nifty*****
2003年 1月 13日 (月) 08:13:08 JST


新井です。

更新。これで一通りの解読が完了。全体を見直して体裁を整えた後は huf.c
に移るか、slide.c のソース書き換えを行うか?

Index: Hackinf_of_LHa
===================================================================
RCS file: /cvsroot/lha/lha/Hackinf_of_LHa,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -u -r1.4 -r1.5
--- Hackinf_of_LHa	12 Jan 2003 18:46:22 -0000	1.4
+++ Hackinf_of_LHa	12 Jan 2003 23:07:31 -0000	1.5
@@ -1,4 +1,4 @@
-$Id: Hackinf_of_LHa,v 1.4 2003/01/12 18:46:22 arai Exp $
+$Id: Hackinf_of_LHa,v 1.5 2003/01/12 23:07:31 arai Exp $
 
                The Hacking of LHa for UNIX (1st draft)
              -------------------------------------------
@@ -1850,6 +1850,142 @@
 
 細部は未だ目をつぶっているのだがこれで match_insert() の解読は終りだ。
 
+match_insert() の処理とは以下の通りだ。
+
+----------------------------------------------------------------------------
+  match_insert() は、text[pos] から始まる文字列に一致する文字列を辞書
+  から検索し、見つかった位置と一致長を matchpos, matchlen に設定する。
+
+  もし、最長一致文字列が見つからなければ matchpos は、off に設定され、
+  matchlen は更新されない。
+
+  見つかった場合、matchlen は呼び出し前の matchlen よりも大きくなる。
+  (呼び出し前では matchlen の意味は最低限一致しなくてはならない文字列
+  長で、照合条件の一つになっている)
+
+  この関数の入力は
+
+      matchlen
+      pos
+
+  出力は
+
+      matchlen
+      matchpos
+
+  といったところ。
+
+  さらに、insert() と同様の処理で、pos の位置をハッシュ配列に記録し、
+  次回の検索に備える。これはついでの処理。
+---------------------------------------------------------------------------- 
+
+これを踏まえた上で処理内容を再読しよう。(E) 〜 (H) だ。
+
+        /* (E) */
+        lastmatchlen = matchlen;  lastmatchoffset = pos - matchpos - 1;
+        --matchlen;
+
+        /* (F) */    /* (G) */
+        get_next();  match_insert();
+        if (matchlen > remainder) matchlen = remainder;
+
+        /* (H) */
+        if (matchlen > lastmatchlen || lastmatchlen < THRESHOLD) {
+            /* (H.1) */
+            encode_set.output(text[pos - 1], 0);
+            count++;
+        } else {
+
+(H) の条件とは何なのかを見る。この条件が真なら、文字列をそのまま出力す
+るのだが、素直に slide 辞書法の処理を考えればこの条件は「辞書から見つ
+からなかった場合」となる。が、実際にはもう少し複雑だ。
+
+        /* (H) */
+        if (matchlen > lastmatchlen || lastmatchlen < THRESHOLD) {
+
+matchlen は、pos の位置の文字列が見つかった辞書上の長さ
+lastmatchlen は、pos-1 の位置の文字列が見つかった辞書上の長さ
+
+であるとすると、この条件は、「pos の位置で見つかった長さが、pos-1 の位
+置で見つかった長さよりも長ければ」っとなる。
+
+これはつまり、pos-1 と pos のニ箇所に関して辞書を検索して、より長くマッ
+チする方を選ぼうとしているわけだ。matchlen の方が長いなら 1 つ前
+(pos-1)の文字はそのまま出力し、次のループに移る(もし、次の文字が
+さらに長くマッチするなら。またそのまま出力される)
+
+この条件で「見つからなかった場合」というのはどう表されているかを考える。
+もし、pos の文字列が辞書になければ pos - 1 の文字列は、どうすべきかと
+いうと「pos-1 の文字列が見つかってなければ。そのまま出力。辞書にあった
+なら <lastmatchlen, lastmatchoffset> のペアを出力」っとならなければな
+らない。
+
+lastmatchlen は、初期状態では THRESHOLD - 1 であったので、見つからなかっ
+たという条件は (H) の右側の条件 lastmatchlen < THRESHOLD でまず表され
+ている。
+
+では、例えば lastmatchlen が 5 であったとしよう。このとき (E) の処理で 
+matchlen は lastmatchlen - 1 つまり、4 に設定される。そして、match_insert()
+で次の文字列がもし辞書から見つからなければ matchlen は更新されないので
+  matchlen < lastmatchlen 
+となる。このような条件(前回見つかり、今回見つからない)場合に限り、(H.2)
+の処理が実行されるようになっている。では、(H.2) の処理を追いかけよう。
+
+まず、この状態を図示する。
+
+----------------------------------------------------------------------------
+
+                         lastmatchlen                  lastmatchlen
+                       |--          --|              |--          --|
+       +---------------+--------------+--------------+--------------+--+
+text   |               |  |  |  |  |  |              |  |  |  |  |  |  |
+       +---------------+--------------+--------------+--------------+--+
+                       ^                             |   \           \
+                      matchpos                    pos-1  pos         pos2
+
+                       |--------------------------|
+                             lastmatchoffset
+
+----------------------------------------------------------------------------
+
+
+            /* (H.2) */
+            encode_set.output(lastmatchlen + (UCHAR_MAX + 1 - THRESHOLD),
+               (lastmatchoffset) & (dicsiz-1) );
+            --lastmatchlen;
+
+            while (--lastmatchlen > 0) {
+                get_next();  insert();
+                count++;
+            }
+            get_next();
+            matchlen = THRESHOLD - 1;
+            match_insert();
+            if (matchlen > remainder) matchlen = remainder;
+        }
+
+まず、<長さ, 位置> のペアを出力する。これはいいだろう。出力する「位置」
+は0 なら 1 文字前を表すので、実際のオフセット pos - 1 - matchpos より
+も 1 小さい値になっていることに注意しておこう。
+
+そして、lastmatchlen は 1 引かれる。この場合例えば 4 になる。したがっ
+て、次のループでは 3 文字 pos が先送りされる(4 ではない)。pos は既に 1 
+文字先に進んでいるので、最初に 1 引くのはこのことを考慮している。while 
+ループが終った後はpos の位置は実際に出力した文字列の最後の文字 pos2-1 
+を指していることになる。
+
+そして、get_next() でまた 1 文字先に送る。pos は図の pos2 の位置になる。
+そして、match_insert() で、この位置の文字列を照合する。matchlen は、
+THRESHOLD - 1 に初期化されるので pos2 の位置の文字列が辞書から見つから
+なければ matchlen は、THRESHOLD-1 だ。これは初期状態と同じ状態を示すの
+で、次のループの処理も想像がつく((H) の条件の右側 lastmatchlen < THRESHOLD
+が有効になる)。では、見つかった場合はというと、次のループでさらに先 
+pos2+1 の照合結果と比較されるのでこの処理も想像がつく。
+
+最初、どうにもこの処理内容が理解できなかったのだが「現在の文字列と、次
+の文字列のそれぞれで辞書を検索し、より長く見つかった方を使う」という最
+適化を行っている事がわかってしまった後は解読は簡単だった。(実はこの事
+実も教えてもらった事だ。全然ダメじゃん)
 
 # Local Variables:
 # mode : indented-text



Lha-users メーリングリストの案内
Back to archive index