From: Koji Arai Date: Mon, 19 Jan 2015 13:08:59 +0000 (+0900) Subject: Change documents to UTF-8 encoding X-Git-Url: http://git.sourceforge.jp/view?p=lha%2Flha.git;a=commitdiff_plain;h=f063c70d75a8b302e3f9c0add269f953b0667175 Change documents to UTF-8 encoding Rewrite 00readme.autoconf to README.jp.md (Markdown) --- diff --git a/00readme.autoconf b/00readme.autoconf deleted file mode 100644 index 44faeff..0000000 --- a/00readme.autoconf +++ /dev/null @@ -1,459 +0,0 @@ -# -*- Mode: text ; Coding: euc-japan -*- - -LHa for UNIX with Autoconf - Koji Arai - -¤³¤Î¥Õ¥¡¥¤¥ë¤Ï¡¢Autoconf ÈÇ LHa for UNIX ¤Ë¤Ä¤¤¤ÆÀâÌÀ¤·¤Þ¤¹¡£ - -1. ¥³¥ó¥Ñ¥¤¥ë¤Î¼ê½ç -2. ¥ª¥ê¥¸¥Ê¥ë¤«¤é¤ÎÊѹ¹ÅÀ - ¡¦-lh6-, -lh7- ¥á¥½¥Ã¥É¤Î¥¢¡¼¥«¥¤¥ÖºîÀ® - ¡¦¥¢¡¼¥«¥¤¥ÖÃæ¤Î´Á»ú¥Õ¥¡¥¤¥ë̾ - ¡¦É¸½àÆþÎϤ«¤é¤ÎŸ³«¥Õ¥¡¥¤¥ë¤Î»ØÄê - ¡¦³ÈÄ¥¥Ø¥Ã¥À(¥æ¡¼¥¶Ì¾/¥°¥ë¡¼¥×̾)¤Î¥µ¥Ý¡¼¥È - ¡¦¥Ð¥Ã¥¯¥¢¥Ã¥×¥Õ¥¡¥¤¥ëºîÀ®¤ÎÍÞ»ß - ¡¦header.c ¤Î½ñ¤­´¹¤¨ - ¡¦level 3 header - ¡¦¥Ø¥Ã¥À¤Î¥À¥ó¥× - ¡¦¥Ç¥Õ¥©¥ë¥È¥Ø¥Ã¥À¥ì¥Ù¥ë - ¡¦³ÈÄ¥¥Ø¥Ã¥À - ¡¦-x ¥ª¥×¥·¥ç¥ó - ¡¦Cygwin ¤Ç¤Î²òÅà - ¡¦large files Âбþ - ¡¦MacBinary¤Ä¤­¥¢¡¼¥«¥¤¥Ö¤Î¥µ¥Ý¡¼¥È -3. ¥Ð¥° -4. ºÆÇÛÉۤˤĤ¤¤Æ - -1. ¥³¥ó¥Ñ¥¤¥ë¤Î¼ê½ç -==================== - - ¥³¥ó¥Ñ¥¤¥ë¤Î¼ê½ç¤Ï°Ê²¼¤Î¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹¡£ - - gzip -dc lha-1.14i-acXXXXXXXX.tar.gz | tar xvf - - cd lha-114i-acXXXXXXXX - - sh ./configure - make - make check - make install - - MinGW Âбþ¤Ï¦ÁÈǤǤ¹¡£¤Û¤È¤ó¤É¥Æ¥¹¥È¤µ¤ì¤Æ¤¤¤Þ¤»¤ó(make check ¤¬À®¸ù - ¤¹¤ëÄøÅÙ)¡£Cygwin ´Ä¶­¤Ç MinGW ÈǤò»î¤¹¤Ë¤Ï - - sh ./configure CC='gcc -mno-cygwin' - --build=i686-pc-mingw32 - --with-tmp-file=no - - ¤Ê¤É¤È¤·¤Æ¤¯¤À¤µ¤¤¡£ - - ¢¨ Cygwin ¤ä MinGW Åù¡¢Windows ´Ä¶­¤Ç¤Ï¿¼¤¤¥Ç¥£¥ì¥¯¥È¥ê¤Ç¥³¥ó¥Ñ¥¤¥ë - ¤¹¤ë¤Èmake check ¤¬¼ºÇÔ¤¹¤ë¾ì¹ç¤¬¤¢¤ê¤Þ¤¹¡£¤³¤ì¤ÏŤ¤¥Ñ¥¹¤Î³ÊǼ - ¥Á¥§¥Ã¥¯¤ò¤¹¤ë¤È¤­¤Ë¡¢Windows ¤Î¥Õ¥ë¥Ñ¥¹Ä¹¤ÎÀ©¸Â¤Ë¤Ò¤Ã¤«¤«¤ë¤¿¤á - ¤Ç¤¹¡£¤³¤Î¤è¤¦¤Ê¾ì¹ç¤Ï¡¢°Ê²¼¤Î¤è¤¦¤Ë configure ¤òÀõ¤á¤Î¥Ç¥£¥ì¥¯¥È - ¥ê¤Ç¼Â¹Ô¤·¤Æ¤¯¤À¤µ¤¤¡£ - - Îã: - cd /tmp/build - sh ~/src/lha/configure .... - make - make check - make install - - autoconf/automake ¤¬¥¤¥ó¥¹¥È¡¼¥ë¤µ¤ì¤Æ¤¤¤ë¾ì¹ç¤Ç¡¢lha ¥½¡¼¥¹¤ä - configure.ac, Makefile.am ¤ò¥á¥ó¥Æ¥Ê¥ó¥¹¤¹¤ë¾ì¹ç¤Ï°Ê²¼¤Î¼ê½ç¤Ë¤Ê¤ê¤Þ - ¤¹¡£autoconf/automake ¤Î¥Ð¡¼¥¸¥ç¥ó¤Ï¤½¤ì¤¾¤ì autoconf 2.5x, automake - 1.6.x °Ê¹ß¤Ç¤ÎÍøÍѤòÁ°Äó¤È¤·¤Æ¤¤¤Þ¤¹¡£ - - gzip -dc lha-1.14i-acXXXXXXXX.tar.gz | tar xvf - - cd lha-114i-acXXXXXXXX - - aclocal - automake -a - autoheader - autoconf - - # aclocal ¤«¤é autoconf ¼Â¹Ô¤Þ¤Ç¤Î¼ê½ç¤Ï¡¢ºÇ¶á¤Ç¤Ï¡¢autoreconf -is - # ¤Ç¤¤¤±¤ë¤è¤¦¤Ç¤¹¡£ - - sh ./configure - make - make check - make install - -2. ¥ª¥ê¥¸¥Ê¥ë¤«¤é¤ÎÊѹ¹ÅÀ -========================= - -Autoconf ÈÇ LHa for UNIX ¤Ï¡¢ -LHa for UNIX ver1.14i -¤ò¸µ¤Ë²þÊѤµ¤ì¤Þ¤·¤¿¡£ - -¼ç¤ÊÊѹ¹ÅÀ¤ò²¼µ­¤Ëµó¤²¤Þ¤¹¡£ - -¡¦-lh6-, -lh7- ¥á¥½¥Ã¥É¤Î¥¢¡¼¥«¥¤¥ÖºîÀ® - - ¥ª¥ê¥¸¥Ê¥ë¤Î LHa for UNIX 1.14i ¤Ç¤Ï¡¢SUPPORT_LH7 ¤ÎÄêµÁ¤ò¤»¤º¤Ë¥³¥ó - ¥Ñ¥¤¥ë¤·¤¿¾ì¹ç¡¢-lh6- ¤ª¤è¤Ó -lh7- ¥á¥½¥Ã¥É¤Î¥¢¡¼¥«¥¤¥Ö¤òºîÀ®¤Ç¤­¤Þ - ¤»¤ó¤Ç¤·¤¿¡£¤³¤Î¤³¤È¤ÏÊ̤˹½¤ï¤Ê¤¤¤Î¤Ç¤¹¤¬ SUPPORT_LH7 ¤òÄêµÁ¤¹¤ë¤È - ¥Ç¥Õ¥©¥ë¥È¤Ç -lh7- ¥á¥½¥Ã¥É¤Î¥¢¡¼¥«¥¤¥ÖºîÀ®¤ò¶¯À©¤µ¤ì¤Æ¤·¤Þ¤¤¤Þ¤¹¡£ - ¤½¤³¤Ç¡¢¤â¤¦¾¯¤·½ÀÆð¤Ë lha ÍøÍѼԤ¬¤³¤ì¤é¤òÁªÂò¤Ç¤­¤ë¤è¤¦ - SUPPORT_LH7 ¤Ï¾ï¤ËÄêµÁ¤¹¤ë¤è¤¦¤Ë¤·¡¢¥Ç¥Õ¥©¥ë¥È¤ÇºîÀ®¤µ¤ì¤ë¥¢¡¼¥«¥¤¥Ö - ¤Î¥á¥½¥Ã¥É»ØÄê¤ò configure ¥ª¥×¥·¥ç¥ó¤Î --with-default-method=[567] - ¤Ç»ØÄê¤Ç¤­¤ë¤è¤¦¤Ë¤·¤Þ¤·¤¿¡£ - - ¤³¤Î¥ª¥×¥·¥ç¥ó¤Î¾ÊάÃÍ¤Ï -lh5- ¤Ç¤¹¡£¤Ä¤Þ¤ê¥Ç¥Õ¥©¥ë¥È¤Ç¤Ï -lh5- ¥¢¡¼ - ¥«¥¤¥Ö¤òºîÀ®¤·¤Þ¤¹¡£(¤½¤·¤Æ¡¢¾å¤Ç½Ò¤Ù¤¿Ä̤ꡢlha¤Î o6 ¤Þ¤¿¤Ï o7 ¥ª¥× - ¥·¥ç¥ó¤Ë¤è¤ê¤¤¤Ä¤Ç¤â-lh6-¡¢-lh7- ¥¢¡¼¥«¥¤¥Ö¤òºîÀ®¤Ç¤­¡¢configure ¥ª - ¥×¥·¥ç¥ó¤Ë¤è¤ê¥Ç¥Õ¥©¥ë¥È¤ÎµóÆ°¤òÊѹ¹¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹) - -¡¦¥¢¡¼¥«¥¤¥ÖÃæ¤Î´Á»ú¥Õ¥¡¥¤¥ë̾ - - ¥ª¥ê¥¸¥Ê¥ë¤Î LHa for UNIX 1.14i ¤Ï¥¢¡¼¥«¥¤¥Ö¤Ë³ÊǼ¤¹¤ë¥Õ¥¡¥¤¥ë̾¤Î´Á - »ú¥³¡¼¥É¤Ë´Ø¤·¤Æ̵ÆÜÃå¤Ç¤¹¡£¥³¥ó¥Ñ¥¤¥ë»þ¤Ë MULTIBYTE_CHAR ¤òÄêµÁ¤·¤¿ - ¤È¤­¤Ç¤â¥¢¡¼¥«¥¤¥ÖÃæ¤Î Shift JIS ¥Õ¥¡¥¤¥ë̾¤ò EUC ¤Ë¤¹¤ë¤³¤È¤â¤Ê¤¯¡¢ - EUC ¥³¡¼¥É¤Î¤Þ¤Þ(Àµ³Î¤Ë¤Ï¥·¥¹¥Æ¥à¤Î´Á»ú¥³¡¼¥É¤Î¤Þ¤Þ)¥¢¡¼¥«¥¤¥Ö¤Ë³ÊǼ - ¤·¤¿¤ê¤·¤Þ¤¹¡£ - - autoconf ÈǤǤϡ¢configure ¥ª¥×¥·¥ç¥ó --enable-multibyte-filename ¤Ë - ¤è¤ê´Á»ú¥Õ¥¡¥¤¥ë̾¤¬»ÈÍѤǤ­¡¢¥¢¡¼¥«¥¤¥Ö¤Ë³ÊǼ¤µ¤ì¤ë¥Õ¥¡¥¤¥ë̾¤Î´Á»ú - ¥³¡¼¥É¤ò SJIS ¸ÇÄê¤È¤·¤Æ°·¤¤¤Þ¤¹¡£ - - --enable-multibyte-filename ¤Î°ú¿ô(¥·¥¹¥Æ¥à¤Î¥Õ¥¡¥¤¥ë̾¤Î´Á»ú¥³¡¼¥É - »ØÄê)¤Ï¡¢°Ê²¼¤ÎÄ̤ê¤Ç¤¹¡£ - - --enable-multibyte-filename=sjis - ¥·¥¹¥Æ¥à¤Î´Á»ú¥³¡¼¥É¤ò SJIS ¤È¤·¤Æ°·¤¤¤Þ¤¹¡£ - --enable-multibyte-filename=euc - ¥·¥¹¥Æ¥à¤Î´Á»ú¥³¡¼¥É¤ò EUC ¤È¤·¤Æ°·¤¤¤Þ¤¹¡£ - --enable-multibyte-filename=utf8 - ¥·¥¹¥Æ¥à¤Î´Á»ú¥³¡¼¥É¤ò UTF-8 ¤È¤·¤Æ°·¤¤¤Þ¤¹¡£ - º£¤Î¤È¤³¤í Mac OS X ¤Ç¤À¤±¤³¤Î¥ª¥×¥·¥ç¥ó¤ò¥µ¥Ý¡¼¥È¤·¤Þ¤¹¡£ - --enable-multibyte-filename=auto (¤Þ¤¿¤Ï yes ¤Þ¤¿¤Ï°ú¿ô¤Ê¤·) - ¥·¥¹¥Æ¥à¤Î´Á»ú¥³¡¼¥É¤ò¼«Æ°¤ÇȽÊ̤·¤Þ¤¹¡£¼«Æ°¤È¤¤¤Ã¤Æ¤â¸½¾õ¤Ï¡¢ - Cygwin, MinGW, HP-UX ¤Î¾ì¹ç¤Ë SJIS¡¢Mac OS X ¤Î¾ì¹ç UTF-8¡¢ - ¤½¤ì°Ê³°¤ò EUC ¤È¤ß¤Ê¤¹¤À¤±¤Ç¤¹¡£ - --enable-multibyte-filename=no - --disable-multibyte-filename - ¥Õ¥¡¥¤¥ë̾¤Î¥Þ¥ë¥Á¥Ð¥¤¥È¥µ¥Ý¡¼¥È¤ò̵¸ú¤Ë¤·¤Þ¤¹¡£ - - ¥Ç¥Õ¥©¥ë¥È¤Ï¡¢auto ¤Ç¤¹¡£ - - lha ¤Î¥³¥Þ¥ó¥É¥é¥¤¥ó¥ª¥×¥·¥ç¥ó¤Ë¤è¤ê¡¢¥³¥ó¥Ñ¥¤¥ë»þ¤Î¥Ç¥Õ¥©¥ë¥È»ØÄê¤ò - Êѹ¹¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£¤³¤Î¥³¥Þ¥ó¥É¥é¥¤¥ó¥ª¥×¥·¥ç¥ó¤Ï¡¢GNU style ¤Î - long option (¥À¥Ã¥·¥å2¤Ä¤¬Àè¹Ô¤¹¤ë¥¹¥¿¥¤¥ë)¤Ç»ØÄꤷ¤Þ¤¹¡£ - - --system-kanji-code=xxx - ¥·¥¹¥Æ¥à¤Î¥Õ¥¡¥¤¥ë̾¤Î´Á»ú¥³¡¼¥É¤ò»ØÄꤷ¤Þ¤¹¡£ - - --archive-kanji-code=xxx - ¥¢¡¼¥«¥¤¥ÖÆâ¤Ø³ÊǼ¤¹¤ë¤È¤­¤Î¥Õ¥¡¥¤¥ë̾¤Î¥³¡¼¥É¤ò»ØÄꤷ - ¤Þ¤¹¡£¤³¤ì¤ÏÄ̾ï SJIS ¸ÇÄê¤Ê¤Î¤ÇÊѹ¹¤¹¤ë¤Ù¤­¤Ç¤Ï¤¢¤ê¤Þ - ¤»¤ó¡£ - - xxx ¤Ï sjis, euc, utf8, cap ¤Î¤¤¤º¤ì¤«¤Ç¤¹¡£cap ¤Ï¡¢samba ¤Ê¤É¤Ç»È¤ï¤ì¤ë - ¥³¡¼¥É¤Ç¡¢´Á»ú¥³¡¼¥É¤ò ":" ¤È 16 ¿Êʸ»ú¤Çɽ¸½¤¹¤ë¥³¡¼¥É¤Ç¤¹¡£ - - Î㤨¤Ð¡¢ - - $ touch ´Á»ú - $ lha c foo.lzh ´Á»ú - $ lha l foo.lzh - - PERMISSION UID GID SIZE RATIO STAMP NAME - ---------- ----------- ------- ------ ------------ -------------------- - -rw-r--r-- 1000/1000 0 ****** Mar 23 21:23 ´Á»ú - ---------- ----------- ------- ------ ------------ -------------------- - Total 1 file 0 ****** Mar 23 21:23 - - $ lha l --system-kanji-code=cap foo.lzh - - PERMISSION UID GID SIZE RATIO STAMP NAME - ---------- ----------- ------- ------ ------------ -------------------- - -rw-r--r-- 1000/1000 0 ****** Mar 23 21:23 :8a:bf:8e:9a - ---------- ----------- ------- ------ ------------ -------------------- - - ¤Ê¤É¤È¤Ê¤ê¤Þ¤¹¡£ - - Mac OS X ÍѤΠutf8 <-> sjis ÊÑ´¹¤Ï¡¢2002/6 ¤Ëºä°æ¹À¿Í¤µ¤ó¤ËºîÀ®¤·¤Æ - ¤¤¤¿¤À¤­¤Þ¤·¤¿¡£¤¢¤ê¤¬¤È¤¦¤´¤¶¤¤¤Þ¤¹¡£ - - ¤Þ¤¿¡¢Mac OS X °Ê³°¤Ç¤â¡¢iconv ¥é¥¤¥Ö¥é¥ê¤ò»ÈÍѤ¹¤ì¤Ð¡¢UTF-8 ¤ò»ÈÍѤ¹ - ¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£¤¿¤À¤·¡¢¸½ºß¤Î¤È¤³¤í iconv ¥é¥¤¥Ö¥é¥ê¤Î¸ºß¤ò¼«Æ°Åª - ¤Ë¸¡½Ð¤·¤Ê¤¤¤Î¤Ç¡¢libc ¤Ë iconv() ´Ø¿ô¤¬¤Ê¤¯¡¢libiconv ¤¬Â¸ºß¤¹¤ë - (iconv¥é¥¤¥Ö¥é¥ê¤ò¥ê¥ó¥¯¤¹¤ëɬÍפ¬¤¢¤ë)´Ä¶­¤Ç¤Ï¡¢¥³¥ó¥Ñ¥¤¥ë»þ¤Ë - - sh ./configure LIBS=-liconv - - ¤Ê¤É¤È¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£ - -¡¦É¸½àÆþÎϤ«¤é¤ÎŸ³«¥Õ¥¡¥¤¥ë¤Î»ØÄê - - ¥ª¥ê¥¸¥Ê¥ë¤Î LHa for UNIX 1.14i ¤Ï¡¢ - - echo foo.txt | lha x foo.lzh - find bar -name '*.[ch]' | lha c bar.lzh - - ¤È¤¹¤ë¤È¡¢foo.lzh ¤«¤é foo.txt ¤À¤±¤òŸ³«¤·¤¿¤ê¡¢bar ¥Ç¥£¥ì¥¯¥È¥êÇÛ - ²¼¤Î¥Õ¥¡¥¤¥ë¤ò bar.lzh ¤Ë³ÊǼ¤·¤¿¤ê¤Ç¤­¤Þ¤¹¡£¤Ä¤Þ¤ê¡¢É¸½àÆþÎϤ«¤é¡¢ - °µ½Ì¡¿Å¸³«¥Õ¥¡¥¤¥ë¤ò»ØÄê¤Ç¤­¤ëµ¡Ç½¤Ê¤Î¤Ç¤¹¤¬¡¢¤¤¤Þ¤¤¤Á»È¤¤Æ»¤¬¤Ê¤¤¤ï - ¤ê¤Ë¼ÙËâ¤Êµ¡Ç½¤Ç¤¹(°µ½Ì¤ÎÎã¤Ï¡¢cpio ƱÍͤޤ¢»È¤¨¤ë¤±¤É¡¢¤³¤Î¤¿¤á¤Î¥ª - ¥×¥·¥ç¥ó¤ò¿·Àߤ¹¤ë¤Î¤¬Îɤ¤¤È¹Í¤¨¤Æ¤¤¤Þ¤¹) - - tty ¤«¤é¤ÎÍøÍѤ·¤«ÁÛÄꤷ¤Æ¤Ê¤¤¤È»×¤ï¤ì¤Þ¤¹¤¬¡¢¤ª¤½¤é¤¯ daemon ¤«¤é - lha ¤ò¼Â¹Ô¤¹¤ë¾ì¹ç¤Ê¤É¤Ç°Õ¿Þ¤·¤Ê¤¤Æ°ºî¤ò¤¹¤ë¤Ç¤·¤ç¤¦¡£¤½¤¦¤¤¤¦¤ï¤±¤Ç - ¾¡¼ê¤Ê¤¬¤é¤³¤Îµ¡Ç½¤Ïºï½ü¤·¤Þ¤·¤¿¡£°Ê²¼¤Î¤è¤¦¤Ê»ö¤¬¤Ç¤­¤Ê¤¤ Windows - ´Ä¶­¤Ç¤Ï°ÕÌ£¤Î¤¢¤ëµ¡Ç½¤À¤Ã¤¿¤«¤â¤·¤ì¤Þ¤»¤ó¤¬ - - lha x bar.lzh `echo foo.txt` - - »ÄÇ°¤Ê¤¬¤é MinGW ¤Ç isatty() ¤¬¤¦¤Þ¤¯Æ°ºî¤·¤Þ¤»¤ó¤Ç¤·¤¿¤«¤é Windows - ¤Ç¤Ï»È¤¨¤Ê¤¤¤Î¤Ç¤·¤¿¡£ - - ¥ª¥ê¥¸¥Ê¥ë¤Î»ÅÍͤòÉü³è¤µ¤»¤¿¤¤¾ì¹ç¤Ï¡¢lharc.c ¤Î 568¹ÔÌÜÉÕ¶á¤Î #if 0 - ¤ò #if 1 ¤Ë¤·¤Æ¤¯¤À¤µ¤¤¡£ - -¡¦³ÈÄ¥¥Ø¥Ã¥À(¥æ¡¼¥¶Ì¾/¥°¥ë¡¼¥×̾)¤Î¥µ¥Ý¡¼¥È - - ¥æ¡¼¥¶Ì¾¡¢¥°¥ë¡¼¥×̾¤Î³ÈÄ¥¥Ø¥Ã¥À(0x52, 0x53)¤òºîÀ®¤Ç¤­¤ë¤è¤¦¤Ë¤·¤Þ¤· - ¤¿(¥Ç¥Õ¥©¥ë¥È¤Ïoff)¡£¾ÜºÙ¤Ï header.doc.euc ¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£Å¸³« - ¤È°ìÍ÷ɽ¼¨¤Î¤È¤­¤Ë¥Ø¥Ã¥À¤Ë¤³¤Î¾ðÊ󤬤¢¤ì¤Ð ID ¤ËÍ¥À褷¤Æ¤³¤Î¾ðÊó¤¬Íø - ÍѤµ¤ì¤Þ¤¹¡£ºîÀ®¤Ï configure ¥ª¥×¥·¥ç¥ó --enable-user-name-header ¤ò - »ØÄꤷ¤Æ build ¤·¤¿¾ì¹ç¤ËÍ­¸ú¤Ë¤Ê¤ê¤Þ¤¹¡£ - -¡¦¥Ð¥Ã¥¯¥¢¥Ã¥×¥Õ¥¡¥¤¥ëºîÀ®¤ÎÍÞ»ß - - ¥ª¥ê¥¸¥Ê¥ë LHa for UNIX 1.14i ¤Ç¤Ï¡¢¥¢¡¼¥«¥¤¥Ö¤Ë¥Õ¥¡¥¤¥ë¤òÄɲä·¤¿¤È - ¤­¤ä¥¢¡¼¥«¥¤¥Ö¤«¤é¥Õ¥¡¥¤¥ë¤òºï½ü¤·¤¿¤È¤­¤Ë¡¢¸µ¤Î¥¢¡¼¥«¥¤¥Ö¤ò .bak ¤È - ¤¤¤¦³ÈÄ¥»Ò¤ÇÊݸ¤·¤Þ¤¹¡£¤³¤ÎµóÆ°¤ÏÈѤ路¤¯´¶¤¸¤¿¤Î¤Ç autoconf ÈÇ¤Ç¤Ï - ¥Ð¥Ã¥¯¥¢¥Ã¥×¥Õ¥¡¥¤¥ë¤òºîÀ®¤·¤Ê¤¤¤è¤¦¤Ë¤·¤Þ¤·¤¿¡£¤³¤Î autoconf Èǥѥà - ¥Á¤¬¿®ÍѤǤ­¤Ê¤¤¤è¤¦¤Ê¿Í¤Ï configure ¥ª¥×¥·¥ç¥ó --enable-backup-archive - ¤ò»ØÄꤷ¤Æ build ¤·¤Æ¤¯¤À¤µ¤¤(¤¼¤Ò¤½¤¦¤¹¤ë¤Ù¤­¤Ç¤¹:p)¡£¥ª¥ê¥¸¥Ê¥ë¤È - Ʊ¤¸µóÆ°¤Ë¤Ê¤ê¤Þ¤¹¡£ - -¡¦header.c ¤Î½ñ¤­´¹¤¨ - - header.c ¤Ïºî¤êÊѤ¨¤é¤ì¤Þ¤·¤¿¡£¾åµ­¤Ë¼¨¤·¤¿Êѹ¹¤Ë²Ã¤¨¤Æ¥ª¥ê¥¸¥Ê¥ë - LHa for UNIX 1.14i ¤«¤é°Ê²¼¤ÎÉÔ¶ñ¹ç¤¬½¤Àµ¤µ¤ì¤Þ¤·¤¿¡£ - - o level 2 header ¤Î¥Ð¥° - - total header size (¥¢¡¼¥«¥¤¥Ö¥Ø¥Ã¥À¤ÎÀèƬ 2 byte) ¤¬ 256 °Ê¾å - ¤Ç¤¢¤ë¥¢¡¼¥«¥¤¥Ö¤òÀµ¤·¤¯Æɤळ¤È¤¬¤Ç¤­¤Þ¤»¤ó¤Ç¤·¤¿¡£¤Þ¤¿¡¢total - header size ¤¬¤Á¤ç¤¦¤É 256 ¤Ë¤Ê¤ë¤è¤¦¤ÊÉÔÀµ¤Ê¥¢¡¼¥«¥¤¥Ö¤òºîÀ®¤· - ¤Æ¤¤¤Þ¤·¤¿¡£LHA ¤Î¥Ø¥Ã¥À»ÅÍͤǤϥإåÀÀèƬ¤¬ 0 ¤Ç¤¢¤ì¤Ð¥¢¡¼¥«¥¤ - ¥Ö¤Î½ªÃ¼¤È¤ß¤Ê¤¹¤¿¤á total header size ¤ò 256 ¤ÎÇÜ¿ô¤Ë¤Ç¤­¤Þ¤»¤ó¡£ - (256 ¤Ê¤É¤Ï little-endian ¤Ç 0x00 0x01 ¤È¤Ê¤ë¤¿¤á¡¢ÀèƬ¤¬ 0 ¤Ë¤Ê - ¤ê¤Þ¤¹¡£) - - Æɤ߹þ¤ß»þ¤Ë¥Ø¥Ã¥À¤Î CRC check ¤ò¹Ô¤¦¤è¤¦¤Ë¤·¤Þ¤·¤¿¡£ - - o level 1 header ¤Î¥Ð¥° - - ¥Õ¥¡¥¤¥ë̾¤ËÂФ·¤Æ³ÈÄ¥¥Ø¥Ã¥À¤ò»ÈÍѤ¹¤ë¤³¤È¤¬¤Ê¤¤¤¿¤á¡¢230 ¥Ð¥¤¥È - ¤ò±Û¤¨¤ë¥Õ¥¡¥¤¥ë̾(¥Ç¥£¥ì¥¯¥È¥ê¤ò´Þ¤Þ¤Ê¤¤)¤ò¥¢¡¼¥«¥¤¥Ö¤Ë½ñ¤¯¤È¥¢¡¼ - ¥«¥¤¥Ö¥Ø¥Ã¥À¤Î¥µ¥¤¥ºÀ©¸Â¤ò±Û¤¨¤¿ÉÔÀµ¤Ê¥¢¡¼¥«¥¤¥Ö¤¬ºîÀ®¤µ¤ì¤Æ¤¤¤Þ - ¤·¤¿¡£ - - o level 0 header ¤Î¥Ð¥° - - Ť¤¥Ñ¥¹Ì¾(¥Ç¥£¥ì¥¯¥È¥ê¤â´Þ¤à)¤ËÂФ·¤Æ¥¢¡¼¥«¥¤¥Ö¥Ø¥Ã¥À¤Î¥µ¥¤¥ºÀ© - ¸Â¤ò±Û¤¨¤¿ÉÔÀµ¤Ê¥¢¡¼¥«¥¤¥Ö¤¬ºîÀ®¤µ¤ì¤Æ¤¤¤Þ¤·¤¿(¼ÂºÝ¤Ë¤Ï¡¢¥ª¥ê¥¸ - ¥Ê¥ë¤Ï level 0 header ¤Ë¥Ç¥£¥ì¥¯¥È¥ê¤Î¾ðÊó¤ò°ìÀÚ½ñ¤«¤Ê¤¤¤Î¤Ç¤³¤Î - À©¸Â¤Ï¤ä¤Ï¤ê¥Õ¥¡¥¤¥ë̾ŤÀ¤±¤¬Âоݤˤʤê¤Þ¤¹)¡£autoconf ÈǤǤÏÀ© - ¸Â¤ò±Û¤¨¤¿¥Ñ¥¹Ì¾¤Ï warning ¥á¥Ã¥»¡¼¥¸¤ò½ÐÎϤ·¡¢¥Ñ¥¹Ì¾¤Î¸å¤í¤ò - ÀÚ¤êµÍ¤á¤Þ¤¹¡£(level 0 header ¤Ï»ÈÍѤ¹¤ë¤Ù¤­¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó) - - ¶õ¤Î(¥Ç¥£¥ì¥¯¥È¥ê̾¾ðÊó¤Î¤Ê¤¤) -lhd- ¥Ø¥Ã¥À¤¬ºîÀ®¤µ¤ì¤Æ¤¤¤Þ¤·¤¿¡£ - - $ mkdir foo - $ lha c0 foo.lzh foo - $ lha v foo.lzh - PERMSSN UID GID PACKED SIZE RATIO METHOD CRC STAMP NAME - ---------- ----------- ------- ------- ------ ---------- ------------ ------------- - drwxrwxr-x 1000/1000 0 0 ****** -lhd- 0000 Jul 29 00:18 - ---------- ----------- ------- ------- ------ ---------- ------------ ------------- - Total 1 file 0 0 ****** Jul 29 00:18 - - ¤Ê¤ª¡¢level 0 header ¤Ç -lhd- method ¤Ï»È¤¨¤Ê¤¤¤È¤¤¤¦À⤬¤¢¤ë - - - - ¤Î¤Ç¤¹¤¬¡¢µÈºê±ÉÂÙ»á¤Î¥ª¥ê¥¸¥Ê¥ë LHA (DOS/WindowsÈÇ) (ver 2.55, - 2.67) ¤Ê¤É¤Ï -lhd- method ¤ò level 0 header ¤ÇºîÀ®¤·¤Þ¤¹¡£ - - ¢¨ ¤³¤Î°Õ¸«¤Ïǧ¤á¤é¤ì()¡¢ - ¾åµ­¤Î¥É¥­¥å¥á¥ó¥È¤Ï¡¢ÄûÀµ¤µ¤ì¤¿¤è¤¦¤Ç¤¹¡£ - - - - o g ¥ª¥×¥·¥ç¥ó¤òÉÕ¤±¤¿¤È¤­¤Î level 0, 1, 2 header - - g ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¢¡¼¥«¥¤¥ÖºîÀ®¤Î¤È¤­ UNIX ¸ÇÍ­¤Î¾ðÊó¤ò¥¢¡¼¥«¥¤¥Ö - ¤ËºîÀ®¤¹¤ë¤Î¤òÍ޻ߤ¹¤ë¤È man ¤Ë¤Ï¤¢¤ë¤Î¤Ç¤¹¤¬¡¢¼ÂºÝ¤Ë¤Ï¥Ç¥£¥ì¥¯ - ¥È¥ê¾ðÊó¤Þ¤ÇÍ޻ߤµ¤ì¤Æ¤¤¤Þ¤·¤¿¡£ - - $ mkdir foo - $ touch foo/bar - $ lha cg1 foo.lzh foo - $ lha foo.lzh - PERMSSN UID GID SIZE RATIO STAMP NAME - ---------- ----------- ------- ------ ------------ -------------------- - [generic] 0 ****** Jul 29 00:02 bar - ---------- ----------- ------- ------ ------------ -------------------- - Total 1 file 0 ****** Jul 29 00:02 - - autoconf ÈǤǤϾ嵭¤Ï foo/bar ¤Ë¤Ê¤ê¤Þ¤¹¡£(¥ª¥ê¥¸¥Ê¥ë¤Ï¤ï¤¶¤È¤½ - ¤¦¤·¤Æ¤¤¤¿¤Î¤«¤â¤·¤ì¤Þ¤»¤ó¤¬¡¢¤½¤¦¤¹¤ëÍýͳ¤Ï¤Ê¤¤¤ÈȽÃǤ·¤Þ¤·¤¿) - g ¥ª¥×¥·¥ç¥ó¤Ç -lhd- ¤ÎºîÀ®¤¬Í޻ߤµ¤ì¤ë¤Î¤ÏƱ¤¸¤Ç¤¹¡£ - - ¤Ê¤ª¡¢g ¥ª¥×¥·¥ç¥ó¤È¥Ø¥Ã¥À¥ì¥Ù¥ë¤Î»ØÄê¤òƱ»þ¤Ë¹Ô¤¦¤È¤­¤Ï¾åµ­¤Î¤è - ¤¦¤Ë g ¥ª¥×¥·¥ç¥ó¤òÀè¤Ë»ØÄꤹ¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£lha c1g ¤Ê¤É g ¥ª - ¥×¥·¥ç¥ó¤ò¸å¤Ë»ØÄꤹ¤ë¤È level 0 header ¤¬ºîÀ®¤µ¤ì¤Þ¤¹(¤³¤Î¥ª¥ê - ¥¸¥Ê¥ë»ÅÍͤϤÁ¤ç¤Ã¤È¤ï¤«¤ê¤Ë¤¯¤¤¤Ç¤¹)¡£ - -¡¦level 3 header - - À¤¤ÎÃæ¤Ë¤Ï¡¢level 3 header ¤È¤¤¤¦¤â¤Î¤¬Â¸ºß¤¹¤ë¤è¤¦¤Ç¤¹¤¬¡¢¤Þ¤À»ÅÍÍ - ¤È¤·¤Æfix ¤µ¤ì¤Æ¤Ê¤¤¤è¤¦¤Ê¤Î¤Ç*Æɤ߹þ¤ß¤Î¤ß*¥µ¥Ý¡¼¥È¤·¤Þ¤·¤¿¡£ÄɲäΠ- ³ÈÄ¥¥Ø¥Ã¥À¤Ï̤Âбþ¤Ç¤¹¡£(ÆäËÂбþ¤¹¤Ù¤­¥Ø¥Ã¥À¤¬¸«Åö¤¿¤é¤Ê¤«¤Ã¤¿) - largefile Âбþ¤¹¤ë¾ì¹ç¤Ï¡¢¤³¤Î level 3 header ¤ò¥µ¥Ý¡¼¥È¤·¤¿Êý¤¬Îɤµ - ¤½¤¦¤Ç¤¹¡£ - -¡¦¥Ø¥Ã¥À¤Î¥À¥ó¥× - - ¤Þ¤Ã¤¿¤¯¤Î¤ª¤Þ¤±µ¡Ç½¤È¤·¤Æ¥Ø¥Ã¥À¤Î¥À¥ó¥×µ¡Ç½¤òÄɲä·¤Þ¤·¤¿¡£¤³¤ì¤Ï´° - Á´¤Ë¥Ç¥Ð¥Ã¥°ÍѤǤ¹¡£ - lha vvv foo.lzh - ¤È¤¹¤ë¤È¡¢¥¢¡¼¥«¥¤¥Ö¤ÎÆâÍÆ°ìÍ÷¤Ë¤Þ¤¶¤Ã¤Æ¥À¥ó¥×¤¬½ÐÎϤµ¤ì¤Þ¤¹¡£ - -¡¦¥Ç¥Õ¥©¥ë¥È¥Ø¥Ã¥À¥ì¥Ù¥ë - - ¥¢¡¼¥«¥¤¥Ö¤òºîÀ®¤¹¤ë¤È¤­¤Î¥Ç¥Õ¥©¥ë¥È¤Î¥Ø¥Ã¥À¥ì¥Ù¥ë¤ò 2 ¤Ë¤·¤Þ¤·¤¿¡£ - (¥ª¥ê¥¸¥Ê¥ë¤Î LHa for UNIX 1.14i ¤Ç¤Ï¥ì¥Ù¥ë 1 ¤¬¥Ç¥Õ¥©¥ë¥È) - -¡¦³ÈÄ¥¥Ø¥Ã¥À - - ³ÈÄ¥¥Ø¥Ã¥À Windows timestamp (0x41) ¤ò²ò¼á¤¹¤ë¤è¤¦¤Ë¤·¤Þ¤·¤¿¡£(level - 1 header ¤Î¤ß)¡£level 2 °Ê¾å¤Ç¤Ï¡¢´ðËܥإåÀ¤Ë time_t ¤Î¾ðÊ󤬤¢¤ë¤Î - ¤Ç¡¢³ÈÄ¥¥Ø¥Ã¥À¤ÎÊý¤Ï̵»ë¤·¤Þ¤¹¡£ - level 1 header ¤Î¥¢¡¼¥«¥¤¥Ö¤ËÂФ·¤Æ¡¢Windows timestamp ³ÈÄ¥¥Ø¥Ã¥À - ¤ò½ÐÎϤ¹¤ë LHA ¥¢¡¼¥«¥¤¥Ð¤¬Â¸ºß¤¹¤ë¤«¤É¤¦¤«¤Ï̤³Îǧ¤Ç¤¹¡£¤¢¤Þ¤ê¡¢ - Ìò¤ËΩ¤¿¤Ê¤¤½¤Àµ¤À¤Ã¤¿µ¤¤¬¤·¤Þ¤¹¤¬¤»¤Ã¤«¤¯ºî¤Ã¤¿¤Î¤Ç»Ä¤·¤Æ¤Þ¤¹:-) - -¡¦-x ¥ª¥×¥·¥ç¥ó - - °µ½ÌÂоݤΥե¡¥¤¥ë¤«¤é½ü³°¤µ¤ì¤ë¥Ñ¥¿¡¼¥ó¤ò»ØÄꤹ¤ë -x ¥ª¥×¥·¥ç¥ó¤òÄÉ - ²Ã¤·¤Þ¤·¤¿¡£¤³¤ì¤Ëȼ¤¤¡¢ - - lha c -x '*.o' -x='*.a' -x'*.c' src.lzh src - - ¤È¤¤¤Ã¤¿»ØÄ꤬¤Ç¤­¤ë¤è¤¦¡¢¥ª¥×¥·¥ç¥ó²òÀÏÉô¤ÏÊѹ¹¤µ¤ì¤Þ¤·¤¿¡£ - ËܥС¼¥¸¥ç¥ó¤Î usage ¤Ï°Ê²¼¤Î¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹¡£ - - usage: lha [-][] [- ...] archive_file [file...] - -¡¦Cygwin ¤Ç¤Î²òÅà - - MS-DOS ¥¿¥¤¥×¤Ê¤É permission ¤Î¾ðÊó¤ò»ý¤¿¤Ê¤¤¥¢¡¼¥«¥¤¥Ö¤ò Cygwin ¤Ç - ²òÅह¤ë¾ì¹ç¤Ï¡¢0777 & ~umask ¤ÇŸ³«¤¹¤ë¤è¤¦¤Ë¤·¤Þ¤·¤¿¡£¤³¤ì¤Ï¡¢.exe - ¤ä .dll ¤Ë¼Â¹Ô°À­¤òÉÕ¤±¤ë¤¿¤á¤Ç¤¹¡£ - -¡¦large files Âбþ - - ¥·¥¹¥Æ¥à¤¬Âбþ¤·¤Æ¤¤¤ì¤Ð¡¢2G over ¤Ê large file ¤ò°·¤¦¤³¤È¤¬¤Ç¤­¤Þ¤¹ - (configure ¤¬Å¬Åö¤Ê¥³¥ó¥Ñ¥¤¥é¥ª¥×¥·¥ç¥ó¤ò»ØÄꤷ¤Æ¤¯¤ì¤Þ¤¹) - - ¤¿¤À¤·¡¢HP-UX 11.0 ¤Ç large files ¤ËÂбþ¤¹¤ë¤Ë¤Ï°Ê²¼¤Î¤è¤¦¤Ë - - CC="cc -Ae +DA2.0W" - - ¤È»Ø¼¨¤·¤Æ¤¢¤²¤ëɬÍפ¬¤¢¤ë¤è¤¦¤Ç¤¹¡£ - - ./configure --with-tmp-file=no CC="cc -Ae +DA2.0W" \ - ac_cv_have_mktime=yes \ - ac_cv_func_mktime=yes - - --with-tmp-file=no ¤Ï¡¢Ãæ´Ö¥Õ¥¡¥¤¥ë¤ò½ÐÎÏÀè¤ÈƱ¤¸¥Ç¥£¥ì¥¯¥È¥ê¤Ë - ºîÀ®¤·¤Þ¤¹¡£¥Æ¥ó¥Ý¥é¥ê¥Ç¥£¥ì¥¯¥È¥ê¤¬ 2G over ¤ò¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤Ê¤¤ - ¾ì¹ç¤ò¹Íθ¤·¤Æ¤¤¤Þ¤¹¡£ - - (largefiles Âбþ¤È¤Ï´Ø·¸¤¢¤ê¤Þ¤»¤ó¤¬ ac_cv_*=yes ¤Ï¡¢HP-UX ¤Ç¤Ï¤Ê¤¼ - ¤« mktime ¤ÎȽÄê¤Ë¼ºÇÔ¤¹¤ë¤¿¤á¶¯À©Åª¤Ë mktime ¤ò»È¤¦¤è¤¦¤Ë¤·¤Æ¤¤¤Þ¤¹) - - ¤â¤· large files Âбþ¤ò¡Ö̵¸ú¡×¤Ë¤·¤¿¤±¤ì¤Ð¡¢ - - ./configure --disable-largefile - - ¤Î¤è¤¦¤Ë¤·¤Þ¤¹¡£ - - ¤Ê¤ª¡¢Àµµ¬¤Î LHA ¤Ç¤Ï¡¢level 0, 1, 2 ¥Ø¥Ã¥À¤Î»ÅÍ;å 4G ̤Ëþ¤Î¥Õ¥¡¥¤¥ë - ¤·¤«½ñ¸Ë¤Ë³ÊǼ¤Ç¤­¤Þ¤»¤ó(¥Õ¥¡¥¤¥ë¥µ¥¤¥º¤ò³ÊǼ¤¹¤ëÎΰ褬 4 bytes ¤·¤«¤Ê¤¤)¡£ - - ¤·¤«¤·¡¢UNLHA32.DLL ¤Ê¤É¤Ï¡¢³ÈÄ¥¥Ø¥Ã¥À(0x42)¤Ë¤è¤ê¡¢4G over ¤Ê¥Õ¥¡¥¤ - ¥ë¤â°·¤¨¤ë¤è¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤Þ¤¹¡£autoconf ÈǤǤϡ¢º£¤Î¤È¤³¤íŸ³«¤Î¤È¤­¤Î - ¤ß¤³¤Î³ÈÄ¥¥Ø¥Ã¥À¤ò»²¾È¤·¤Æ¡¢4G over¥Õ¥¡¥¤¥ë¤ò¥µ¥Ý¡¼¥È¤·¤Þ¤¹¡£ - - (ºîÀ®¤ËÂбþ¤·¤Æ¤¤¤Ê¤¤¤Î¤Ï¡¢°Â°×¤ËUNLHA32.DLL¤Ë½¾¤¦¤³¤È¤¬Àµ¤·¤¤¤Î¤«¤è - ¤¯¤ï¤«¤é¤Ê¤«¤Ã¤¿¤«¤é¤Ç¤¹¡£) - -¡¦MacBinary¤Ä¤­¥¢¡¼¥«¥¤¥Ö¤Î¥µ¥Ý¡¼¥È - - MacLHA¤Ç¡ÖMacBinary¡×¥Á¥§¥Ã¥¯¥Ü¥Ã¥¯¥¹¤òON¤Ë¤·¤ÆºîÀ®¤·¤¿¥¢¡¼¥«¥¤¥Ö¤Ë - ³ÊǼ¤µ¤ì¤Æ¤¤¤ë¥Õ¥¡¥¤¥ë¤Ï¡¢MacBinary¥¨¥ó¥³¡¼¥É¤µ¤ì¤Æ¤¤¤Þ¤¹¡£ - ²òÅà»þ¤Ë -b ¥ª¥×¥·¥ç¥ó¤ò»ØÄꤹ¤ë¤È¡¢²òÅà¸å¤ËMacBinary¥Ç¥³¡¼¥É¤ò¹Ô¤¤¡¢ - ¥Ç¡¼¥¿¥Õ¥©¡¼¥¯¤Î¤ß¤ò¼è¤ê½Ð¤·¤Þ¤¹¡Ê¥ê¥½¡¼¥¹¥Õ¥©¡¼¥¯¤Ï̵»ë¤µ¤ì¤Þ¤¹¡Ë¡£ - ¤Þ¤¿¡¢ÉáÄ̤Υ¢¡¼¥«¥¤¥Ö¤ËÂФ·¤Æ b ¥ª¥×¥·¥ç¥ó¤ò»ØÄꤷ¤Æ²òÅष¤¿¾ì¹ç¤Ï - Ä̾ï¤Î²òÅà½èÍý¤¬¹Ô¤ï¤ì¤Þ¤¹¡£ - - ¤³¤Îµ¡Ç½¤òÍøÍѤ¹¤ë¤Ë¤Ïapplefile¥é¥¤¥Ö¥é¥ê¤¬É¬ÍפǤ¹¡£ - applefile¥é¥¤¥Ö¥é¥ê¤Ï²¼µ­¤è¤êÆþ¼ê¤Ç¤­¤Þ¤¹¡£ - - http://sourceforge.net/projects/applefile/ - -3. ¥Ð¥° -======= - -¡¦²õ¤ì¤¿¥¢¡¼¥«¥¤¥Ö¤ÎŸ³« (--extract-broken-archive) - - LHa for UNIX (autoconfÈÇ)¤Ï¡¢¥Ð¡¼¥¸¥ç¥ó 1.14i-ac20030713 (slide.c - revision 1.20) ¤è¤ê²õ¤ì¤¿¥¢¡¼¥«¥¤¥Ö¤òºîÀ®¤·¤Æ¤·¤Þ¤¦Ã×̿Ū¤Ê¥Ð¥°¤¬¤¢ - ¤ê¤Þ¤·¤¿¡£(¤³¤Î¤è¤¦¤Ê¥¢¡¼¥«¥¤¥Ö¤¬ºîÀ®¤µ¤ì¤ë¸½¾Ý¤ËÁø¶ø¤¹¤ë¤³¤È¤Ï¤Û¤È - ¤ó¤É¤Ê¤¤¤«¤âÃΤì¤Þ¤»¤ó¡£¤¿¤À¡¢¥Ð¥°¤Î¤¢¤ë LHa for UNIX ¤Ç¤ÏÀµ¾ï¤ËŸ³« - ¤Ç¤­¤Æ¤·¤Þ¤¦¤Î¤Ç¡¢²õ¤ì¤¿¥¢¡¼¥«¥¤¥Ö¤Ç¤¢¤ë¤³¤È¤Ëµ¤¤¬ÉÕ¤¤¤Æ¤Ê¤¤¤À¤±¤«¤â - ¤·¤ì¤Þ¤»¤ó) - - ¤³¤Î²õ¤ì¤¿¥¢¡¼¥«¥¤¥Ö¤Ï¾¤ÎÀµ¾ï¤Ê LHA (¤¢¤ë¤¤¤Ï¸½ºß¤Î LHa for UNIX) - ¤Ç¤ÏŸ³«»þ¤Ë CRC ¥¨¥é¡¼¤¬È¯À¸¤·¤Æ¤·¤Þ¤¤¤Þ¤¹¡£ - - ¸½ºß¤Î¥Ð¡¼¥¸¥ç¥ó¤Ç¤Ï¡¢ - - lha x --extract-broken-archive broken.lzh - - ¤È¥ª¥×¥·¥ç¥ó --extract-broken-archive ¤ò»ØÄꤹ¤ë¤³¤È¤Ç¡¢¤³¤Î¥Ð¥°¤Ë¤è - ¤êºîÀ®¤µ¤ì¤¿²õ¤ì¤¿¥¢¡¼¥«¥¤¥Ö¤ò¶¯À©Åª¤ËŸ³«¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£CRC ¥¨ - ¥é¡¼¤¬È¯À¸¤¹¤ë¥¢¡¼¥«¥¤¥Ö¤ò¸«ÉÕ¤±¤¿¤È¤­¤Ë¤Ï(¤½¤·¤Æ¡¢¤½¤ì¤¬²áµî¤Î LHa - for UNIX (autoconfÈÇ)¤ÇºîÀ®¤µ¤ì¤¿¤â¤Î¤Ç¤¢¤ë¾ì¹ç¤Ë¤Ï)¤³¤Î¥ª¥×¥·¥ç¥ó¤ò - »î¤·¤Æ¤ß¤Æ¤¯¤À¤µ¤¤¡£ - -4. ºÆÇÛÉۤˤĤ¤¤Æ -================= - - »ä¤Ï¡¢src/header.c ¤Ë¤Ï¤â¤Ï¤ä¸µ¤Î lha 1.14i ¤Ë¤¢¤Ã¤¿¥³¡¼¥É¤Ï´Þ¤Þ¤ì¤Æ - ¤¤¤Ê¤¤¤È¹Í¤¨¤Æ¤¤¤Þ¤¹¡£src/header.c ¤Ï»ä¤ÎÃøºîʪ¤Ç¤¹¡£¤¿¤À¤·¡¢lha - 1.14i ¤Ë¤¢¤Ã¤¿¥³¡¼¥É¤¬»²¹Í¤Ë¤Ê¤Ã¤¿¤³¤È¤Ï»ö¼Â¤Ç¤¹¡£·É°Õ¤òɽ¤¹¤ë°ÕÌ£¤Ç - ¤â src/header.c ¤Ë¤¢¤Ã¤¿ÎòÂå¤Î²þÊѼԤÎ̾Á°¤Ï¤½¤Î¤Þ¤Þ»Ä¤·¤Æ¤¤¤Þ¤¹¡£ - - ¤³¤ì¤Î°Õ¿Þ¤¹¤ë½ê¤Ï¡¢man/lha.man ¤Ë¤¢¤ëºÆÇÛÉÛ¾ò·ï¤ò src/header.c ¤ËŬ - ÍѤ·¤Ê¤¤¤³¤È¤Ç¤¹¡£»ä¤Ï¡¢¤è¤ê¼«Í³¤Ç»È¤¤¤ä¤¹¤¤¥³¡¼¥É¤ò LHa for UNIX ¤Î - ¹½À®Êª¤Ë¤·¤è¤¦¤È¹Í¤¨¤Æ¤¤¤Þ¤¹¡£src/header.c ¤ËŬÍѤ¹¤ë¥é¥¤¥»¥ó¥¹¤Ï¤Þ - ¤À̤Äê¤Ç¤¹(¤Ê¤Î¤Ç¡¢¸½¾õ¤Ï LHa for UNIX ¤ÎºÆÇÛÉÛ¾ò·ï¤¬Å¬ÍѤµ¤ì¤ë¤È¹Í - ¤¨¤Æ¤¯¤À¤µ¤¤)¡£ - - ¥é¥¤¥»¥ó¥¹¸õÊä(¥á¥â): - - - - ¸½ºß¤Î¤È¤³¤í¡¢src/vsnprintf.c, src/fnmatch.[ch], src/getopt_long.[ch] - ¤ò½ü¤¯¤½¤Î¾¤Î¹½À®Êª¤ä LHa for UNIX Á´ÂÎ¤Ë¤Ï man/lha.man ¤Ë´Þ¤Þ¤ì¤ë - ¾ò¹à¤¬Å¬ÍѤµ¤ì¤Þ¤¹¡£(vsnprintf.c, fnmatch.c ¤òÍøÍѤ¹¤ë¤è¤¦¤Ë make ¤· - ¤¿ LHa for UNIX ¤Ë¤Ï¡¢³Æ¥½¡¼¥¹¤Ëµ­½Ò¤µ¤ì¤¿¾ò¹à¤âŬÍѤµ¤ì¤ë¤³¤È¤ËÃí°Õ - ¤·¤Æ¤¯¤À¤µ¤¤) diff --git a/Hacking_of_LHa b/Hacking_of_LHa index 37343d9..a98a501 100644 --- a/Hacking_of_LHa +++ b/Hacking_of_LHa @@ -5,127 +5,127 @@ $Id$ Koji Arai -Ëܽñ¤Ï¡¢LHa for UNIX 1.14i ¤Î¥½¡¼¥¹¤ò²òÆɤ·¡¢¤½¤Î°µ½Ì¥¢¥ë¥´¥ê¥º¥à¤Î¼Â -Áõ¤ò³Îǧ¤¹¤ë¤¿¤á¤Î¤â¤Î¤À¡£¤³¤ÎÀ®²Ì¤ÏÊ̤ηÁ¤Ç¤Þ¤È¤á¤Ê¤ª¤µ¤ì»ñÎÁ¤Ë¤Ê¤ë¤« -¤â¤·¤ì¤Ê¤¤¤·¡¢¤³¤Î¤Þ¤Þ¤Î·Á¤ÇÊݴɤµ¤ì¤ë¤«¤â¤·¤ì¤Ê¤¤¤·¡¢¿·¤¿¤Ë¥½¡¼¥¹¤ò½ñ -¤­µ¯¤³¤¹¸µ¤Ë¤Ê¤ë¤«¤â¤·¤ì¤Ê¤¤¡£¤È¤Ë¤«¤¯¡¢²Ë¤ÊÀµ·îµÙ¤ß¤òÄÙ¤¹¤¿¤á¤Ë¤ä¤Ã¤Æ -¤ß¤¿¤À¤±¤Î¤â¤Î¤À¡£(µÙ¤ß¤¬ÌÀ¤±¤ë¤È¤Þ¤¿Ë»¤·¤¯¤Ê¤ë¤Î¤Ç¡¢¤³¤ì°Ê¾å¤Þ¤Ã¤¿¤¯ -²¿¤â¤·¤Ê¤¤¤«¤â¤·¤ì¤Ê¤¤) - -Ëܽñ¤Ï¡¢¤Þ¤À̤´°À®¤Ç¤¢¤ë¡£¤Ë¤â¤«¤«¤ï¤é¤º¸ø³«¤¹¤ë¤Î¤Ï¤³¤ì°Ê¾å³¤«¤Ê¤¤¤« -¤â¤·¤ì¤Ê¤¤¤«¤é¤Ç¤¢¤ë(µ¤¤¬¸þ¤¤¤¿¤é¤Þ¤¿Â³¤­¤ò½ñ¤¯¤À¤í¤¦¡£¤¢¤ë¤¤¤Ï±þ±ç¤Î -¤ª¼ê»æ¤¬¤¯¤ì¤Ð¤ä¤ëµ¤¤¬½Ð¤ë¤«¤â¤·¤ì¤Ê¤¤)¡£ - -Ëܽñ¤Ï¥Õ¥ê¡¼¤Ç¤¢¤ë¡£Ê£À½¡¢²þÊÑ¡¢ºÆÇÛÉۤϼ«Í³¤Ç¤¢¤ë¤È¤¤¤¦¤³¤È¤À¡£¤¿¤À¤· -Ëܽñ¤Ë¤è¤êÀ¸¤¸¤¿¤¢¤é¤æ¤ë»³²¡¢ÉÔÍø±×¤ËÂФ·¤Æ¤Ï°ìÀÚ¤ÎÊݾڤϤʤ¤¡£Ëܽñ¤Ë -¤Ï±³¤¬¤¢¤ë¤«¤â¤·¤ì¤Ê¤¤¡£¤½¤ì¤ËÂФ·¤Æ±³¤ò¶µ¤¨¤é¤ì¤¿¤ÈÃø¼Ô¤òÈòÆñ¤ò¤·¤Ê¤¤ -¤Ç夭¤¿¤¤¡£¤·¤«¤·´Ö°ã¤¤¤Î»ØŦ¤Ï¹½¤ï¤Ê¤¤(¤¼¤Ò¤ª´ê¤¤¤·¤¿¤¤)¡¢Ãø¼Ô¤Ï°µ½Ì -½èÍý¤Ë´Ø¤·¤Æ¤Ï̵ÃΤǤ¢¤ë¡£ÍѸì¤Î»È¤¤ÊýÅù¤ÏŬÀڤǤʤ¤¤«¤â¤·¤ì¤Ê¤¤¤Î¤Ç¤³ -¤ÎÊýÌ̤Ǥâ¸æ»ØƳ失¤ì¤Ð¹¬¤¤¤Ç¤¢¤ë¡£ - -< Ìܼ¡ > - -ɽµ­¤Ë¤Ä¤¤¤Æ -slide ¼­½ñË¡ (slide.c) -bit Æþ½ÐÎϥ롼¥Á¥ó (crcio.c) -Huffman Ë¡ (huf.c) -LHA ¥Õ¥¡¥¤¥ë¤Î¹½Â¤(¤Þ¤È¤á) -¥»¥­¥å¥ê¥Æ¥£¥Ð¥°¤Î¹Í»¡ +本書は、LHa for UNIX 1.14i のソースを解読し、その圧縮アルゴリズムの実 +装を確認するためのものだ。この成果は別の形でまとめなおされ資料になるか +もしれないし、このままの形で保管されるかもしれないし、新たにソースを書 +き起こす元になるかもしれない。とにかく、暇な正月休みを潰すためにやって +みただけのものだ。(休みが明けるとまた忙しくなるので、これ以上まったく +何もしないかもしれない) + +本書は、まだ未完成である。にもかかわらず公開するのはこれ以上続かないか +もしれないからである(気が向いたらまた続きを書くだろう。あるいは応援の +お手紙がくればやる気が出るかもしれない)。 + +本書はフリーである。複製、改変、再配布は自由であるということだ。ただし +本書により生じたあらゆる損害、不利益に対しては一切の保証はない。本書に +は嘘があるかもしれない。それに対して嘘を教えられたと著者を避難をしない +で頂きたい。しかし間違いの指摘は構わない(ぜひお願いしたい)、著者は圧縮 +処理に関しては無知である。用語の使い方等は適切でないかもしれないのでこ +の方面でも御指導頂ければ幸いである。 + +< 目次 > + +表記について +slide 辞書法 (slide.c) +bit 入出力ルーチン (crcio.c) +Huffman 法 (huf.c) +LHA ファイルの構造(まとめ) +セキュリティバグの考察 =============================================================================== -ɽµ­¤Ë¤Ä¤¤¤Æ +表記について -* ´Ø¿ô¤Ï¡¢¤½¤ÎÄêµÁ¥½¡¼¥¹ file.c ¤È´Ø¿ô̾ func() ¤ò¼¨¤¹¤Î¤Ë +* 関数は、その定義ソース file.c と関数名 func() を示すのに file.c:func() - ¤È¤¤¤¦µ­½Ò¤ò»È¤¦ + という記述を使う -* ÇÛÎó¤Îź»ú¤Ï¡¢Python¸À¸ì¤Î¥¹¥é¥¤¥¹±é»»»Ò¤Îµ­Ë¡¤Ë½à¤¸¤¿ +* 配列の添字は、Python言語のスライス演算子の記法に準じた - a[m:n] ¤Ï¡¢m <= i < m+n ¤ÎÈϰϤΠa[i] ¤ò°ÕÌ£¤¹¤ë¡£ + a[m:n] は、m <= i < m+n の範囲の a[i] を意味する。 -* ÃͤÎÈϰϤϡ¢Ruby¸À¸ì¤ÎÈϰϱ黻»Ò¤Îµ­Ë¡¤Ë½à¤¸¤¿¡£¤³¤ì¤òÇÛÎó¤Î - ź»ú¤Ë»ÈÍѤ¹¤ë¾ì¹ç¤â¤¢¤ë¡£ +* 値の範囲は、Ruby言語の範囲演算子の記法に準じた。これを配列の + 添字に使用する場合もある。 m <= i <= n -> i = m..n m <= i < n -> i = m...n - a[m..n] ¤Ï¡¢m <= i <= n ¤ÎÈϰϤΠa[i] ¤ò°ÕÌ£¤¹¤ë¡£ + a[m..n] は、m <= i <= n の範囲の a[i] を意味する。 -* m ¤Î n ¾è ¤Ï¡¢m^n ¤Çɽ¤¹¡£^ ¤Ï¡¢ÇÓ¾ŪÏÀÍýϤȤ·¤Æ¤âÍøÍѤµ¤ì¤ë¤¬ - ʸ̮¤«¤éȽÃǤ·¤Æ¤â¤é¤¦¡£ +* m の n 乗 は、m^n で表す。^ は、排他的論理和としても利用されるが + 文脈から判断してもらう。 -* v{n} ¤Ï¡¢ÊÑ¿ô v ¤ÎÃͤ¬ n ¤Ç¤¢¤ë¤³¤È¤òɽ¤¹¡£n ¤Ï¡¢¥µ¥ó¥×¥ë¤ÎÃͤǤ¢¤Ã¤¿¤ê - Äê¿ô¤ÎÃͤǤ¢¤Ã¤¿¤ê¤¹¤ë¡£ +* v{n} は、変数 v の値が n であることを表す。n は、サンプルの値であったり + 定数の値であったりする。 - v=n ¤ÏÂåÆþʸ + v=n は代入文 - ÇÛÎó¤ÎÆâÍƤϡ¢ + 配列の内容は、 ary[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 } - ¤Î¤è¤¦¤Ë½ñ¤¯ + のように書く -o ÍѸì¤Ë¤Ä¤¤¤Æ +o 用語について -* Éä¹æ - Éä¹æ²½¡¢Éä¹æ¸ì¡¢°µ½Ìʸ +* 符号 + 符号化、符号語、圧縮文 - Éä¹æ¸ì¤Ï¡¢1¤Ä¤Îʸ»ú¤òÉä¹æ²½¤·¤¿·ë²Ì¡£°µ½Ìʸ¤ÏÉä¹æ¸ì¤Îʤӡ£ + 符号語は、1つの文字を符号化した結果。圧縮文は符号語の並び。 -* Éü¹æ - Éü¹æ²½¡¢Éü¹æ¸ì¡¢Éü¹æʸ +* 復号 + 復号化、復号語、復号文 - Éü¹æ¸ì¤Ï¡¢°µ½Ìʸ¤«¤é1¤Ä¤Îʸ»ú¤òÉü¹æ²½¤·¤¿·ë²Ì¡£Éü¹æʸ¤ÏÉü¹æ¸ì¤Îʤӡ£ + 復号語は、圧縮文から1つの文字を復号化した結果。復号文は復号語の並び。 -* ʿʸ - °µ½ÌÁ°¤Îʸ¤ò¼¨¤¹¡£ÂФ·¤ÆÉü¹æʸ¤Ï¡¢Éü¹æ¤·¤¿¸å¤Îʸ¤ò°ÕÌ£¤¹¤ë¡£ +* 平文 + 圧縮前の文を示す。対して復号文は、復号した後の文を意味する。 -* slide ¼­½ñË¡ +* slide 辞書法 -* Huffman Ë¡ - ưŪ Huffman Ë¡¡¢ÀÅŪ Huffman Ë¡ +* Huffman 法 + 動的 Huffman 法、静的 Huffman 法 =============================================================================== -slide ¼­½ñË¡ (slide.c) +slide 辞書法 (slide.c) ---------------------- -¤Þ¤º¡¢¹½Â¤¤Ë¤Ä¤¤¤Æ¹Í¤¨¤ë¡£ +まず、構造について考える。 -slide¼­½ñË¡¤Ï¡¢encoding ¤Ë¤µ¤Þ¤¶¤Þ¤Ê¹©Éפ¬¶Å¤é¤µ¤ì¤ë¤Î¤Ç¤È¤Æ¤âÊ£»¨¤À¤¬¡¢ -ÉáÄÌ decoding ¤Ïñ½ã¤Ç¤¢¤ë¡£decoding ¤ò²òÀϤ¹¤ë¤³¤È¤Ç¤É¤Î¤è¤¦¤Ê°µ½Ìʸ -¤òºî¤Ã¤Æ¤¤¤ë¤Î¤«¤òÄ´¤Ù¤Æ¤ß¤ë¡£ +slide辞書法は、encoding にさまざまな工夫が凝らされるのでとても複雑だが、 +普通 decoding は単純である。decoding を解析することでどのような圧縮文 +を作っているのかを調べてみる。 -decoding ¤ò¹Ô¤¦½èÍý¤Ï¡¢slide.c ¤Î decode() ¤Ç¤¢¤ë¡£¤³¤Î½èÍý¤ò¸«¤Æ¤ß¤ë -¤È»×¤Ã¤¿Ä̤ê´Êñ¤Ë²òÆɤǤ­¤¿(°Ê²¼) +decoding を行う処理は、slide.c の decode() である。この処理を見てみる +と思った通り簡単に解読できた(以下) - 1. huffman coding ¤Ë¤è¤êÉü¹æ¤·¤¿Ê¸»ú¤ò´Ä¾õ¥Ð¥Ã¥Õ¥¡ dtext ¤Ë½ñ¤­¹þ¤à - Ä̾ï¤Îʸ»ú c ¤Ï¡¢c < 256 ¤Çɽ¸½¤µ¤ì¤Æ¤¤¤ë(¤Ä¤Þ¤ê¤½¤Î¤Þ¤Þ) + 1. huffman coding により復号した文字を環状バッファ dtext に書き込む + 通常の文字 c は、c < 256 で表現されている(つまりそのまま) - 2. Ä̾ï¤Îʸ»ú¤Ç¤Ê¤¤¤â¤Î¤¬¸½¤ì¤¿¤é¡¢¤½¤ì¤ÏŤµ¤Ç¤¢¤ë¡£Ä¹¤µ len ¤Ï¡¢ - len > 255 ¤Çɽ¸½¤µ¤ì¤Æ¤¤¤ë¡£len ¤«¤é 0xfd(253) ¤ò°ú¤¤¤¿Ãͤ¬ - ¼ÂºÝ¤ÎŤµ¤òɽ¤¹(-lzs- method ¤Î¾ì¹ç¤Ï¡¢0xfe(254)¤ò°ú¤¯) - ¡ÖŤµ¡×¤¬¡¢¸½¤ì¤¿¤é¤½¤Îľ¸å¤Ë¤Ï¡Ö°ÌÃ֡פ¬½ñ¤«¤ì¤Æ¤¤¤ë¤Î¤Ç¤½¤ì¤ò - Æɤࡣ¤³¤¦¤·¤Æ¡¢Ä¹¤µ¤È°ÌÃ֤Υڥ¢¤òÆÀ¤ë + 2. 通常の文字でないものが現れたら、それは長さである。長さ len は、 + len > 255 で表現されている。len から 0xfd(253) を引いた値が + 実際の長さを表す(-lzs- method の場合は、0xfe(254)を引く) + 「長さ」が、現れたらその直後には「位置」が書かれているのでそれを + 読む。こうして、長さと位置のペアを得る - dtext ¤«¤é pt+1 ¥Ð¥¤¥ÈÁ°¤Î len ¥Ð¥¤¥È¤òÆɤߡ¢dtext ¤ËÄɲäǽñ¤­¹þ¤à + dtext から pt+1 バイト前の len バイトを読み、dtext に追加で書き込む - 3. dtext ¤¬°ìÇÕ(dicsiz)¤Ë¤Ê¤Ã¤¿¤é¥Õ¥¡¥¤¥ë¤Ë½ñ¤­½Ð¤¹ + 3. dtext が一杯(dicsiz)になったらファイルに書き出す -¤³¤ì¤Î·«¤êÊÖ¤·¤Ç¤¢¤ë¡£¤Ä¤Þ¤ê¡¢slide ¼­½ñË¡¤Î°µ½Ìʸ¤Ï¡¢Ê¸»ú c ¤È ¤ÎʤӤǤ¢¤ë¤³¤È¤¬¤ï¤«¤ë¡£Î㤨¤Ð¡¢Ê¸»úÎó c1 c2 c1 c2 ¤Ï¡¢°Ê²¼¤Î¤è -¤¦¤Ëɽ¸½¤µ¤ì¤Æ¤¤¤ë¤Ï¤º¤Ç¤¢¤ë¡£(ËÜÅö¤Ï¡¢Ä¹¤µ¤¬ 2 °Ê²¼¤Ç¤Ï°µ½Ì¤¬µ¯¤³¤é¤Ê -¤¤¤Î¤Çʿʸ¤Î¤Þ¤Þ½ÐÎϤ¹¤ë¡£Ä¹¤µ¤ÏºÇÄã¤Ç¤â 3 ¤ÏɬÍ×) +これの繰り返しである。つまり、slide 辞書法の圧縮文は、文字 c と の並びであることがわかる。例えば、文字列 c1 c2 c1 c2 は、以下のよ +うに表現されているはずである。(本当は、長さが 2 以下では圧縮が起こらな +いので平文のまま出力する。長さは最低でも 3 は必要) +----+----+--------+ | c1 | c2 | <2, 1> | +----+----+--------+ -¤Ç¤Ï¡¢¤³¤Î¹½Â¤¤òºîÀ®¤¹¤ë°µ½Ì½èÍý¤Ë¤Ä¤¤¤Æ¹Í¤¨¤ë¡£slide ¼­½ñË¡¤Ç¤Ï¡¢¥Õ¥¡ -¥¤¥ë¤«¤éÆɤ߹þ¤ó¤Àʸ»úÎó token ¤¬¡¢°ÊÁ°¤ËÆɤ߹þ¤ó¤À¼­½ñ¤Ë¸ºß¤¹¤ì¤Ð - ¤Î¥Ú¥¢¤ò½ÐÎϤ·¡¢Â¸ºß¤·¤Ê¤±¤ì¤Ð token ¤ò¤½¤Î¤Þ¤Þ½ÐÎϤ¹¤ë¡£ÆÉ -¤ß¹þ¤ó¤À token ¤Ï¡¢¼­½ñ¤ËÄɲä·¡¢¼­½ñ¤Î¸ì¤¬°î¤ì¤¿¤é¸Å¤¤¾ðÊó¤ò¼Î¤Æ¤ë¡£ +では、この構造を作成する圧縮処理について考える。slide 辞書法では、ファ +イルから読み込んだ文字列 token が、以前に読み込んだ辞書に存在すれば + のペアを出力し、存在しなければ token をそのまま出力する。読 +み込んだ token は、辞書に追加し、辞書の語が溢れたら古い情報を捨てる。 -²¿¤âͽÈ÷Ã챤¬¤Ê¤¤¾õÂ֤ǽñ¤±¤Ð +何も予備知識がない状態で書けば while (read_file(&token, tokensiz)) { len = search_dict(dict, token, &pt); @@ -136,32 +136,32 @@ pt> update_dict(dict, token); } -¤Î¤è¤¦¤Ë¤Ê¤ë¤Ï¤º¡£¤³¤³¤Ç¡¢tokensiz ¤Ï token ¤ÎºÇÂ祵¥¤¥º¤Ç¡¢ºÇĹ°ìÃ×Ĺ -¤òɽ¤¹¡£¤³¤ÎÃͤ¬Â礭¤±¤ì¤ÐÂ礭¤¤Äø¡¢°µ½Ì¸úΨ¤ÏÎɤ¯¤Ê¤ë¤Ï¤º¤Ç¡¢lha ¤Ç¤Ï¡¢ -¤³¤ì¤Ï MAXMATCH{256}¤Ç¤¢¤ë¡£¤Þ¤¿¡¢dict ¤Ï¼­½ñ¤Ç¤³¤Î¥µ¥¤¥º¤Ï lha ¤Î --lh5- ¥á¥½¥Ã¥É¤Ç¤Ï¡¢8192 ¤È¤Ê¤Ã¤Æ¤¤¤ë¡£¤³¤Î¼­½ñ¤âÂ礭¤±¤ì¤ÐÂ礭¤¤ÄøÎÉ -¤¤¤Ï¤º¤À¡£¤½¤ÎÊý¤¬°ìÃ×ʸ»úÎ󤬸«¤Ä¤«¤ê¤ä¤¹¤¤¡£(¤¿¤À¤·¡¢¼­½ñ¤¬Â礭¤¤¤È -°ìÃ×°ÌÃÖ¤ò¼¨¤¹¾ðÊó ¤Î¾ðÊóÎ̤¬Áý¤¨¤ë¤Ï¤º¤À¤·¡¢Â®ÅÙ¤âÃÙ¤¯¤Ê¤ë -¤À¤í¤¦¡£¸å¤Ç¸¡¾Ú¤¹¤ë) - -¤Ç¡¢¼ÂºÝ¤Ë¥½¡¼¥¹¤ò¸«¤Æ¤ß¤ë¤È(slide.c:encode())¡¦¡¦¡¦¡¢¤Þ¤Ã¤¿¤¯¤³¤Î¤è¤¦ -¤Ê¹½Â¤¤Ë¤Ï¤Ê¤Ã¤Æ¤Ê¤¤¤è¤¦¤Ë¸«¤¨¤ë¡£²¿¤ä¤é¤ä¤ä¤³¤·¤¤¤³¤È¤Ð¤«¤ê¤Ç¤Þ¤Ã¤¿¤¯ -¤ï¤«¤é¤Ê¤¤¡£¤Ê¤¼¤³¤³¤Þ¤Ç¤ä¤ä¤³¤·¤¤¤Î¤«¤Èµã¤­¤¿¤¯¤Ê¤Ã¤Æ¤¯¤ë¤¬¡¢¤½¤ì¤Ï® -Å٤Τ¿¤á¤Ç¤¢¤ë(ËÜÅö¡©)¡£¾åµ­¤Î¥³¡¼¥É¤Ç¡¢search_dict() ¤Ï¡¢Ã±¤Ë dict ¤« -¤é token ¤Ë°ìÃפ¹¤ë°ÌÃÖ¤ò¸¡º÷¤¹¤ë¤À¤±¤ÇÎɤµ¤½¤¦(¼ÂºÝ¤Ë¤½¤ì¤Ç¤âÎɤ¤)¤À -¤¬¡¢¤³¤ì¤Ç¤Ï¤Þ¤Ã¤¿¤¯Â®ÅÙ¤¬½Ð¤Ê¤¤¡£¤³¤Î¤¢¤¿¤ê¤Î¹©Éפ¬ slide ¼­½ñË¡¤Î¥­ -¥â¤Ç¤¢¤ë¡£ - -¤½¤¦¤¤¤¦¤ï¤±¤Ç¡¢¤³¤ÎÉôʬ¤òÆɤ߲ò¤¯¤³¤È¤Ë¤¹¤ë¡£¤Ê¤ª¡¢Í½È÷Ãμ±¤È¤·¤Æ lha -¤Ç¤Ï¡¢¼­½ñ¤«¤é token ¤òõ¤¹¤Î¤Ë¥Ï¥Ã¥·¥å¤¬»È¤ï¤ì¤Æ¤¤¤ë¤é¤·¤¤¤³¤È¤òµ­¤· -¤Æ¤ª¤¯¡£ - -¤³¤³¤Ç¤Ï¼ÂºÝ¤Ë¥Ç¥Ð¥Ã¥¬¤ÇÆ°ºî¤µ¤»¤Ê¤¬¤é²òÆɤ¹¤ë¤Î¤Ç¤Ï¤Ê¤¯¡¢¥½¡¼¥¹¤òÆɤà -¤À¤±¤ÇÍý²ò¤Ç¤­¤ë¤«¤ò»î¤¹¤³¤È¤Ë¤¹¤ë¡£¤Þ¤¿¡¢ËÜʸ¤ÏË¿½ñ(Ææ)¤Î¥Î¥ê¤ò¥Þ¥Í¤Æ -¤¤¤ë¤È»ØŦ¤¹¤ëÊý¤¬¤¤¤ë¤«¤â¤·¤ì¤Ê¤¤¡¦¡¦¡¦¤¬¤Þ¤Ã¤¿¤¯¤½¤ÎÄ̤ê¤À¡£ - -¤Þ¤º¡¢¤½¤Î¤â¤Î¤º¤Ð¤ê¤Î encode() (slide.c) ¤ò¸«¤ë¡£°Ê²¼¤¬¤³¤Î´Ø¿ô¤À¤¬ -½èÍý¤ÎÍ×ÅÀ¤À¤±¤ËÃåÌܤ¹¤ë¤¿¤á¤ËÉÔÍפ½¤¦¤ÊÉôʬ¤Ï(¸½»þÅÀ¤Çͽ¬¤Ç)ºï¤Ã¤¿¡£ +のようになるはず。ここで、tokensiz は token の最大サイズで、最長一致長 +を表す。この値が大きければ大きい程、圧縮効率は良くなるはずで、lha では、 +これは MAXMATCH{256}である。また、dict は辞書でこのサイズは lha の +-lh5- メソッドでは、8192 となっている。この辞書も大きければ大きい程良 +いはずだ。その方が一致文字列が見つかりやすい。(ただし、辞書が大きいと +一致位置を示す情報 の情報量が増えるはずだし、速度も遅くなる +だろう。後で検証する) + +で、実際にソースを見てみると(slide.c:encode())・・・、まったくこのよう +な構造にはなってないように見える。何やらややこしいことばかりでまったく +わからない。なぜここまでややこしいのかと泣きたくなってくるが、それは速 +度のためである(本当?)。上記のコードで、search_dict() は、単に dict か +ら token に一致する位置を検索するだけで良さそう(実際にそれでも良い)だ +が、これではまったく速度が出ない。このあたりの工夫が slide 辞書法のキ +モである。 + +そういうわけで、この部分を読み解くことにする。なお、予備知識として lha +では、辞書から token を探すのにハッシュが使われているらしいことを記し +ておく。 + +ここでは実際にデバッガで動作させながら解読するのではなく、ソースを読む +だけで理解できるかを試すことにする。また、本文は某書(謎)のノリをマネて +いると指摘する方がいるかもしれない・・・がまったくその通りだ。 + +まず、そのものずばりの encode() (slide.c) を見る。以下がこの関数だが +処理の要点だけに着目するために不要そうな部分は(現時点で予測で)削った。 unsigned int encode() @@ -220,35 +220,35 @@ encode() } } -¤Þ¤º¡¢¤³¤Î´Ø¿ô¤«¤é³µ´Ñ¤ò¸«¤Æ¤ß¤ë¤È¡¢¥ë¡¼¥×¤ÎÁ°¤Ë½é´ü²½½èÍý¤È¤·¤Æ -°Ê²¼¤¬¹Ô¤ï¤ì¤Æ¤¤¤ë¡£ +まず、この関数から概観を見てみると、ループの前に初期化処理として +以下が行われている。 -(A) init_slide() ½é´ü²½¤¹¤ë -(B) ¥Õ¥¡¥¤¥ë¤òÆɤ߹þ¤ß text[] ¤Ë³ÊǼ¤¹¤ë¡£ -(C) ¥Ï¥Ã¥·¥åÃÍ hval ¤ò·×»»¤¹¤ë¡£ -(D) insert() ¤¹¤ë (¤­¤Ã¤È¼­½ñ¤Ë token ¤òÄɲ䷤Ƥ¤¤ë¤Î¤À¤í¤¦) +(A) init_slide() 初期化する +(B) ファイルを読み込み text[] に格納する。 +(C) ハッシュ値 hval を計算する。 +(D) insert() する (きっと辞書に token を追加しているのだろう) -¤½¤·¤Æ¡¢¥ë¡¼¥×½èÍý¤Ç¤Ï°Ê²¼¤Î½èÍý¤¬¹Ô¤ï¤ì¤Æ¤¤¤ë +そして、ループ処理では以下の処理が行われている -(E) lastmatchlen, lastmatchoffset, matchlen ¤ò¹¹¿·¤¹¤ë¡£ -(F) get_next() (¼¡¤Î token ¤òÆɤࡣ¤¿¤Ö¤ó) -(G) match_insert() (¼­½ñ¤ËÄɲ乤롣¤¿¤Ö¤ó) +(E) lastmatchlen, lastmatchoffset, matchlen を更新する。 +(F) get_next() (次の token を読む。たぶん) +(G) match_insert() (辞書に追加する。たぶん) -(H) matchlen > lastmatchlen || lastmatchlen < THRESHOLD ¤Ê¤é +(H) matchlen > lastmatchlen || lastmatchlen < THRESHOLD なら -(H.1) output() ¤¹¤ë¡£(¥Þ¥Ã¥Á¤·¤Ê¤«¤Ã¤¿¤é¤½¤Î¤Þ¤Þ½ÐÎϤ·¤Æ¤¤¤ë¤Î¤À¤í¤¦¡£¤¿¤Ö¤ó) -(H.2) ¤½¤¦¤Ç¤Ê¤±¤ì¤Ð(¥Þ¥Ã¥Á¤·¤¿¤Ê¤é)¡¢output()¤·¡¢²¿¤«¤¤¤í¤¤¤í¤¹¤ë¡£ +(H.1) output() する。(マッチしなかったらそのまま出力しているのだろう。たぶん) +(H.2) そうでなければ(マッチしたなら)、output()し、何かいろいろする。 -¸½»þÅÀ¤Ç¡¢(H.2) ¤ÎÉôʬ¤Ï¤è¤¯²òÆɤǤ­¤Ê¤«¤Ã¤¿¡£²¿¤ä¤éºÆÅÙ get_next() ¤¬ -¸Æ¤Ð¤ì¤Æ¤¤¤¿¤ê¤·¤Æ»×¤Ã¤¿Ä̤ê¤Î½èÍý¥Õ¥í¡¼¤Ë¤Ï¤Ê¤Ã¤Æ¤¤¤Ê¤¤¡£¤À¤¬¡¢¤³¤³¤Ç -¤Ï¾Ç¤é¤ºÊüÃÖ¤¹¤ë¤³¤È¤Ë¤·¤Æ¡¢¤³¤³¤Þ¤ÇͽÁۤǽñ¤¤¤¿Éôʬ¤ÎºÙÉô¤Ë¿¨¤ì¤ë¤³¤È -¤Ë¤¹¤ë(ñ¤Ë¤³¤³¤Þ¤Ç¤ÎͽÁÛ¤¬´Ö°ã¤Ã¤Æ¤¤¤ë¤À¤±¤«¤â¤·¤ì¤Ê¤¤¤Î¤À¤«¤é¡¢¤ï¤« -¤é¤Ê¤¤Éôʬ¤ò̵Íý¤Ë¤ï¤«¤ë¤è¤¦¤Ë´èÄ¥¤ëɬÍפϤʤ«¤í¤¦) +現時点で、(H.2) の部分はよく解読できなかった。何やら再度 get_next() が +呼ばれていたりして思った通りの処理フローにはなっていない。だが、ここで +は焦らず放置することにして、ここまで予想で書いた部分の細部に触れること +にする(単にここまでの予想が間違っているだけかもしれないのだから、わか +らない部分を無理にわかるように頑張る必要はなかろう) -´Ø¿ô¤ÎºÙÉô¤Ë¿¨¤ì¤ëÁ°¤Ë¥Ç¡¼¥¿¹½Â¤¤Ë¤Ä¤¤¤ÆÄ´¤Ù¤Æ¤ª¤¯¡£¥Ç¡¼¥¿¹½Â¤¤ËÂФ·¤Æ -¤ÎÍý²ò¤¬¿¼¤Þ¤ì¤Ð¥¢¥ë¥´¥ê¥º¥à¤Î80%¤Ïʬ¤«¤Ã¤¿¤âƱÁ³¤À(¸ØÄ¥)¡£slide.c ¤Ç -»ÈÍѤµ¤ì¤Æ¤¤¤ë¥Ç¡¼¥¿¹½Â¤¤Ï°Ê²¼¤ÎÄ̤ê¤À¡£(ÉÔÍפ½¤¦¤À¤È»×¤¦¤â¤Î¤Ï½ü¤¤¤Æ -¤¢¤ë) +関数の細部に触れる前にデータ構造について調べておく。データ構造に対して +の理解が深まればアルゴリズムの80%は分かったも同然だ(誇張)。slide.c で +使用されているデータ構造は以下の通りだ。(不要そうだと思うものは除いて +ある) static unsigned int *hash; static unsigned int *prev; @@ -261,11 +261,11 @@ static unsigned int matchpos; static unsigned int pos; static unsigned int remainder; -too_flag ¤À¤±¡¢static ¤¬¤Ä¤¤¤Æ¤Ê¤¤¤¬¡¢Â¾¤Î¥½¡¼¥¹¤ò grep ¤·¤Æ¤â¤³¤ÎÊÑ¿ô -¤ò»È¤Ã¤Æ¤¤¤ë²Õ½ê¤Ï¤Ê¤¤¡¢Ã±¤Ë static ¤ÎÉÕ¤±Ëº¤ì¤À¤í¤¦¡£ +too_flag だけ、static がついてないが、他のソースを grep してもこの変数 +を使っている箇所はない、単に static の付け忘れだろう。 -¤³¤ì¤é¤ÎÊÑ¿ô¤Ï¡¢encode() ¤ÎËÁƬ init_slide() ¤Ç½é´ü²½¤µ¤ì¤Æ¤¤¤ë¡¦¡¦¤Î -¤«¤È»×¤Ã¤¿¤é°ã¤Ã¤¿¡£slide.c:encode_alloc() ¤Ç¹Ô¤ï¤ì¤Æ¤¤¤ë¡£ +これらの変数は、encode() の冒頭 init_slide() で初期化されている・・の +かと思ったら違った。slide.c:encode_alloc() で行われている。 int encode_alloc(method) @@ -304,9 +304,9 @@ encode_alloc(method) return method; } -°ú¿ô¤ËÅϤµ¤ì¤¿ method (¤³¤ì¤Ï¡¢lh1, lh5, lh6, lh7 ¤Ê¤É¤ò¼¨¤¹)¤Ë¤è¤Ã¤Æ¡¢ -½é´ü²½¤µ¤ì¤ëÆâÍƤ¬ÊѤï¤ë(encode_alloc()Á°È¾Éôʬ)¡£¤³¤Î¤³¤È¤«¤é³ÆÊÑ¿ô¤Î -ÍÑÅÓ¤â¤ï¤«¤ë¡£ +引数に渡された method (これは、lh1, lh5, lh6, lh7 などを示す)によって、 +初期化される内容が変わる(encode_alloc()前半部分)。このことから各変数の +用途もわかる。 method maxmatch dicbit ---------------------------- @@ -315,36 +315,36 @@ encode_alloc(method) -lh6- 256 15 -lh7- 256 16 -¤È¤¤¤¦¤³¤È¤é¤·¤¤¡£dicbit ¤È¤¤¤¦¤Î¤Ï¼­½ñ¥µ¥¤¥º¤Îbit¥µ¥¤¥º¤Ç¡¢¼­½ñ¥µ¥¤¥º -¤Ï 2^dicbit ¤Çɽ¤µ¤ì¤Æ¤¤¤ë¡£lh5 ¤¬ 8KB(2^13)¡¢lh6 ¤¬ 32KB(2^15)¡¢lh7 -¤¬ 64KB(2^16) ¤Î¼­½ñ¥µ¥¤¥º¤òÍøÍѤ¹¤ë¤È¸À¤¦¤Î¤ÏͽÈ÷Ãμ±¤Ç¤¢¤ë¡£maxmatch -¤È¤¤¤¦¤Î¤Ï¡¢token ¤ÎºÇĹ°ìÃ׍Ǥ¢¤ë¡£¤³¤Î¤³¤È¤âͽÈ÷Ãμ±¤È¤·¤Æ¾ÜºÙ¤Ë¤Ï -¿¨¤ì¤Ê¤¤¡£(¤È¤³¤í¤Ç¡¢Ëܽñ¤Ç¤ÏÅöÌÌ¡¢lh5, 6, 7 ¤Î¤³¤È¤·¤«¸ÀµÚ¤·¤Ê¤¤) +ということらしい。dicbit というのは辞書サイズのbitサイズで、辞書サイズ +は 2^dicbit で表されている。lh5 が 8KB(2^13)、lh6 が 32KB(2^15)、lh7 +が 64KB(2^16) の辞書サイズを利用すると言うのは予備知識である。maxmatch +というのは、token の最長一致長である。このことも予備知識として詳細には +触れない。(ところで、本書では当面、lh5, 6, 7 のことしか言及しない) -encode_set, encode_define ¤È¤¤¤¦¤Î¤¬¤¢¤ë¤¬¡¢method ¤Ë¤è¤Ã¤Æ¡¢Huffman -coding ¤ÎÊýË¡¤òÊѤ¨¤Æ¤¤¤ë¤³¤È¤Ï¤Á¤ç¤Ã¤È¸«¤ì¤Ð¤¹¤°¤Ë¤ï¤«¤ë¤·¡¢Â礷¤¿¤³ -¤È¤Ç¤Ï¤Ê¤¤¡£°Ê¹ß̵»ë¤¹¤ë¡£ +encode_set, encode_define というのがあるが、method によって、Huffman +coding の方法を変えていることはちょっと見ればすぐにわかるし、大したこ +とではない。以降無視する。 -encode_alloc() ¤Î¸åȾ¤Ç¤Ï¡¢Â¾¤ÎÊÑ¿ô¤Î½é´ü²½(¥Ð¥Ã¥Õ¥¡¤Î³ä¤êÅö¤Æ)¤¬¹Ô¤ï¤ì¤ë¡£ +encode_alloc() の後半では、他の変数の初期化(バッファの割り当て)が行われる。 dicsiz = (((unsigned long)1) << dicbit); -dicsiz ¤Ï¤½¤Î¤â¤Î¤º¤Ð¤ê¼­½ñ¥µ¥¤¥º¤Ç¤¢¤ë¡£ +dicsiz はそのものずばり辞書サイズである。 txtsiz = dicsiz*2+maxmatch; -¸½»þÅÀ¤Ç txtsiz ¤¬²¿¤Ê¤Î¤«¤Ï¤ï¤«¤é¤Ê¤¤¡£ +現時点で txtsiz が何なのかはわからない。 if (hash) return method; -hash ¤Ï¤³¤Îľ¸å¤Ç³ä¤êÅö¤Æ¤é¤ì¤ë¡£¤Ä¤Þ¤ê¡¢°ìÅÙ³äÅö¤ò¹Ô¤Ã¤¿¤é¡¢ -encode_alloc() ¤Ï¡¢°Ê¹ß¥á¥â¥ê¤Î³äÅö¤ò¹Ô¤ï¤Ê¤¤¡£¤¿¤À¤½¤ì¤À¤±¤À¡£ +hash はこの直後で割り当てられる。つまり、一度割当を行ったら、 +encode_alloc() は、以降メモリの割当を行わない。ただそれだけだ。 if (alloc_buf() == NULL) exit(207); /* I don't know this 207. */ -alloc_buf() ¤Ï¡¢huf.c ¤ÇÄêµÁ¤µ¤ì¤¿´Ø¿ô¡£¤³¤Î¤³¤È¤«¤é Huffman coding ¤Î -¤¿¤á¤Î¥Ð¥Ã¥Õ¥¡¤ò³ä¤êÅö¤Æ¤Æ¤¤¤ë¤Î¤À¤í¤¦¡£¤³¤³¤Ç¤Ï̵»ë¡£(¤·¤«¤·¡¢207 ¤È -¤¤¤¦¤Î¤Ï²¿¤Ê¤Î¤À¤í¤¦¡©) +alloc_buf() は、huf.c で定義された関数。このことから Huffman coding の +ためのバッファを割り当てているのだろう。ここでは無視。(しかし、207 と +いうのは何なのだろう?) hash = (unsigned int*)malloc(HSHSIZ * sizeof(unsigned int)); prev = (unsigned int*)malloc(DICSIZ * sizeof(unsigned int)); @@ -354,28 +354,28 @@ alloc_buf() if (hash == NULL || prev == NULL || text == NULL || too_flag == NULL) exit(207); -hash ¤Ï¡¢¥Ï¥Ã¥·¥åÍѤβ¿¤«¡¢HSHSIZ ¤Ï¡¢¸ÇÄêÃÍ¤Ç 2^15 ¤Ç¤¢¤ë¡£ +hash は、ハッシュ用の何か、HSHSIZ は、固定値で 2^15 である。 -prev ¤Ï¡¢DICSIZ¤«¤é¼­½ñ¤À¤í¤¦¡£Í×ÁǤη¿¤¬ char ¤Ç¤Ê¤¯ int ¤Ç¤¢¤ë¤³¤È¤Ë -¤âÃíÌܤ·¤Æ¤ª¤¯¡£DICSIZ ¤Ï dicsiz ¤Ç¤â¹½¤ï¤Ê¤¤¤Ï¤º¡£Ã±¤Ë¡ÖÂç¤Ï¾®¤ò·ó¤Í -¤ë¡×¤ò¼ÂÁ©¤·¤Æ¤¤¤ë¤À¤±¤Ç¤¢¤í¤¦¡¢TXTSIZ ¤âƱÍͤǤ¢¤ë¡£¤ª¤½¤é¤¯¡¢°ìÅ٤Π-¼Â¹Ô¤ÇÊ£¿ô¤Î°µ½Ì¥á¥½¥Ã¥É¤ò»ÈÍѤ·¤¿¾ì¹ç¡¢¤½¤Î¥á¥½¥Ã¥ÉËè¤ËÎΰè¤ò³ä¤êÅö¤Æ -¤ë¤è¤ê¤ÏºÇÂç¤ÎÃͤò¤¢¤é¤«¤¸¤á°ìÅÙ¤À¤±³ä¤êÅö¤Æ¤¿Êý¤¬Îɤ¤¤È¹Í¤¨¤¿¤Î¤À¤í¤¦¡£ -¤·¤«¤·¡¢¥½¡¼¥¹¤ò»²¾È¤¹¤ë¤È¤­¤ÏÈË»¨¤Ë¤Ê¤ë¤Î¤Ç°Ê¹ß¡¢ +prev は、DICSIZから辞書だろう。要素の型が char でなく int であることに +も注目しておく。DICSIZ は dicsiz でも構わないはず。単に「大は小を兼ね +る」を実践しているだけであろう、TXTSIZ も同様である。おそらく、一度の +実行で複数の圧縮メソッドを使用した場合、そのメソッド毎に領域を割り当て +るよりは最大の値をあらかじめ一度だけ割り当てた方が良いと考えたのだろう。 +しかし、ソースを参照するときは繁雑になるので以降、 DICSIZ == dicsiz TXTSIZ == txtsiz -¤Ç¤¢¤ë¤È¤¹¤ë¡£¤³¤ì½ÅÍס£ +であるとする。これ重要。 -text ¤Ï¡¢¸½»þÅÀ¤Ç¤ÏÉÔÌÀ +text は、現時点では不明 -too_flag ¤âÉÔÌÀ +too_flag も不明 -¤Ã¤È¤Ê¤ë¡£¤Þ¤À¡¢Îɤ¯Ê¬¤«¤é¤Ê¤¤¤¬¡¢°Ê²¼¤Î¿Þ¤ò½ñ¤¤¤Æ¤ª¤³¤¦¡£¸å¤Ç²¿Å٤⸫ -¤ë¤³¤È¤Ë¤Ê¤ë¤À¤í¤¦¡£¤³¤Î¿Þ¤Ï¥¹¥±¡¼¥ë¤¬ lh7 ¤Î¾ì¹ç¤òÁÛÄꤷ¤Æ¤¤¤ë¤¬¡£¤³ -¤Î¤³¤È¤ÏÂ礷¤¿¤³¤È¤Ç¤Ï¤Ê¤¤¤Ï¤º¤À¡£¤Þ¤¿¡¢too_flag ¤È hash ¤Î¥¹¥±¡¼¥ë¤¬ -°ì½ï¤À¤¬¤³¤ì¤Ï¥µ¥¤¥º(Îΰè¤Î¥Ð¥¤¥È¿ô)¤¬°ì½ï¤Ê¤Î¤Ç¤Ï¤Ê¤¯¡¢Í×ÁÇ¿ô¤¬°ì½ï¤Ç -¤¢¤ë¤³¤È¤ò¼¨¤·¤Æ¤¤¤ë¡£¤Û¤È¤ó¤É¤Î¾ì¹çÍ×ÁǤη¿¤Î°ã¤¤¤È¤¤¤¦¤Î¤Ï½èÍýÆâÍÆ¤Ë -¤È¤Ã¤Æ½ÅÍפʤ³¤È¤Ç¤Ï¤Ê¤¤¤Ï¤º¤À¡£ +っとなる。まだ、良く分からないが、以下の図を書いておこう。後で何度も見 +ることになるだろう。この図はスケールが lh7 の場合を想定しているが。こ +のことは大したことではないはずだ。また、too_flag と hash のスケールが +一緒だがこれはサイズ(領域のバイト数)が一緒なのではなく、要素数が一緒で +あることを示している。ほとんどの場合要素の型の違いというのは処理内容に +とって重要なことではないはずだ。 ---------------------------------------------------------------------------- @@ -398,7 +398,7 @@ too_flag ---------------------------------------------------------------------------- -Àè¤Ë¼¨¤·¤¿ÊÑ¿ô¤ÎÃæ¤Ç¤Þ¤À½é´ü²½¤Ë¤Ï¸½¤ì¤Æ¤¤¤Ê¤¤¤â¤Î¤¬¤¢¤ë¡£Îóµó¤¹¤ë¤È +先に示した変数の中でまだ初期化には現れていないものがある。列挙すると static unsigned int hval; static int matchlen; @@ -406,17 +406,17 @@ static unsigned int matchpos; static unsigned int pos; static unsigned int remainder; -¤À¡¢¤¶¤Ã¤È¥½¡¼¥¹¤òį¤á¤ë¤È slide.c:insert() ¤È¤¤¤¦´Ø¿ô¤Ë +だ、ざっとソースを眺めると slide.c:insert() という関数に hash[hval] = pos; -¤È¤¤¤¦¤Î¤¬¸½¤ì¤Æ¤¤¤ë¤«¤é¡¢hval ¤Ï¡¢hash[] ¤Î°ÌÃÖ¤ò»Ø¤·¡¢hash ¤Ë¤Ï¡¢pos -¤ò³ÊǼ¤¹¤ë¤È¿ä¬¤µ¤ì¤ë¡£Æ±ÍÍ¤Ë +というのが現れているから、hval は、hash[] の位置を指し、hash には、pos +を格納すると推測される。同様に prev[pos & (dicsiz - 1)] = hash[hval]; -¤È¤¤¤¦¤Î¤â¸½¤ì¤Æ¤¤¤ë¤«¤é pos ¤Ï¡¢prev[] ¤Î°ÌÃÖ¤ò»Ø¤·¡¢prev ¤Ë¤Ï¡¢ -hash[hval] ¤Ä¤Þ¤ê¡¢pos ¤ò³ÊǼ¤·¤Æ¤¤¤ë¤è¤¦¤À¡£¤³¤ì¤Ï¾¯¤·Ææ¤Ê½èÍý¤À¤¬¡¢ -insert() ¤ÎÁ´ËƤÏû¤¤(¤È¤¤¤¦¤«¤³¤ì¤À¤±)¤Ê¤Î¤Ç¡¢¤Á¤ç¤Ã¤È²£Æ»¤Ë¤½¤ì¤Æ¾Ü -ºÙ¤Ë¸«¤Æ¤ß¤è¤¦¡£(¸½ºß¤Î²òÀϤμñ»Ý¤Ï¡¢ÊÑ¿ô¤ÎÍÑÅӤγµÍפòͽÁÛ¤¹¤ë¤³¤È) +というのも現れているから pos は、prev[] の位置を指し、prev には、 +hash[hval] つまり、pos を格納しているようだ。これは少し謎な処理だが、 +insert() の全貌は短い(というかこれだけ)なので、ちょっと横道にそれて詳 +細に見てみよう。(現在の解析の趣旨は、変数の用途の概要を予想すること) -/* ¸½ºß¤Îʸ»úÎó¤ò¥Á¥§¡¼¥ó¤ËÄɲ乤ë */ +/* 現在の文字列をチェーンに追加する */ static void insert() { @@ -424,40 +424,40 @@ static void insert() hash[hval] = pos; } -¥³¥á¥ó¥È¤Ï¤Þ¤Ã¤¿¤¯°ÕÌ£ÉÔÌÀ¤À¤¬¡¢Ìµ»ë¤·¤Æ½èÍýÆâÍƤËÃåÌܤ¹¤ë¡£prev[] ¤Î -¥¤¥ó¥Ç¥Ã¥¯¥¹ pos & (dicsiz - 1) ¤Ï¡¢dicsiz ¤¬ 2^n ¤Ç¤¢¤ë¤³¤È¤«¤édicsiz -¤Ï¥Ó¥Ã¥È¥Þ¥¹¥¯¤Ç¤¢¤ë¤³¤È¤¬¤ï¤«¤ë¡£Î㤨¤Ð²¾¤Ë dicsiz ¤¬ 2^8 ¤À¤È -dicsiz - 1 ¤Ï¡¢ +コメントはまったく意味不明だが、無視して処理内容に着目する。prev[] の +インデックス pos & (dicsiz - 1) は、dicsiz が 2^n であることからdicsiz +はビットマスクであることがわかる。例えば仮に dicsiz が 2^8 だと +dicsiz - 1 は、 8 7 6 5 4 3 2 1 0 bit -------------------------- dicsiz 1 0 0 0 0 0 0 0 0 dicsiz-1 1 1 1 1 1 1 1 1 -¤Ç¤¢¤ë¡£¤³¤Î¤¹¤Ù¤Æ 1 ¤¬Î©¤Ã¤¿¥Ó¥Ã¥È¥Þ¥¹¥¯¤È pos ¤ò & ¤¹¤ë¤È¡¢¤É¤Î¤è¤¦ -¤Ê pos ¤ÎÃͤËÂФ·¤Æ¤â pos & (dicsiz - 1) ¤Ï¡¢prev[] ¤Î¥¤¥ó¥Ç¥Ã¥¯¥¹¤ÎÈÏ -°Ï¤ËǼ¤Þ¤ë¡£¤â¤¦¾¯¤·¸À¤¦¤È pos ¤¬²¾¤Ë¥¤¥ó¥Ç¥Ã¥¯¥¹¤ÎºÇÂçÃÍ+1¤À¤Ã¤¿¾ì¹ç¡¢ -pos & (dicsiz - 1) ¤Ï¡¢0 ¤Ë¤Ê¤ë¡£¤³¤ì¤Ë¤è¤ê¼¡¤ÎͽÁÛ¤¬Î©¤Ä¡£ +である。このすべて 1 が立ったビットマスクと pos を & すると、どのよう +な pos の値に対しても pos & (dicsiz - 1) は、prev[] のインデックスの範 +囲に納まる。もう少し言うと pos が仮にインデックスの最大値+1だった場合、 +pos & (dicsiz - 1) は、0 になる。これにより次の予想が立つ。 - o pos ¤¬¡¢prev[] ¤Î°ÌÃÖ¤ò»Ø¤¹¤Î¤Ç¤Ï¤Ê¤¯¡¢pos & (dicsiz - 1) ¤¬ - prev[]¤Î°ÌÃÖ¤ò»Ø¤¹¡£(pos ¤Ï¡¢¤³¤Î¥¤¥ó¥Ç¥Ã¥¯¥¹¤ÎÈϰϤò±Û¤¨¤ë²ÄǽÀ­¤¬¤¢¤ë) - o ¤½¤ì¤ËÈ¿¤·¤Æ¡¢prev[] ¤Ï´Ä¾õ¥Ð¥Ã¥Õ¥¡¤é¤·¤¤¤È¤¤¤¦Í½ÁÛ¤¬Î©¤Æ¤Ð¤ä¤Ï¤ê - pos ¤Ï¡¢prev ¤Î¥¤¥ó¥Ç¥Ã¥¯¥¹¤Ç¤¢¤ë¡£ + o pos が、prev[] の位置を指すのではなく、pos & (dicsiz - 1) が + prev[]の位置を指す。(pos は、このインデックスの範囲を越える可能性がある) + o それに反して、prev[] は環状バッファらしいという予想が立てばやはり + pos は、prev のインデックスである。 -prev ¤¬´Ä¾õ¥Ð¥Ã¥Õ¥¡¤Ç¤¢¤ë¤ÈͽÁÛ¤¬ÉÕ¤±¤ÐÏä¬Áᤤ¡£pos & (dicsiz-1) ¤Ï¡¢ -pos ¤ÈƱµÁ¤À¤È²ò¼á²Äǽ¤À¤«¤é¤Ç¤¢¤ë(prev ¤¬´Ä¾õ¥Ð¥Ã¥Õ¥¡¤Ç¤Ê¤¯Ìµ¸ÂĹ¤Î¥Ð¥Ã -¥Õ¥¡¤Ç¤¢¤ë¤ÈÁÛÁü¤·¤è¤¦)¤½¤·¤Æ¡¢pos & (dicsiz-1) ¤ò pos ¤ËÃÖ¤­´¹¤¨¤Æ¡¢ -ºÆÅÙ½èÍýÆâÍƤËÃåÌܤ¹¤ë¤È +prev が環状バッファであると予想が付けば話が早い。pos & (dicsiz-1) は、 +pos と同義だと解釈可能だからである(prev が環状バッファでなく無限長のバッ +ファであると想像しよう)そして、pos & (dicsiz-1) を pos に置き換えて、 +再度処理内容に着目すると prev[pos] = hash[hval]; hash[hval] = pos; -¤È¤¤¤¦¤³¤È¤«¤é¡¢ - 1. (¤³¤Î´Ø¿ô¤ËÍè¤ëÁ°¤Ë) pos ¤¬¹¹¿·¤µ¤ì¤ë¡£(ͽÁÛ) - 2. prev[pos] ¤Ë°ÊÁ°¤Î hash[hval] (°ÊÁ°¤Î pos)¤ò³ÊǼ¤¹¤ë - 3. hash[hval] ¤Ë¿·¤·¤¤ pos ¤ò½ñ¤¯¡£ -¤È¤¤¤Ã¤¿½èÍý¤Ç¤¢¤ë¤³¤È¤¬Í½ÁÛ¤µ¤ì¤ë¡£¥³¥á¥ó¥È¤Î¡Ö¥Á¥§¡¼¥ó¡×¤â¤Ê¤ó¤È¤Ê¤¯ -ǼÆÀ¤Ç¤­¤ë¡£¿·¤¿¤Ê»ö¼Â(¤Þ¤ÀͽÁÛ¤À¤¬)¤¬Ê¬¤«¤Ã¤¿¤Î¤Ç¡¢¿Þ¤Ë½ñ¤­µ­¤½¤¦¡£ +ということから、 + 1. (この関数に来る前に) pos が更新される。(予想) + 2. prev[pos] に以前の hash[hval] (以前の pos)を格納する + 3. hash[hval] に新しい pos を書く。 +といった処理であることが予想される。コメントの「チェーン」もなんとなく +納得できる。新たな事実(まだ予想だが)が分かったので、図に書き記そう。 ---------------------------------------------------------------------------- 0 2^15=32768 @@ -473,53 +473,53 @@ pos +----+-----+-------------------- `- ppos `-pos - * hash ¤Î¼è¤êÆÀ¤ëÃÍ¤Ï pos ¤½¤Î°ÌÃÖ¤Ï hval - * ppos ¤Ï°ÊÁ°¤Î pos ¤ò¼¨¤¹¡£pppos ¤Ï¤µ¤é¤Ë°ÊÁ°¤Î pos ¤ò»Ø¤¹¡£ - * prev ¤Ï̵¸ÂĹ¤Î¥Ð¥Ã¥Õ¥¡(ËÜÅö¤Ï´Ä¾õ¥Ð¥Ã¥Õ¥¡) + * hash の取り得る値は pos その位置は hval + * ppos は以前の pos を示す。pppos はさらに以前の pos を指す。 + * prev は無限長のバッファ(本当は環状バッファ) ---------------------------------------------------------------------------- -¤Þ¤À¡¢²òÀϤǤ­¤Æ¤Ê¤¤ÊÑ¿ô¤¬»Ä¤Ã¤Æ¤¤¤ë¡£ +まだ、解析できてない変数が残っている。 static int matchlen; static unsigned int matchpos; static unsigned int remainder; -¤·¤«¤·¤³¤ì¤é¤Ï¤É¤¦¤Ë¤â¥Ñ¥Ã¤È¸«¤Ç¤Ï¤ï¤«¤é¤Ê¤¤¡£½èÍýÆâÍƤòÄɤ¤¤«¤±¤Ê¤¤¤È -¤À¤á¤½¤¦¤À¡£»ÅÊý¤Ê¤¤¤Î¤ÇÊÑ¿ô̾¤ÇͽÁÛ¤·¤è¤¦¡£(¹¬¤¤Á°¤ÎÊÑ¿ô̾¤È°ã¤Ã¤Æͽ -ÁÛ¤·¤ä¤¹¤¤)°Ê²¼ +しかしこれらはどうにもパッと見ではわからない。処理内容を追いかけないと +だめそうだ。仕方ないので変数名で予想しよう。(幸い前の変数名と違って予 +想しやすい)以下 ---------------------------------------------------------------------------- - * matchlen °ìÃפ·¤¿Ê¸»úÎóĹ - * matchpos °ìÃפ·¤¿¼­½ñ¾å¤Î°ÌÃÖ - * remainder token ¤Î»Ä¤ê¥µ¥¤¥º + * matchlen 一致した文字列長 + * matchpos 一致した辞書上の位置 + * remainder token の残りサイズ ---------------------------------------------------------------------------- -¤Ï¤¿¤·¤Æ¡¢Í½ÁۤϤ¢¤Ã¤Æ¤¤¤ë¤Î¤«¡¢º£¤Ï¤Þ¤Àʬ¤«¤é¤Ê¤¤¡£ +はたして、予想はあっているのか、今はまだ分からない。 -slide.c ¤ò¸«¤ë¸Â¤ê¥Ç¡¼¥¿¹½Â¤¤ÏÌÖÍå¤Ç¤­¤¿¡£·ë¶Éʬ¤«¤Ã¤¿¤Î¤«Ê¬¤«¤é¤Ê¤¤¤Î -¤«Îɤ¯Ê¬¤«¤é¤Ê¤¤¤¬¾¯¤·¤º¤Ä¤Ç¤âÁ°¿Ê¤Ï¤·¤Æ¤¤¤ë¤Ï¤º¤À¡£¤³¤³¤Ç¡¢ºÆÅÙ -encode() ¤Î½èÍý¤òÄɤ¤¤«¤±¤è¤¦¡£º£Å٤ϺÙÉô¤Ë¤âÃåÌܤ¹¤ë¡£ +slide.c を見る限りデータ構造は網羅できた。結局分かったのか分からないの +か良く分からないが少しずつでも前進はしているはずだ。ここで、再度 +encode() の処理を追いかけよう。今度は細部にも着目する。 -Á°¤Ë¡¢encode() ¤Î¥½¡¼¥¹¤Ë¤Ï (A) ¡Á (H) ¤Þ¤Ç¤Îµ­¹æ¤òµ­¤·¤¿¡£¤³¤Î½çÈÖ¤Ë -²òÀϤò¿Ê¤á¤è¤¦¡£ +前に、encode() のソースには (A) 〜 (H) までの記号を記した。この順番に +解析を進めよう。 /* (A) */ init_slide(); -¤Þ¤¢¡¢½é´ü²½¤Ç¤¢¤ë¡£ÆâÍƤò¸«¤Æ¤ß¤ë¤È +まあ、初期化である。内容を見てみると for (i = 0; i < HSHSIZ; i++) { hash[i] = NIL; too_flag[i] = 0; } -¤À¤±¤Ç¤¢¤ë¡£NIL ¤È¤¤¤¦¤Î¤Ï¡¢0 ¤Ç¤¢¤ë¤È slide.c ¤ÇÄêµÁ¤µ¤ì¤Æ¤¤¤ë¡£ÉáÄÌ -¤³¤Î¤è¤¦¤Ê½é´üÃͤϡ¢Ä̾ï¤ÎÃͤ¬¼è¤êÆÀ¤Ê¤¤Ãͤò¼¨¤·¤Æ¤¤¤ë¡£NIL ¤¬ 0 ¤Ê¤é -hash[] ¤Ë³ÊǼ¤µ¤ì¤ë pos ¤Ï 0 ¤Ë¤Ê¤é¤Ê¤¤²ÄǽÀ­¤¬¤¢¤ë¡£¤Þ¤¢¡¢Í½ÁۤФ«¤ê -½ñ¤¤¤Æ¤â»ÅÊý¤Ê¤¤¤Î¤Ç¡¢¤³¤Î´Ø¿ô¤Ï½ª¤í¤¦¡£Í¾Ã̤À¤¬¡¢nil ¤Ï null ¤ÈƱ¤¸¤Ç¡£ -¡Ö¤Ê¤¤¡×¤Î°ÕÌ£¤À¤¬¡¢NULL ¤¬C¸À¸ì¤Ç¤Ï¥Ý¥¤¥ó¥¿¤À¤«¤é¡£Ê̤Υޥ¯¥í̾¤Ë¤·¤¿ -¤Î¤«¤âÃΤì¤Ê¤¤¡£¤¤¤º¤ì¤Ë¤·¤Æ¤â¤³¤ÎÄøÅ٤ϥޥ¯¥í¤Ë¤¹¤ëɬÍפâ¤Ê¤«¤í¤¦¤È¤Ï -»×¤¦¤Î¤Ï¡¢Í¾·×¤Ê¤ªÀ¤Ï䫤⤷¤ì¤Ê¤¤¡£ +だけである。NIL というのは、0 であると slide.c で定義されている。普通 +このような初期値は、通常の値が取り得ない値を示している。NIL が 0 なら +hash[] に格納される pos は 0 にならない可能性がある。まあ、予想ばかり +書いても仕方ないので、この関数は終ろう。余談だが、nil は null と同じで。 +「ない」の意味だが、NULL がC言語ではポインタだから。別のマクロ名にした +のかも知れない。いずれにしてもこの程度はマクロにする必要もなかろうとは +思うのは、余計なお世話かもしれない。 /* (B) */ remainder = fread_crc(&text[dicsiz], txtsiz-dicsiz, infile); @@ -530,17 +530,17 @@ hash[] if (matchlen > remainder) matchlen = remainder; -¥Õ¥¡¥¤¥ë¤òÆɤ߹þ¤ß¡¢³ÆÊÑ¿ô¤Î½é´üÃͤòÀßÄꤷ¤Æ¤¤¤ë¡£ÃíÌܤ¹¤Ù¤­¤Ï¥Õ¥¡¥¤¥ë -¤òÆɤ߹þ¤ó¤À¥Ð¥Ã¥Õ¥¡¤Î°ÌÃ֤Ǥ¢¤ë¡£fread_crc() ¤Ï¡¢crcio.c ¤ÇÄêµÁ¤µ¤ì¤¿ -ÈÆÍÑ´Ø¿ô¤Ç¡¢CRCÃͤò·×»»¤·¤¿¤ê´Á»ú¥³¡¼¥ÉÊÑ´¹¤ò¤·¤¿¤ê¤ò½ü¤±¤Ð¡¢fread() -¤ÈƱ¤¸¤Ç¤¢¤ë¡£¤Ä¤Þ¤ê¡¢¥Õ¥¡¥¤¥ë¤ÏºÇ½é¡¢ +ファイルを読み込み、各変数の初期値を設定している。注目すべきはファイル +を読み込んだバッファの位置である。fread_crc() は、crcio.c で定義された +汎用関数で、CRC値を計算したり漢字コード変換をしたりを除けば、fread() +と同じである。つまり、ファイルは最初、 - &text[dicsiz] ¤Î°ÌÃ֤ˡ¢txtsiz-dicsiz ʬ¤À¤±Æɤޤì¤ë¡£ + &text[dicsiz] の位置に、txtsiz-dicsiz 分だけ読まれる。 -¤³¤È¤ò¼¨¤¹¡£¿Þ¼¨¤·¤è¤¦¡£ +ことを示す。図示しよう。 ---------------------------------------------------------------------------- -< ½é´ü¾õÂÖ > +< 初期状態 > dicsiz=2^dicbit 2*2^dicbit v v txtsiz @@ -552,125 +552,125 @@ hash[] <------ remainder --------------> - |--- ¤³¤Î°ÌÃ֤˺ǽé¤Î ---------| - ¥Ç¡¼¥¿¤¬Æɤޤì¤Æ¤¤¤ë + |--- この位置に最初の ---------| + データが読まれている ---------------------------------------------------------------------------- -¤Þ¤¹¤Þ¤¹¡¢text[] ¤ÎÍÑÅÓ¤¬ÉÔÌÀ¤À¤¬¡¢slide ¼­½ñË¡¤Îŵ·¿Åª¤ÊÆɤ߹þ¤ß½èÍý -¤Î¤³¤È¤ò¹Í¤¨¤ë¤È¤¢¤ëÄøÅÙͽÁÛ¤¬¤Ä¤¯(¤½¤ì¤òÀè¤Ë¼¨¤·¤¿Êý¤¬Îɤ¤¤«¡©)¡£¤Þ¤¢¡¢ -¤³¤³¤Ç¤Ï¥Õ¡¼¥ó¤Ã¤ÈÉ¡¤ÇǼÆÀ¤·¤ÆºÑ¤Þ¤½¤¦¡£ +ますます、text[] の用途が不明だが、slide 辞書法の典型的な読み込み処理 +のことを考えるとある程度予想がつく(それを先に示した方が良いか?)。まあ、 +ここではフーンっと鼻で納得して済まそう。 -fread_crc() ¤Ï¡¢Æɤ߹þ¤ó¤À¥Ð¥Ã¥Õ¥¡Ä¹¤òÊÖ¤¹¡£remainder ¤¬¤½¤ÎÃͤǡ¢´û¤Ë -¿Þ¼¨¤·¤Æ¤¢¤ë¡£encoded_origsize ¤Ï¡¢¥½¡¼¥¹¤ò¸«¤ë¤È¡¢¸µ¤Î¥Õ¥¡¥¤¥ë¤Î¥µ¥¤ -¥º¤òɽ¤¹¤¿¤á¤À¤±¤ÎÊÑ¿ô¤Î¤è¤¦¤À¡£°Ê¹ß¤Ï̵»ë¤·¤è¤¦¡£ +fread_crc() は、読み込んだバッファ長を返す。remainder がその値で、既に +図示してある。encoded_origsize は、ソースを見ると、元のファイルのサイ +ズを表すためだけの変数のようだ。以降は無視しよう。 -¤È¤³¤í¤Ç¡¢¥Õ¥¡¥¤¥ë¥µ¥¤¥º¤¬¾®¤µ¤¤¾ì¹ç¿Þ¤ÎÄ̤ê¤Ë¤Ê¤é¤Ê¤¤¤Ã¤È¹Í¤¨¤ë¤«¤âÃÎ -¤ì¤Ê¤¤¡£¤½¤ÎÄ̤ê¤Ê¤Î¤À¤¬¡¢Îã³°¾ò·ï¤Ï¾¯¤Ê¤¤Êý¤¬¥½¡¼¥¹¤ÏÍý²ò¤·¤ä¤¹¤¤¡£Ã± -½ã¤Ê¾ì¹ç¤À¤±¤ò¹Í¤¨¤¿Êý¤¬¡¢¤¢¤ì¤³¤ì¹Í¤¨¤ò¤á¤°¤é¤¹É¬Íפ¬¤Ê¤¤¤«¤é¤À¡£¤Ê¤Ë -¤·¤í´û¤ËÆ°¤¯¥½¡¼¥¹¤ò¸«¤Æ¤¤¤ë¤Î¤À¤«¤é¡¢ºÙ¤«¤¤¤³¤È¤ËÌܤò¤Ä¤Ö¤Ã¤Æ¤â¥¨¥ó¥Ð -¥°¤¹¤ë¤³¤È¤Ï¤Ê¤¤¤Î¤Ç¤¢¤ë¡£¤½¤¦¤¤¤¦¤ï¤±¤Ç¡¢ÅöÌ̤Ϥ³¤Î¿Þ¤¬Í£°ì¤Î½é´ü¾õÂÖ -¤Ç¤¢¤ë¤È¹Í¤¨¤ë¡£ +ところで、ファイルサイズが小さい場合図の通りにならないっと考えるかも知 +れない。その通りなのだが、例外条件は少ない方がソースは理解しやすい。単 +純な場合だけを考えた方が、あれこれ考えをめぐらす必要がないからだ。なに +しろ既に動くソースを見ているのだから、細かいことに目をつぶってもエンバ +グすることはないのである。そういうわけで、当面はこの図が唯一の初期状態 +であると考える。 -(B) ¤ÎÉôʬ¤Ï¤â¤¦¾¯¤·ÃíÌܤ¹¤Ù¤­²Õ½ê¤¬¤¢¤ë¡£ +(B) の部分はもう少し注目すべき箇所がある。 matchlen = THRESHOLD - 1; -matchlen ¤Ï¡¢¡Ö°ìÃפ·¤¿Ê¸»úÎóĹ¡×¤Ç¤¢¤ë¤ÈͽÁÛ¤·¤¿¤¬ THRESHOLD ¤ÎÃÍ¤Ï 3 -(¸ÇÄêÃÍ)¤Ç¤¢¤ë¤«¤é¡¢matchlen ¤Î½é´üÃÍ¤Ï 2 ¤À¡£¤¤¤­¤Ê¤êͽÁÛ¤¬¤Ï¤º¤ì¤¿µ¤ -¤¬¤¹¤ë¡£Í½ÁÛ¤òΩ¤Æľ¤½¤¦¡£2 ¤È¤¤¤¦Ææ¤Ê¿ôÃÍ¤È match*len* ¤Ë¤Ä¤¤¤Æ¹Í¤¨¤ë -¤È¡¢ËÁƬ¤Ç ¤Î¥Ú¥¢¤Î len ¤Ï 2 ¤Ç¤¢¤ë¤³¤È¤Ï¤Ê¤¤¤ÈÀâÌÀ¤·¤¿¡£Ìµ -°ÕÌ£¤À¤«¤é¤Ç¤¢¤ë¤¬¡¢matchlen ¤Î½é´üÃͤϤ³¤Î 2 ¤È´ØÏ¢¤¹¤ë¤«¤â¤·¤ì¤Ê¤¤¡£ -¤½¤³¤Ç¡¢matchlen ¤ÎÍÑÅÓ¤ò°Ê²¼¤Î¤è¤¦¤ËͽÁÛ¤·¤Ê¤ª¤¹¤³¤È¤Ë¤¹¤ë¡£°Ê²¼¤Î¤è -¤¦¤Ë¥á¥â¤ò¹¹¿·¤·¤è¤¦¡£THRESHOLD(threshold ¤ÏïçÃͤΰÕ)¤â°ì½ï¤ËͽÁÛ¤·¤¿¡£ +matchlen は、「一致した文字列長」であると予想したが THRESHOLD の値は 3 +(固定値)であるから、matchlen の初期値は 2 だ。いきなり予想がはずれた気 +がする。予想を立て直そう。2 という謎な数値と match*len* について考える +と、冒頭で のペアの len は 2 であることはないと説明した。無 +意味だからであるが、matchlen の初期値はこの 2 と関連するかもしれない。 +そこで、matchlen の用途を以下のように予想しなおすことにする。以下のよ +うにメモを更新しよう。THRESHOLD(threshold は閾値の意)も一緒に予想した。 ---------------------------------------------------------------------------- -* matchlen ºÇÄã¸Â°ìÃפ·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤Ä¹¤µ-1 -* THRESHOLD ºÇÄã¸Â°ìÃפ·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤Ä¹¤µ +* matchlen 最低限一致しなければならない長さ-1 +* THRESHOLD 最低限一致しなければならない長さ ---------------------------------------------------------------------------- -¤¦¡¼¤ó¡¢ËÜÅö¤À¤í¤¦¤«¡© +うーん、本当だろうか? -(B) ¤Î»Ä¤ê¤ÎÉôʬ¤òÊÒÉÕ¤±¤è¤¦ +(B) の残りの部分を片付けよう pos = dicsiz; if (matchlen > remainder) matchlen = remainder; -pos ¤¬ dicsiz ¤Ç¤¢¤ë¤³¤È¤«¤é¤É¤¦¤ä¤é¡¢pos ¤Ï¡¢text[] ¤Î¥¤¥ó¥Ç¥Ã¥¯¥¹¤é -¤·¤¤¡£Á°¤ÎͽÁÛ¤Ç pos ¤Ï¡¢prev[] ¤Î¥¤¥ó¥Ç¥Ã¥¯¥¹¤Ç¤â¤¢¤ê¡¢hash[] ¤ÎÃÍ¤Ç -¤â¤¢¤ë¤ÈͽÁÛ¤·¤¿¤Î¤À¤¬(¤³¤ì¤Ï¤â¤Á¤í¤ó´Ö°ã¤¤¤Ç¤Ï¤Ê¤«¤í¤¦¤¬)¡£¤É¤¦¤ä¤é -ËÜÅö¤Î°ÕÌ£¤Ï¡¢½èÍý¤¹¤ë¥Æ¥­¥¹¥È¤ÎÀèƬ¤ò¼¨¤·¤Æ¤¤¤ë¤Î¤Ç¤Ï¤Ê¤¤¤«¤È¤â»×¤¨¤ë¡£ -¤Þ¤¢¡¢¤³¤³¤Ç¤Ï̵Æñ¤Ë¡Ötext[] ¤Î¥¤¥ó¥Ç¥Ã¥¯¥¹(¤Ç¤â¤¢¤ë)¡×¤È¤À¤±Íý²ò¤·¤è¤¦¡£ -´û¤Ë¿Þ¤Ë¤Ï½ñ¤­¹þ¤ó¤Ç¤¢¤ë¡£ +pos が dicsiz であることからどうやら、pos は、text[] のインデックスら +しい。前の予想で pos は、prev[] のインデックスでもあり、hash[] の値で +もあると予想したのだが(これはもちろん間違いではなかろうが)。どうやら +本当の意味は、処理するテキストの先頭を示しているのではないかとも思える。 +まあ、ここでは無難に「text[] のインデックス(でもある)」とだけ理解しよう。 +既に図には書き込んである。 -¼¡¤Î if ¤À¤¬¡¢remainder ¤¬ matchlen ¤è¤ê¤â¾®¤µ¤¤¾ì¹ç¤Î¾ò·ï¤À¡£¤Þ¤¿¡¢ -matchlen ¤ÎͽÁÛ¤¬Ê¤¤µ¤ì¤½¤¦¤Êͽ´¶¤¬¤·¤Ê¤¤¤Ç¤â¤Ê¤¤¤¬¡¢¤³¤Î if ʸ¤Ï*Îã³° -¾ò·ï*¤Ê¤Î¤Ç̵»ë¤¹¤ë¤³¤È¤Ë¤·¤¿¡£ÅÔ¹ç¤Î°­¤¤¤³¤È¤Ï¸«¤Ê¤¤Êý¤¬Îɤ¤¤Î¤À¡£ +次の if だが、remainder が matchlen よりも小さい場合の条件だ。また、 +matchlen の予想が覆されそうな予感がしないでもないが、この if 文は*例外 +条件*なので無視することにした。都合の悪いことは見ない方が良いのだ。 /* (C) */ hval = ((((text[dicsiz] << 5) ^ text[dicsiz + 1]) << 5) ^ text[dicsiz + 2]) & (unsigned)(HSHSIZ - 1); -(C) ¤Ç¤¢¤ë¡£¤³¤ì¤ÏÆñ²ò¤Ç¤¢¤ë¡£Ê£»¨¤Ê¿ô¼°¤Ï¶ì¼ê¤Ç¤¢¤ë¤¬¡¢¤¸¤Ã¤¯¤ê¹Í¤¨¤è -¤¦¡£¤Þ¤ºµá¤á¤ëÃÍ¤Ï hval ¤Ç¤¢¤ë¡£¤³¤ì¤Ï hash[] ¤Î¥¤¥ó¥Ç¥Ã¥¯¥¹¤Ê¤Î¤À¤¬¡¢ -¤³¤Î¤è¤¦¤ÊÊ£»¨¤Ê¼°¤Çµá¤Þ¤ë¥¤¥ó¥Ç¥Ã¥¯¥¹¤Ê¤ó¤ÆÁÛÁü¤â¤Ä¤«¤Ê¤¤¡£¤Þ¤º¡¢ºÇ½é -¤Î¥¤¥ó¥¹¥Ô¥ì¡¼¥·¥ç¥ó¤òÂç»ö¤Ë¤¹¤ë¤³¤È¤Ë¤·¤è¤¦¡£ËÁƬ¤Ç¡¢(C) ¤Î½èÍý¤Ï¡Ö¥Ï¥Ã -¥·¥åÃÍ hval ¤ò·×»»¤¹¤ë¡£¡×¤Ã¤È¶ì¤â¤Ê¤¯Í½ÁÛ¤·¤¿¡£¤½¤·¤Æ¤³¤ì¤Ï´Ö°ã¤¤¤Ç¤Ï -¤Ê¤¤¤À¤í¤¦(¤­¤Ã¤È)¡£hash[] ¤È¤Î´ØÏ¢¤ò¤³¤³¤Ç¹Í¤¨¤Æ¤â¤ï¤«¤é¤Ê¤¤¤«¤é¡¢¤³ -¤Î¥Ï¥Ã¥·¥åÃͤη׻»¤À¤±¤ò¹Í¤¨¤ë¤³¤È¤Ë¤·¤è¤¦¡£ +(C) である。これは難解である。複雑な数式は苦手であるが、じっくり考えよ +う。まず求める値は hval である。これは hash[] のインデックスなのだが、 +このような複雑な式で求まるインデックスなんて想像もつかない。まず、最初 +のインスピレーションを大事にすることにしよう。冒頭で、(C) の処理は「ハッ +シュ値 hval を計算する。」っと苦もなく予想した。そしてこれは間違いでは +ないだろう(きっと)。hash[] との関連をここで考えてもわからないから、こ +のハッシュ値の計算だけを考えることにしよう。 -¼°¤ò¤¸¤Ã¤¯¤ê¸«¤Æ¤ß¤ë¡£¡£¡£¤¸¤Ã¤¯¤ê¸«¤Æ¤ß¤ë¤È°Ê²¼¤Î¤³¤È¤¬¤ï¤«¤ë¡£ +式をじっくり見てみる。。。じっくり見てみると以下のことがわかる。 x(i) = text[dicsiz + i] -¤È¤¹¤ë¤È +とすると hval = (( x(0) << 5 ^ x(1) ) << 5 ^ x(2) ) & (unsigned)(HSHSIZ - 1); -¤Ç¤¢¤ë¡£±é»»»Ò << ¤Ï¡¢±é»»»Ò ^ ¤è¤ê¤âÍ¥Àè½ç°Ì¤¬Ä㤤¤Î¤Ç;·×¤Ê³ç¸Ì¤Ï¾Ê -ά¤·¤¿¡£ºÇ¸å¤Î & (unsigned)(HSHSIZ - 1) ¤Ï¡¢Á°¤Ë¤â»÷¤¿¤è¤¦¤Ê¼°¤¬½Ð¤¿¤¬ -¤³¤ì¤Ï¤¢¤ëÈϰϤοôÃÍ(¤³¤³¤Ç¤Ï¡¢0 ¡Á HSHSIZ{2^15}-1)¤òÃê½Ð¤¹¤ë¤¿¤á¤Î¥Ó¥Ã -¥È¥Þ¥¹¥¯¤Ç¤¢¤ë¡£¥Ï¥Ã¥·¥å´Ø¿ô¤È¸À¤¦¤Î¤Ï¤¢¤ëÉä¹æ¤ò¤¢¤ë½¸¹ç¤ÎÉä¹æ¤Ë¼ÌÁü¤¹ -¤ë´Ø¿ô¤Ç¤¢¤ë¤«¤é¤³¤Î¤è¤¦¤Ê¥Ó¥Ã¥È¥Þ¥¹¥¯¤ÏÅöÁ³É¬ÍפÀ¤·¡¢Îɤ¯¹Ô¤ï¤ì¤ë»ö¤À -(ÉáÄÌ¤Ï mod ÁÇ¿ô¤ò¹Ô¤¦¤ó¤À¤±¤É)¡£¤Þ¤¿¡¢hval ¤Ï¡¢hash[] ¤Î¥¤¥ó¥Ç¥Ã¥¯¥¹ -¤Ê¤Î¤À¤«¤é¡¢¼ÌÁü¤¹¤ë½¸¹ç¤È¤Ï hash[] ¤Î¥¤¥ó¥Ç¥Ã¥¯¥¹¤À¡£¤ª¤Ã¡¢°Æ³°´Êñ¤Ë -¤ï¤«¤Ã¤¿¡£x(i) ¤¬ text[dicsiz + i] ¤Ç¡¢¥Ï¥Ã¥·¥å´Ø¿ô¤ÎÊÑ¿ô¤Ï x(0), -x(1), x(2) ¤À¤«¤é¡¢ÀèƬ¤Î 3 ¥Ð¥¤¥È¤Îʸ»úÎó(ʿʸ)¤Î¥Ï¥Ã¥·¥åÃͤòµá¤á¤Æ¤¤ -¤ë¤ï¤±¤À¡£¤½¤Î¾¤Î·×»»(<< 5 ¤È¤« ^ ¤È¤«) ¤ÏÂ礷¤¿¤³¤È¤Ç¤Ï¤Ê¤¤¡£Ìµ»ë¤· -¤è¤¦¡£¤Þ¤¿¡¢Â³¤±¤Æ (D) ¤Î½èÍý¤â¸«¤ë¤¬¡¢ +である。演算子 << は、演算子 ^ よりも優先順位が低いので余計な括弧は省 +略した。最後の & (unsigned)(HSHSIZ - 1) は、前にも似たような式が出たが +これはある範囲の数値(ここでは、0 〜 HSHSIZ{2^15}-1)を抽出するためのビッ +トマスクである。ハッシュ関数と言うのはある符号をある集合の符号に写像す +る関数であるからこのようなビットマスクは当然必要だし、良く行われる事だ +(普通は mod 素数を行うんだけど)。また、hval は、hash[] のインデックス +なのだから、写像する集合とは hash[] のインデックスだ。おっ、案外簡単に +わかった。x(i) が text[dicsiz + i] で、ハッシュ関数の変数は x(0), +x(1), x(2) だから、先頭の 3 バイトの文字列(平文)のハッシュ値を求めてい +るわけだ。その他の計算(<< 5 とか ^ とか) は大したことではない。無視し +よう。また、続けて (D) の処理も見るが、 /* (D) */ insert(); -insert() ¤Ï¡¢¹¬¤¤²òÆɤº¤ß¤Ç¤¢¤ë pos ¤ò hash[] ¤Ë³ÊǼ¤¹¤ë½èÍý¤À¡£ -ͽÁÛ¤ÎÃʳ¬¤Ç¤Ï¡¢(C) ¤È (D) ¤òÊ̸ĤνèÍý¤È¹Í¤¨¤Æ¤¤¤¿¤Î¤À¤¬¤³¤ì¤Ï -¤É¤¦¤ä¤é¥»¥Ã¥È¤Ç¤¢¤ë¡£ +insert() は、幸い解読ずみである pos を hash[] に格納する処理だ。 +予想の段階では、(C) と (D) を別個の処理と考えていたのだがこれは +どうやらセットである。 - (C) pos ¤Î°ÌÃ֤Π3 ʸ»ú¤Î¥Ï¥Ã¥·¥åÃͤò·×»»¤· - (D) hash[¥Ï¥Ã¥·¥åÃÍ] = pos ¤ò¹Ô¤¦ + (C) pos の位置の 3 文字のハッシュ値を計算し + (D) hash[ハッシュ値] = pos を行う -¤â¤¦¾¯¤·Ãí°Õ¿¼¤¯¸¡Æ¤¤¹¤ë¤È¡Öpos¤Î°ÌÃÖ¤Î3ʸ»ú¡×¤È¡¢µá¤á¤¿¡Ö¥Ï¥Ã¥·¥åÃÍ¡× -¤ÏÏÀÍýŪ¤Ë¤Ï = ¤Ç¤¢¤ë¡£ +もう少し注意深く検討すると「posの位置の3文字」と、求めた「ハッシュ値」 +は論理的には = である。 -¤Ä¤Þ¤ê¡¢(C) (D) ¤Î½èÍý¤Ï +つまり、(C) (D) の処理は - hash[ʸ»úÎó] = °ÌÃÖ + hash[文字列] = 位置 -¤È¤¤¤¦½èÍý¤ò¹Ô¤Ã¤Æ¤¤¤ë¡£¥Ï¥Ã¥·¥åÃͤξ×ÆͤϤ³¤³¤Ç¤Ï¹Í¤¨¤Ê¤¤¡£slide ¼­½ñ -Ë¡¤Ç¤Ï¡¢¤¢¤ëʸ»úÎó¤ËÂФ·°ÊÁ°¤½¤Îʸ»úÎ󤬸½¤ì¤¿¤«¤É¤¦¤«¤ò¸¡º÷¤·¡¢¤½¤Î°Ì -ÃÖ¤òµá¤á¤ëɬÍפ¬¤¢¤ë¤Î¤À¤¬¡¢¤³¤ÎºÇ½é¤Î 3 ʸ»ú¤Ë´Ø¤·¤Æ¤Ï¸½»þÅÀ¤Ç¤½¤ÎÍÑ -·ï(°ÌÃÖ¤òµá¤á¤ë)¤òËþ¤¿¤¹»ö¤¬¤Ç¤­¤Æ¤¤¤ë¡£¤³¤³¤Þ¤Ç¤Ç¼«¤º¤È encode() ¤ÎÁ´ -ÂÎÁü¤âͽÁÛ¤¬¤Ä¤­¤½¤¦¤Êµ¤¤¬¤¹¤ë¡£ +という処理を行っている。ハッシュ値の衝突はここでは考えない。slide 辞書 +法では、ある文字列に対し以前その文字列が現れたかどうかを検索し、その位 +置を求める必要があるのだが、この最初の 3 文字に関しては現時点でその用 +件(位置を求める)を満たす事ができている。ここまでで自ずと encode() の全 +体像も予想がつきそうな気がする。 -¾×ÆͤϹͤ¨¤Ê¤¤¤Ã¤È¤·¤¿¤¬¡¢¤Á¤ç¤Ã¤È¹Í¤¨¤¿¤é¤¹¤°¤ï¤«¤Ã¤¿¡£prev[] ¤Ë¤Ï¡¢ -°ÊÁ°¤Î¥Ï¥Ã¥·¥åÃͤǵá¤á¤¿Ê¸»úÎó¤Î°ÌÃÖ¤¬Æþ¤Ã¤Æ¤¤¤ë¡£¤Ä¤Þ¤ê¡¢prev[] ¤Ï¥Ï¥Ã -¥·¥å¤¬¾×Æͤ·¤¿¤È¤­¤Î¤¿¤á¤Î¥Ð¥Ã¥Õ¥¡¤À¡£¤³¤Î¥Ï¥Ã¥·¥å¤Ï¥Á¥§¡¼¥óË¡¤À¡£ +衝突は考えないっとしたが、ちょっと考えたらすぐわかった。prev[] には、 +以前のハッシュ値で求めた文字列の位置が入っている。つまり、prev[] はハッ +シュが衝突したときのためのバッファだ。このハッシュはチェーン法だ。 -Î㤨¤Ð¡¢insert() ¤Ç¡¢ +例えば、insert() で、 prev[pos] = hash[hval]; hash[hval] = pos; -¤Ã¤È½èÍý¤ò¤·¤Æ¤¤¤ë¤Î¤À¤«¤é +っと処理をしているのだから hash[hval] = pos1 | @@ -681,47 +681,47 @@ insert() prev[pos2] = pos3 ... -¤È¤¤¤Ã¤¿Ãͤ¬Æþ¤ë»ö¤Ë¤Ê¤ë¡£¤¢¤ëʸ»úÎó(¤Î¥Ï¥Ã¥·¥åÃÍ) hval ¤ËÂФ·¤Æ¡¢¤½¤Î -¼­½ñ¾å¤Î°ÌÃÖ¤Ï pos1, pos2, pos3 ¤È¤¤¤¦¸õÊ䤬¤¢¤ë¤ï¤±¤À¡£¼ÂºÝ¤Ë¤É¤Î pos -¤òÁª¤Ö¤«¤ÏÈæ³Ó¤Ë¤è¤Ã¤Æ¹Ô¤ï¤ì¤ë¤Î¤À¤í¤¦¡£ +といった値が入る事になる。ある文字列(のハッシュ値) hval に対して、その +辞書上の位置は pos1, pos2, pos3 という候補があるわけだ。実際にどの pos +を選ぶかは比較によって行われるのだろう。 -# ¤½¤ì¤Ë¤Ä¤±¤Æ¤â¡¢(C) ¤È (D) ¤ÎÉôʬ¤ò¸«¤ë¤À¤±¤Ç¤â¤³¤Î¥½¡¼¥¹¤¬¤Á¤ç¤Ã¤È -# ±ø¤¤¤³¤È¤¬¤ï¤«¤ë¡£¤â¤¦¾¯¤·¡¢°ú¿ô¤È¤«Ìá¤êÃͤȤ«¹Í¤¨¤Æ¤¯¤ì¤Æ¤âÎɤ«¤Ã -# ¤¿¤Ï¤º¤À¡£¥Ï¥Ã¥·¥å´Ø¿ô¤Ë¤·¤Æ¤â¾¯¤Ê¤¯¤È¤â¥Þ¥¯¥í¤°¤é¤¤¤Ë¤Ï¤·¤è¤¦¤è¡£ +# それにつけても、(C) と (D) の部分を見るだけでもこのソースがちょっと +# 汚いことがわかる。もう少し、引数とか戻り値とか考えてくれても良かっ +# たはずだ。ハッシュ関数にしても少なくともマクロぐらいにはしようよ。 -(E) ¡Á (H) ¤Ë°Ü¤í¤¦¤³¤ì¤Ï¥ë¡¼¥×¤ÎÃæ¿È¤Ç¡¢½èÍý¤ÎËÜÂê¤À¡£¤Þ¤º¥ë¡¼¥×¤Îæ -½Ð¾ò·ï¤ò¸«¤Æ¤ß¤ë¤È +(E) 〜 (H) に移ろうこれはループの中身で、処理の本題だ。まずループの脱 +出条件を見てみると while (remainder > 0 && ! unpackable) { -remainder ¤Ï¡¢¥Ð¥Ã¥Õ¥¡¾å¤ËÆɤ߹þ¤ó¤Àʿʸ¤ÎŤµ¤Ç¤¢¤ë¤«¤é¤³¤ì¤¬¤Ê¤¯¤Ê¤ë -¤Þ¤Ç¥ë¡¼¥×¤¹¤ë¤³¤È¤Ë¤Ê¤ë¡£¤µ¤é¤Ë unpackable ¤È¤¤¤¦¤Î¤Ï¡¢crcio.c ¤Î -putcode() ¤Ç¤½¤ÎÃͤòÀßÄꤷ¤Æ¤¤¤ë²Õ½ê¤¬½Ð¤ÆÍè¤ë¤Î¤À¤¬¡¢Éä¹æ²½¤·¤¿½ÐÎÏ¥µ -¥¤¥º¤¬¸µ¤Î¥µ¥¤¥º¤ò±Û¤¨¤¿¤È¤­¤Ë¿¿¤Ë¤Ê¤ë¡£¤Ä¤Þ¤ê¡¢¤³¤ì°Ê¾å½èÍý¤·¤Æ¤â°µ½Ì -¤Î°ÕÌ£¤¬¤Ê¤¤¤È¤ï¤«¤Ã¤¿¤é¥ë¡¼¥×¤òÈ´¤±¤ë¤ï¤±¤À¡£ +remainder は、バッファ上に読み込んだ平文の長さであるからこれがなくなる +までループすることになる。さらに unpackable というのは、crcio.c の +putcode() でその値を設定している箇所が出て来るのだが、符号化した出力サ +イズが元のサイズを越えたときに真になる。つまり、これ以上処理しても圧縮 +の意味がないとわかったらループを抜けるわけだ。 -¤Ç¤Ï¡¢(E)¤ò¸«¤è¤¦¡£ +では、(E)を見よう。 /* (E) */ lastmatchlen = matchlen; lastmatchoffset = pos - matchpos - 1; --matchlen; -¤Á¤ç¤Ã¤È¸«¤¿¤À¤±¤Ç¤Ï¤ä¤Ï¤ê¤ï¤«¤é¤Ê¤¤¡£¤³¤ì¤é¤ÎÊÑ¿ô¤Ï¤Þ¤ÀͽÁÛ¤·¤«¤·¤Æ¤Ê -¤¤¤«¤é¤Ç¤¢¤ë¡£¤¬¡¢¤ï¤«¤ë¤À¤±¤Î¾ðÊó¤Ï½ñ¤­¤·¤ë¤½¤¦¡£ +ちょっと見ただけではやはりわからない。これらの変数はまだ予想しかしてな +いからである。が、わかるだけの情報は書きしるそう。 ---------------------------------------------------------------------------- -* lastmatchlen °ÊÁ°¤Î matchlen ¤ÎÃÍ (ÊÑ¿ô̾¤«¤é) -* lastmatchoffset °ÊÁ°¥Þ¥Ã¥Á¤·¤¿°ÌÃÖ (ÊÑ¿ô̾¤«¤é) +* lastmatchlen 以前の matchlen の値 (変数名から) +* lastmatchoffset 以前マッチした位置 (変数名から) ---------------------------------------------------------------------------- -°ÊÁ°¤ÎÃͤòlast¡Á¤ËÂàÈò¤·¡¢¿·¤¿¤ÊÃͤòÀßÄꤹ¤ë½àÈ÷¤ò¤·¤Æ¤¤¤ë¤ï¤±¤À¡£¤½¤· -¤Æ¡¢¡Ö¿·¤¿¤ÊÃͤÎÀßÄê¡×¤Ï¡¢--matchlen ¤ÇÁ᮹Ԥï¤ì¤Æ¤¤¤ë¡£¤·¤«¤·¡¢¡Ö¥Þ¥Ã -¥Á¤·¤¿Ä¹¤µ¡×¤ò¤Þ¤À²¿¤â¤·¤Æ¤Ê¤¤¤Î¤Ë -1 ¤¹¤ë¤È¤¤¤¦¤Î¤Ï¤¤¤Ã¤¿¤¤¤É¤¦¤¤¤¦¤³ -¤È¤À¤í¤¦¡© matchlen ¤Ï¥ë¡¼¥×¤ÎƬ¤Ç 2 ¤ËÀßÄꤵ¤ì¤Æ¤¤¤ë¡£¤³¤ì¤¬ 1 ¤Ë¤Ê¤Ã -¤¿¡£ËÜÅö¤Î½é´üÃÍ¤Ï 1 ¤Ê¤Î¤«¡© +以前の値をlast〜に退避し、新たな値を設定する準備をしているわけだ。そし +て、「新たな値の設定」は、--matchlen で早速行われている。しかし、「マッ +チした長さ」をまだ何もしてないのに -1 するというのはいったいどういうこ +とだろう? matchlen はループの頭で 2 に設定されている。これが 1 になっ +た。本当の初期値は 1 なのか? ---------------------------------------------------------------------------- -< ³ÆÊÑ¿ô¤Î½é´üÃÍ > +< 各変数の初期値 > matchlen = 1 matchpos = 0 @@ -731,16 +731,16 @@ putcode() lastmatchoffset = dicsiz - 1 (pos - matchpos - 1) ---------------------------------------------------------------------------- -¤³¤Î (E) ¤Ï¤Þ¤¿¸å¤Ç¸«¤ë»ö¤Ë¤Ê¤ë¤À¤í¤¦¡£ +この (E) はまた後で見る事になるだろう。 -(F) (G) ¤Ç¤¢¤ë¡£¤Þ¤¿¡¢¤½¤Îľ¸å¤Ë¤Ï°ÊÁ°¤Ë¤â¸«¤¿¶­³¦¾ò·ï¤¬¤¢¤ë¡£ +(F) (G) である。また、その直後には以前にも見た境界条件がある。 /* (F) */ /* (G) */ get_next(); match_insert(); if (matchlen > remainder) matchlen = remainder; -if ʸ ¤Ï̵»ë¤·¤Æ´Ø¿ô¤ÎÃæ¿È¤À¤±¤òÄɤ¤¤«¤±¤Æ¤ß¤è¤¦¡£¤Þ¤º¡¢get_next() ¤³ -¤ì¤Ï ¼¡¤Î token ¤ò¼è¤Ã¤Æ¤¯¤ë½èÍý¤À¤ÈͽÁÛ¤·¤Æ¤¢¤ë¡£¤Ï¤¿¤·¤Æ¤É¤¦¤À¤í¤¦¤«¡© +if 文 は無視して関数の中身だけを追いかけてみよう。まず、get_next() こ +れは 次の token を取ってくる処理だと予想してある。はたしてどうだろうか? static void get_next() { @@ -751,10 +751,10 @@ static void get_next() hval = ((hval << 5) ^ text[pos + 2]) & (unsigned)(HSHSIZ - 1); } -remainder ¤ò¾ÃÈñ¤·¡¢pos ¤ò¿Ê¤á¤Æ¤¤¤ë¡£Í½ÁÛÄ̤ê¤À¡£¤Ò¤È¤Þ¤º if ¤Î¾ò·ï¤Ï -̵»ë¤¹¤ë¤È¡¢Ä¾¸å¤Ç hash Ãͤòµá¤áľ¤·¤Æ¤¤¤ë¡£¤³¤Î¥Ï¥Ã¥·¥å´Ø¿ô¤Ï¡¢°ÊÁ°¤Î¥Ï¥Ã -¥·¥åÃͤòÍøÍѤ·¤Æ¤¤¤ë¤¬¡¢¤³¤ì¤Ï pos ¤¬°ÊÁ°¤è¤ê + 1 ¤µ¤ì¤Æ¤¤¤ë¤³¤È¤ò¹Í¤¨ -¤ë¤È´ØÏ¢¤¬¸«¤¨¤ÆÍè¤ë¡£°ÊÁ°¤Îhash´Ø¿ô¤ò pos ¤Î´Ø¿ô¤È¤·¤Æ½ñ¤­Ä¾¤¹¤È +remainder を消費し、pos を進めている。予想通りだ。ひとまず if の条件は +無視すると、直後で hash 値を求め直している。このハッシュ関数は、以前のハッ +シュ値を利用しているが、これは pos が以前より + 1 されていることを考え +ると関連が見えて来る。以前のhash関数を pos の関数として書き直すと x(pos+i) = text[pos + i] @@ -763,20 +763,20 @@ remainder ^ x(pos+2) ) & (unsigned)(HSHSIZ - 1); -¤Ç¤¢¤ê¡¢¤Þ¤¿¡¢º£Å٤Υϥå·¥å´Ø¿ô¤Ï¡¢ +であり、また、今度のハッシュ関数は、 hval(pos+1) = ( hval(pos) << 5 ^ x(pos+1 + 2) ) & (unsigned)(HSHSIZ - 1); -¤À¡¢ÈË»¨¤Ê¤Î¤Ç & (HSHSIZE-1) ¤ò³°¤¹¤È¡¢ +だ、繁雑なので & (HSHSIZE-1) を外すと、 hval(pos+1) = (( x(pos+0) << 5 ^ x(pos+1) ) << 5 ^ x(pos+2) ) << 5 ^ x(pos+3) -¤Ã¤È¤Ê¤ë¡£¤³¤Î¼¡ get_next() ¤¬¸Æ¤Ó½Ð¤µ¤ì¤ì¤Ð¡¢ +っとなる。この次 get_next() が呼び出されれば、 hval(pos+2) = ((( x(pos+0) << 5 ^ x(pos+1) ) << 5 @@ -784,29 +784,29 @@ remainder ^ x(pos+3) ) << 5 ^ x(pos+4) -¤Ç¤¢¤ë¡£½ç¤Ë¥Ï¥Ã¥·¥åÃͤòµá¤á¤ëʸ»úÎóŤòÁý¤ä¤·¤Æ¤¤¤ë¡£¤È¤Ë¤«¤¯¡¢ -get_next() ¤Ï¡¢pos ¤ò¿Ê¤á¡¢remainder ¤ò½Ì¤á¡¢¿·¤¿¤Ê(°ÊÁ°¤è¤ê1ʸ»úŤ¤) -ʸ»úÎó¤Î¥Ï¥Ã¥·¥åÃÍ hval ¤òµá¤á¤ë´Ø¿ô¤Î¤è¤¦¤À¡£ +である。順にハッシュ値を求める文字列長を増やしている。とにかく、 +get_next() は、pos を進め、remainder を縮め、新たな(以前より1文字長い) +文字列のハッシュ値 hval を求める関数のようだ。 -¤·¤«¤·¡¢¤¤¤Ä¤Þ¤Ç¤â hash Ãͤθµ¤È¤Ê¤ëʸ»úÎó¤ò¿­¤Ð¤·¤Æ¤â¤·¤ç¤¦¤¬¤Ê¤¤¤À¤í -¤¦¡£hval ¤Ï¤É¤³¤«¤Ç¤Þ¤¿¥ê¥»¥Ã¥È¤µ¤ì¤ë¤Ï¤º¤À¡£¤Ã¤È»×¤Ã¤Æ¥½¡¼¥¹¤òõ¤·¤Æ -¤ß¤¿¤¬¤½¤Î¤è¤¦¤Ê²Õ½ê¤Ï¸«Åö¤¿¤é¤Ê¤¤¡£¤Ê¤¼¤À¤í¤¦¡©¹Í¤¨¤Æ¤ß¤ë¡¦¡¦¡¦ºÇ½é¤Ï -¤ï¤«¤é¤Ê¤«¤Ã¤¿¤¬¿ô¼°¤ò¤è¤¯¸«¤Æ¤ß¤¿¤é¤ï¤«¤Ã¤¿¡£<< 5 ¤¬¸°¤À¡¢hval(pos+2) -¤Î¼°¤ò¸«¤ë¤È x(pos+0) ¤Ï¡¢<< 5 ¤¬¡¢4²ó¤â¹Ô¤ï¤ì¤Æ¤¤¤ë¤Ä¤Þ¤ê¡¢20¥Ó¥Ã¥È¤Î -¥·¥Õ¥È¤À¡£hval(pos+3) ¤Ê¤é¡¢25¥Ó¥Ã¥È¡¢hval(pos+4) ¤Ê¤é 30 ¥Ó¥Ã¥È¤Î¥·¥Õ -¥È¤À¡£¤µ¤¹¤¬¤Ë¤³¤ì¤À¤±¥·¥Õ¥È¤¹¤ì¤Ð¡¢x(pos+0)¤Î¾ðÊó¤Ï¾Ã¤¨¤Æ¤â¤¤¤¤¤À¤í¤¦¡£ +しかし、いつまでも hash 値の元となる文字列を伸ばしてもしょうがないだろ +う。hval はどこかでまたリセットされるはずだ。っと思ってソースを探して +みたがそのような箇所は見当たらない。なぜだろう?考えてみる・・・最初は +わからなかったが数式をよく見てみたらわかった。<< 5 が鍵だ、hval(pos+2) +の式を見ると x(pos+0) は、<< 5 が、4回も行われているつまり、20ビットの +シフトだ。hval(pos+3) なら、25ビット、hval(pos+4) なら 30 ビットのシフ +トだ。さすがにこれだけシフトすれば、x(pos+0)の情報は消えてもいいだろう。 -¼ÂºÝ¡¢hval ¤Ï²¿Ê¸»úʬ¤Î¾ðÊó¤ò»ý¤Ä¤Î¤À¤í¤¦¡©hval ¤Ï¡¢unsigned int ¤Ç¡¢ -ÉáÄÌ 32 bit ¤Ç¤¢¤ë¤«¤é¡¢6.4 ʸ»úʬ¤À¤í¤¦¤«¡©¤¤¤ä¡¢¼ÂºÝ¤Ë¤Ï¥Ï¥Ã¥·¥åÃͤΠ-·×»»»þ¤ËHSHSIZ (15bit) ¤Ç¥Þ¥¹¥¯¤ò¤«¤±¤Æ¤¤¤ë¤«¤é 15 bit ¤Î¾ðÊó¤·¤«»ý¤¿ -¤Ê¤¤¡£¤Ä¤Þ¤ê¡¢3ʸ»ú¤À¡£¥Ó¥Ã¥È·×»»¤Ï¶ì¼ê¤Ê¤Î¤Ç¿Þ¼¨¤·¤Æ³Îǧ¤·¤è¤¦¡£ +実際、hval は何文字分の情報を持つのだろう?hval は、unsigned int で、 +普通 32 bit であるから、6.4 文字分だろうか?いや、実際にはハッシュ値の +計算時にHSHSIZ (15bit) でマスクをかけているから 15 bit の情報しか持た +ない。つまり、3文字だ。ビット計算は苦手なので図示して確認しよう。 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ hval |--| | | | | | | | | | | | | | | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ -ºÇ½é¤Î hval(0) ¤Ï¡¢x(0), x(1), x(2) ¤ËÂФ·¤Æ¡¢ +最初の hval(0) は、x(0), x(1), x(2) に対して、 <--- 5 -----> <--- 5 -----> <--- 5 -----> +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ @@ -817,53 +817,53 @@ get_next() x(2) -- x x x x x x x x +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ -¤ÎÇÓ¾ŪÏÀÍýϤǤ¢¤ë¡£hval(0) ¤Î»þÅÀ¤Ç x(0) ¤Î¾ðÊó¤Ï 5 ¥Ó¥Ã¥È»Ä¤Ã¤Æ¤¤ -¤ë¤¬ hval(1) ¤Ë¤Ê¤ì¤Ð¾Ã¤¨¤Æ¤·¤Þ¤¦¤Î¤Ï¼«ÌÀ¤Ç¤¢¤ë¡£¤É¤¦¤Ë¤âºÇ½é¤Îʸ»ú¤Ë -´Ø¤·¤Æ¤Ï 5 ¥Ó¥Ã¥È¤·¤«¾ðÊó¤ò»ÈÍѤ·¤Ê¤¤¤È¸À¤¦¤Î¤¬µ¤»ý°­¤¤¤Î¤À¤¬¡¢15 bit -¥µ¥¤¥º¤ÎÊÑ¿ô hval ¤Ë¤Ï¡¢²áµî 3 ʸ»úʬ¤Î¾ðÊó¤·¤«ÊÝ»ý¤µ¤ì¤Ê¤¤¤Î¤Ï´Ö°ã¤¤ -¤Ê¤¤¡£get_next() ¤Î½èÍý¤ò¸«¤ì¤Ð¡¢°ÌÃÖ pos ¤ËÂФ·¤Æ¡¢hval ¤Ï¾ï¤Ë pos, -pos+1, pos+2 ¤Î¾ðÊó¤·¤«»ý¤¿¤Ê¤¤¤ï¤±¤À¡£¤³¤ì¤Ï½ÅÍפÀ¡£¥á¥â¤·¤è¤¦ +の排他的論理和である。hval(0) の時点で x(0) の情報は 5 ビット残ってい +るが hval(1) になれば消えてしまうのは自明である。どうにも最初の文字に +関しては 5 ビットしか情報を使用しないと言うのが気持悪いのだが、15 bit +サイズの変数 hval には、過去 3 文字分の情報しか保持されないのは間違い +ない。get_next() の処理を見れば、位置 pos に対して、hval は常に pos, +pos+1, pos+2 の情報しか持たないわけだ。これは重要だ。メモしよう ---------------------------------------------------------------------------- - * hval hash[]¤Î¥¤¥ó¥Ç¥Ã¥¯¥¹¡£¸½ºß°ÌÃÖ pos ¤ËÂФ·¤Æ¡¢ - text[pos], text[pos+1], text[pos+2] ¤Î¥Ï¥Ã¥·¥åÃͤǡ¢ÏÀÍýŪ¤Ë¤Ï + * hval hash[]のインデックス。現在位置 pos に対して、 + text[pos], text[pos+1], text[pos+2] のハッシュ値で、論理的には hval == text[pos] + text[pos+1] + text[pos+2] - ¤ÈƱµÁ + と同義 ---------------------------------------------------------------------------- -¤È¤³¤í¤Ç¡¢Á°²ó¡¢hval ¤Î·×»»¤Èinsert() ¤Ï¥»¥Ã¥È¤À¤È¸À¤Ã¤¿¡£º£²ó¤Ï¤É¤¦¤À -¤í¤¦¡©¼¡¤Î match_insert() ¤ò¸«¤Æ¤ß¤ë¡£ +ところで、前回、hval の計算とinsert() はセットだと言った。今回はどうだ +ろう?次の match_insert() を見てみる。 static void match_insert() { - ... ¾Êά ... + ... 省略 ... prev[pos & (dicsiz - 1)] = hash[hval]; hash[hval] = pos; } -¡¦¡¦¡¦¶¯Å¨¤Ç¤¢¤Ã¤¿¡£¶¯Å¨¤¹¤®¤¿¤Î¤Çƨ¤²¤Æ¡¢ºÇ¸å¤Î2 ¹Ô¤À¤±¤ËÃåÌܤ·¤¿¡£¤³ -¤ì¤Ï¡¢insert()¤ÈƱ¤¸¤À¡£Í½ÁÛ¤ÏÅö¤¿¤Ã¤Æ¤¤¤ë¡£get_next() ¤Ç hval ¤ò¹¹¿· -¤·¤¿¸å¤Ï¡¢¤³¤Î match_insert() ¤Ç¡¢prev[] ¤È hash[] ¤ò¹¹¿·¤¹¤ë¤ï¤±¤À¡£ -¤½¤·¤Æ¡¢match_insert() ¤Î¾Êά¤·¤¿Éôʬ¤Ï¡¢¤É¤¦¤ä¤é matchpos, matchlen, -too_flag ¤ò¹¹¿·¤·¤Æ¤¤¤ë¤À¤±¤Î¤è¤¦¤À¡£¤³¤ì¤¬ËÜÅö¤Ê¤é match_insert()¤Ç¡¢ -insert()¤Î½èÍý¤ò¤»¤º¡¢´Ø¿ô¤òʬ¤±¤ë¤«¤·¤¿Êý¤¬Îɤµ¤½¤¦¤À¡£(¿¿µ¶¤ÎÄø¤Ï¾Ü -ºÙ¤ò¸«¤Æ¤«¤é¤Ë¤Ê¤ë) +・・・強敵であった。強敵すぎたので逃げて、最後の2 行だけに着目した。こ +れは、insert()と同じだ。予想は当たっている。get_next() で hval を更新 +した後は、この match_insert() で、prev[] と hash[] を更新するわけだ。 +そして、match_insert() の省略した部分は、どうやら matchpos, matchlen, +too_flag を更新しているだけのようだ。これが本当なら match_insert()で、 +insert()の処理をせず、関数を分けるかした方が良さそうだ。(真偽の程は詳 +細を見てからになる) -¤ª¤â¤à¤í¤Ë¸å³¤Î½èÍý (H) ¤ò¸«¤ë¤È¡¢ +おもむろに後続の処理 (H) を見ると、 /* (H) */ if (matchlen > lastmatchlen || lastmatchlen < THRESHOLD) { -¤³¤ì¤¬¿¿¤Ê¤é¡Ö¸«¤Ä¤«¤é¤Ê¤«¤Ã¤¿¾õÂ֡פÈͽÁÛ¤·¤¿(¤Ê¤¼¤À¤í¡©)¡£¤½¤·¤Æ¡¢ -lastmatchlen ¤Ï½é´ü¾õÂÖ¤Ç¤Ï 2 ¤Ç¤¢¤ë¡£Í½ÁÛ¤·¤¿¾ò·ï¤ÏµÕ¤«¡© matchlen ¤Þ -¤ï¤ê¤ÏͽÁۤФ«¤ê¤Ç¿Ê¤á¤¹¤®¤¿¡£¤½¤·¤Æ¤É¤¦¤ä¤é match_insert() ¤òÆɤߤȤ« -¤Ê¤±¤ì¤Ð¤³¤ÎÀè¤âʬ¤«¤é¤º¤¸¤Þ¤¤¤Ë¤Ê¤ê¤½¤¦¤À¡£ +これが真なら「見つからなかった状態」と予想した(なぜだろ?)。そして、 +lastmatchlen は初期状態では 2 である。予想した条件は逆か? matchlen ま +わりは予想ばかりで進めすぎた。そしてどうやら match_insert() を読みとか +なければこの先も分からずじまいになりそうだ。 -¤³¤Î¤Þ¤Þ match_insert() ¤ò¾ÜºÙ¤Ë²òÀϤ¹¤ë»ö¤Ë¤·¤è¤¦¡£match_insert() -¤ò¤¹¤Ù¤ÆºÆ·Ç¤¹¤ë¡£ +このまま match_insert() を詳細に解析する事にしよう。match_insert() +をすべて再掲する。 -/* ¸½ºß¤Îʸ»úÎó¤ÈºÇĹ°ìÃפ¹¤ëʸ»úÎó¤ò¸¡º÷¤·¡¢¥Á¥§¡¼¥ó¤ËÄɲ乤ë */ +/* 現在の文字列と最長一致する文字列を検索し、チェーンに追加する */ static void match_insert() { @@ -916,7 +916,7 @@ static void match_insert() hash[hval] = pos; } -¤Þ¤º¡¢½é´ü²½Éôʬ¤ÎÁ°È¾ +まず、初期化部分の前半 max = maxmatch; /* MAXMATCH; */ if (matchlen < THRESHOLD - 1) matchlen = THRESHOLD - 1; @@ -924,27 +924,27 @@ static void match_insert() off = 0; -maxmatch ¤Ï¡¢¸ÇÄêÃÍ¤Ç 256 ¤À¡¢¤À¤«¤é max ¤â 256 -2¹ÔÌܤΠif ʸ¤Ï¡¢¤³¤ì¤Þ¤Ç¤·¤Ä¤³¤¤¤¯¤é¤¤¤Ë½Ð¤ÆÍ褿¾ò·ï¤Ë»÷¤Æ¤¤¤ë¤¬¡¢º£ -²ó¤Ï¾ò·ï¤òËþ¤¿¤¹¤é¤·¤¤¡£¤³¤ì¤Þ¤Ç¤Ï¡¢ +maxmatch は、固定値で 256 だ、だから max も 256 +2行目の if 文は、これまでしつこいくらいに出て来た条件に似ているが、今 +回は条件を満たすらしい。これまでは、 if (matchlen > remainder) matchlen = remainder; -¤È¤¤¤¦¾ò·ï¤À¤Ã¤¿¡£¤½¤·¤Æº£²ó¤Ï¡¢ +という条件だった。そして今回は、 if (matchlen < THRESHOLD - 1) matchlen = THRESHOLD - 1; -¤À¤«¤é¡¢Á´ÂÎŪ¤Ë matchlen ¤ÎÃͤϡ¢ +だから、全体的に matchlen の値は、 THRESHOLD-1 <= matchlen <= remainder -¤Ä¤Þ¤ê¡¢ +つまり、 - 2 <= matchlen <= ¥Ð¥Ã¥Õ¥¡¤Ë»Ä¤Ã¤¿¥Æ¥­¥¹¥ÈĹ + 2 <= matchlen <= バッファに残ったテキスト長 -¤ÎÈϰϤËǼ¤á¤é¤ì¤ë¤è¤¦¤À¡£¤³¤³¤Ç¤Ï¡¢matchlen ¤Ï²¼¸ÂÃͤò²¼²ó¤ë¤Î¤Ç2 ¤Ë -ÀßÄꤵ¤ì¤ë¡£¼¡¤Ë matchpos, off ¤¬½é´ü²½¤µ¤ì¡£°Ê²¼¤Î¿Þ¤Î¾õÂ֤ˤʤ롣 -(pos, remainder ¤Ï¡¢get_next() ¤Ç¹¹¿·¤µ¤ì¤Æ¤¤¤ë¤³¤È¤ËÃí°Õ) +の範囲に納められるようだ。ここでは、matchlen は下限値を下回るので2 に +設定される。次に matchpos, off が初期化され。以下の図の状態になる。 +(pos, remainder は、get_next() で更新されていることに注意) ---------------------------------------------------------------------------- @@ -959,43 +959,43 @@ maxmatch <------ remainder ------------> - |--- ¤³¤Î°ÌÃ֤˺ǽé¤Î ---------| - ¥Ç¡¼¥¿¤¬Æɤޤì¤Æ¤¤¤ë + |--- この位置に最初の ---------| + データが読まれている ---------------------------------------------------------------------------- -½é´ü²½Éôʬ¤Î¸åȾ +初期化部分の後半 for (h = hval; too_flag[h] && off < maxmatch - THRESHOLD; ) { h = ((h << 5) ^ text[pos + (++off) + 2]) & (unsigned)(HSHSIZ - 1); } if (off == maxmatch - THRESHOLD) off = 0; -h ¤Ï¡¢too_flag[] ¤¬º£¤Î¤È¤³¤í¤¹¤Ù¤Æ0¤À¤«¤é hval ¤À¡£(too_flag ¤Ï¡¢h ¤Ä -¤Þ¤ê hval ¤ò¥¤¥ó¥Ç¥Ã¥¯¥¹¤Ë¼è¤ë¤é¤·¤¤¡£hash[] ¤ÈƱ¤¸¤À¡£ºÆ·Ç¤Ï¤·¤Ê¤¤¤¬ -¥á¥â¤Ë½ñ¤­²Ã¤¨¤Æ¤ª¤³¤¦) +h は、too_flag[] が今のところすべて0だから hval だ。(too_flag は、h つ +まり hval をインデックスに取るらしい。hash[] と同じだ。再掲はしないが +メモに書き加えておこう) -off ¤Ï¡¢pos ¤Î°ÌÃÖ¤«¤é¤Î¥ª¥Õ¥»¥Ã¥È¤Î¤è¤¦¤À(h ¤ò¹¹¿·¤¹¤ë for ʸ¤ÎÃæ¿È¤« -¤é)¡£¿Þ¤â¤½¤Î°ÌÃ֤˽ñ¤¤¤¿¡£ºÇ¸å¤Î if ʸ¤Ï off ¤¬¾å¸Â¤Ë㤷¤¿¾ì¹ç¤Ë0 ¤Ë -ºÆ½é´ü²½¤·¤Æ¤¤¤ë¡£¤è¤¯¤ï¤«¤é¤Ê¤¤¤Î¤Ç̵»ë¤·¤è¤¦¡£for ʸ¤ÎÃæ¿È¤«¤éh ¤ä -off ¤ÎÍÑÅӤϤɤ¦¤âÀèÆɤߤ·¤¿¥Ï¥Ã¥·¥åÃͤȤ½¤ÎÀèÆɤߤΰÌÃ֤ʤΤǤϤʤ¤¤« -¤ÈÁÛÁü¤¹¤ë¡£too_flag[] ¤Î¾õÂ֤ˤè¤Ã¤ÆÀèÆɤߤ¹¤Ù¤­Ãͤ¬ÊѤï¤ë¤Î¤À¤í¤¦¤«¡© +off は、pos の位置からのオフセットのようだ(h を更新する for 文の中身か +ら)。図もその位置に書いた。最後の if 文は off が上限に達した場合に0 に +再初期化している。よくわからないので無視しよう。for 文の中身からh や +off の用途はどうも先読みしたハッシュ値とその先読みの位置なのではないか +と想像する。too_flag[] の状態によって先読みすべき値が変わるのだろうか? -¤È¤Ë¤«¤¯½èÍý¤ÎËÜÂê¤ËÆþ¤ë»ö¤Ë¤·¤è¤¦¡£¤Þ¤º¡¢¤³¤Î´Ø¿ô¤Ë¸½¤ì¤ë¶É½êÊÑ¿ô¤òÎó -µó¤·¤Æ¤ª¤³¤¦ +とにかく処理の本題に入る事にしよう。まず、この関数に現れる局所変数を列 +挙しておこう unsigned int scan_pos, scan_end, len; unsigned char *a, *b; unsigned int chain, off, h, max; -off, h, max ¤Ï¤¹¤Ç¤Ë½Ð¤ÆÍ褿¤Î¤Ç»Ä¤ê¤Ï +off, h, max はすでに出て来たので残りは scan_pos, scan_end, len, a, b, chain -¤À¡¢¤³¤ì¤À¤±¤ÎÊÑ¿ô¤Î°ÕÌ£¤ò²òÆɤ·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£ÊÑ¿ô¤Ï¾õÂÖ¤òɽ¤¹¤«¤é¡¢ -¤½¤Î¿ô¤¬Â¿¤¤¤È¸À¤¦¤Î¤Ï¤½¤ì¤À¤±Ê£»¨¤Ê½èÍý¤À¤È¤¤¤¦¤³¤È¤À¡£¤á¤²¤ë¡£ +だ、これだけの変数の意味を解読しなくてはならない。変数は状態を表すから、 +その数が多いと言うのはそれだけ複雑な処理だということだ。めげる。 -¤³¤Î´Ø¿ô¤Î¥á¥¤¥ó¤È¤Ê¤ë¥ë¡¼¥×¤ÎÃæ¤ò¤¶¤Ã¤Èį¤á¤Æ¤ß¤ë¤µ¤é¤ËÆâÉô¤Ë¥ë¡¼¥×¤¬ -¤¢¤ë¡£¤Ò¤È¤Þ¤º¡¢Æó½Å¥ë¡¼¥×¤ÎÃæ¿È¤ò¾Êά¤·¤è¤¦¡£ +この関数のメインとなるループの中をざっと眺めてみるさらに内部にループが +ある。ひとまず、二重ループの中身を省略しよう。 for (;;) { chain = 0; @@ -1004,7 +1004,7 @@ off, h, max while (scan_pos > scan_end) { chain++; - ... ά ... + ... 略 ... } if (chain >= LIMIT) @@ -1017,19 +1017,19 @@ off, h, max h = hval; } -¤Þ¤º¡¢Á°È¾Éôʬ¤«¤é +まず、前半部分から chain = 0; scan_pos = hash[h]; scan_end = (pos > dicsiz) ? pos + off - dicsiz : off; -chain, scan_pos, scan_end ¤Ï¤¹¤Ù¤Æ while ¥ë¡¼¥×¤ËÅϤµ¤ì¤ë¤Ù¤­ÊÑ¿ô¤À¡£ -¤µ¤é¤Ë¡¢while ¤Î¸å¤Ë¤Ï¡¢scan_pos, scan_end ¤Ï¸½¤ì¤Ê¤¤¤«¤é(²¾¤Ë while -¥ë¡¼¥×¤¬1¤Ä¤Î´Ø¿ô¤À¤Ã¤¿¤È¤¹¤ë¤È)¤³¤ì¤é¤Ï while ¥ë¡¼¥×Éô¤Î°ú¿ô(ÆþÎÏ)¤À¡£ -¤³¤Î2¤Ä¤ÎÊÑ¿ô¤Ï¤É¤¦¤ä¤ê¤¯¤ê¤·¤è¤¦¤È¤â¡¢while ¥ë¡¼¥×ÉôÆâ¤Î¾õÂÖ¤·¤«É½¤µ -¤Ê¤¤¤Î¤Ç¡¢¤³¤³¤Ç¤Ï̵»ë¤·¤è¤¦¡£ +chain, scan_pos, scan_end はすべて while ループに渡されるべき変数だ。 +さらに、while の後には、scan_pos, scan_end は現れないから(仮に while +ループが1つの関数だったとすると)これらは while ループ部の引数(入力)だ。 +この2つの変数はどうやりくりしようとも、while ループ部内の状態しか表さ +ないので、ここでは無視しよう。 -while ¥ë¡¼¥×¤Î¸å¤ò¸«¤Æ¤ß¤ë¤È +while ループの後を見てみると if (chain >= LIMIT) too_flag[h] = 1; @@ -1040,104 +1040,104 @@ while off = 0; h = hval; -chain ¤¬ LIMIT¤ò±Û¤¨¤¿¾ì¹ç¡¢too_flag[h] = 1 ¤È¤·¤Æ¤¤¤ë¡£chain ¤Ï¡¢¤¶¤Ã -¤È¸«¤Æ¡¢while ¥ë¡¼¥×¤Î¥«¥¦¥ó¥¿¤é¤·¤¤¤¬¡¢LIMIT ¤Ï 0x100 ¤À¡£¤É¤¦¤Ë¤âÎã -³°¾ò·ï¤Ã¤Ý¤¤(LIMIT¤È¤¤¤¦Ì¾Á°¤ä¿ôÃͤ¬¤½¤¦»×¤ï¤»¤ë)¤Î¤Ç¤³¤³¤Ç¤Ï̵»ë¤·¤è -¤¦¡£while ¥ë¡¼¥×¤¬ 256°Ê¾å²ó¤ë²ÄǽÀ­¤¬¤¢¤ëÅÀ¤À¤±¿´¤Ë¤È¤É¤á¤Æ¤ª¤³¤¦¡£ +chain が LIMITを越えた場合、too_flag[h] = 1 としている。chain は、ざっ +と見て、while ループのカウンタらしいが、LIMIT は 0x100 だ。どうにも例 +外条件っぽい(LIMITという名前や数値がそう思わせる)のでここでは無視しよ +う。while ループが 256以上回る可能性がある点だけ心にとどめておこう。 -¼¡¤Î¾ò·ï¤Ç¤Ï¡¢matchlen ¤È off ¤¬¾ò·ïȽÃǤµ¤ì¤Æ¤¤¤ë¡£¤È¤¤¤¦¤³¤È¤Ï¤³¤Î¤É -¤Á¤é¤«¡¢¤¢¤ë¤¤¤ÏξÊý¤Ï while ¥ë¡¼¥×¤ÎÊÖ¤êÃÍ(½ÐÎÏ)¤À¡£¤¶¤Ã¤È -match_insert()Á´ÂΤò¸«¤Æ¤ß¤ë¤È off ¤ÏºÇ½é¤È¤³¤Îľ¸å¤Ç¤·¤«¹¹¿·¤µ¤ì¤Ê¤¤ -¤é¤·¤¤¡£¤Ä¤Þ¤ê¡¢while ¥ë¡¼¥×Éô¤ÎÊÖ¤êÃͤÏmatchlen ¤ÎÊý¤À¡£ -¤³¤Î¾ò·ï¤Ï for () ¥ë¡¼¥×¤Îæ½Ð¾ò·ï¤Ç¤â¤¢¤ë¡£¿´¤Ë¤È¤É¤á¤Æ¡¢¼¡¤Ë¿Ê¤à¡£ +次の条件では、matchlen と off が条件判断されている。ということはこのど +ちらか、あるいは両方は while ループの返り値(出力)だ。ざっと +match_insert()全体を見てみると off は最初とこの直後でしか更新されない +らしい。つまり、while ループ部の返り値はmatchlen の方だ。 +この条件は for () ループの脱出条件でもある。心にとどめて、次に進む。 max = off + 2; off = 0; h = hval; -¤Õ¤à¡£¤è¤¯¤ï¤«¤é¤Ê¤¤¡£¤·¤«¤·ÃíÌܤ¹¤Ù¤­ÅÀ¤Ï¤¢¤ë¡£off ¤Ï¤³¤³¤Ç¡¢0 ¤Ë¤Ê¤ë -¤¬¤³¤ì°Ê¹ß¤Ï off ¤ÎÃͤÏÊѤï¤é¤Ê¤¤¡£¤Ä¤Þ¤ê¡¢off ¤ÏºÇ½é¤Ï²¿¤é¤«¤ÎÃÍ¤Ç -while ¥ë¡¼¥×Éô¤ËÅϤµ¤ì¤ë¤¬¡¢¤½¤Î¼¡¤«¤é¤Ï¡¢0 ¤À¡£¤³¤Î for ¥ë¡¼¥×¤¬²¿ÅÙ -²ó¤í¤¦¤È¤â 0 ¤À¡£h ¤âƱ¤¸¤ÇºÇ½é¤Ï²¿¤é¤«¤ÎÃͤò»ý¤Ä¤¬¡¢2²óÌܤΥ롼¥×°Ê¹ß¡¢ -h ¤Ï hval ¤À¡£max ¤Ï¡¢off ¤ò 0 ¤Ë¤¹¤ëľÁ°¤Ë¹¹¿·¤·¤Æ¤¤¤ë¤«¤é¡¢h ¤ä off -¤È»ö¤Ê¤ê¡¢3¤Ä¤Î¾õÂÖ¤ò»ý¤Ä¡¢¤¹¤Ê¤ï¤Á¡£maxmatch, off+2, 2 ¤À¡£ +ふむ。よくわからない。しかし注目すべき点はある。off はここで、0 になる +がこれ以降は off の値は変わらない。つまり、off は最初は何らかの値で +while ループ部に渡されるが、その次からは、0 だ。この for ループが何度 +回ろうとも 0 だ。h も同じで最初は何らかの値を持つが、2回目のループ以降、 +h は hval だ。max は、off を 0 にする直前に更新しているから、h や off +と事なり、3つの状態を持つ、すなわち。maxmatch, off+2, 2 だ。 -¤¤¤ä¡¢Ã¦½Ð¾ò·ï¤ò¸«¤Æ¤ß¤ë¤È off == 0 ¤Ê¤é break ¤È¤¢¤ë¡£¤Ä¤Þ¤ê¡¢¤³¤Î -for ¥ë¡¼¥×¤Ï¤É¤ó¤Ê¤Ë´èÄ¥¤Ã¤Æ¤â2²ó¤·¤«²ó¤é¤Ê¤¤¤é¤·¤¤¡£¤ä¤Ã¤Ñ¤ê max ¤â 2 -¤Ä¤Î¾õÂÖ¤·¤«»ý¤¿¤Ê¤¤¤è¤¦¤À¡£ +いや、脱出条件を見てみると off == 0 なら break とある。つまり、この +for ループはどんなに頑張っても2回しか回らないらしい。やっぱり max も 2 +つの状態しか持たないようだ。 -¤³¤ì¤Ç¡¢1 ²óÌÜ¡¢2²óÌÜ¤Ë while ¥ë¡¼¥×Éô¤ËÆþ¤ëľÁ°¤Î¾õÂÖ¤¬½ñ¤±¤ë¡£¤³¤Î´Ø -¿ô match_insert() ¤Ï¡¢while ¥ë¡¼¥×Éô¤ò1²ó¤«2²ó¼Â¹Ô¤¹¤ë½èÍý¤È¸À¤¦¤ï¤±¤À¡£ +これで、1 回目、2回目に while ループ部に入る直前の状態が書ける。この関 +数 match_insert() は、while ループ部を1回か2回実行する処理と言うわけだ。 -¤³¤³¤Ç̵»ë¤·¤Æ¤¤¤¿¡£while ¥ë¡¼¥×Éô¤ÎÆþÎϤȤʤë scan_pos, scan_end -¤â¤½¤ì¤¾¤ì¤É¤Î¤è¤¦¤Ê¾õÂ֤ˤʤ뤫½ñ¤¤¤Æ¤ª¤¯ +ここで無視していた。while ループ部の入力となる scan_pos, scan_end +もそれぞれどのような状態になるか書いておく ---------------------------------------------------------------------------- -< 1²óÌÜ > - h = ²¿¤« - off = ²¿¤« +< 1回目 > + h = 何か + off = 何か max = maxmatch scan_pos = hash[h] - scan_end = pos + off - dicsiz (¤¢¤ë¤¤¤Ï¡¢off) + scan_end = pos + off - dicsiz (あるいは、off) matchlen = 2 matchpos = pos -< 2²óÌÜ > +< 2回目 > h = hval off = 0 - max = Á°¤Î off + 2 + max = 前の off + 2 scan_pos = hash[hval] - scan_end = pos - dicsiz (¤¢¤ë¤¤¤Ï¡¢0) + scan_end = pos - dicsiz (あるいは、0) matchlen = ? matchpos = ? ---------------------------------------------------------------------------- -¾åµ­¤Ï°ìÈ̲½¤·¤¿¾ì¹ç¤À¤¬¡¢º£²ó(½é²ó)¤Î¾ì¹ç¡¢h ¤ä off ¤ÎÃͤϡ¢hval ¤Ç¤¢ -¤ê¡¢0 ¤À¤Ã¤¿¡£2²óÌܥ롼¥×¤Î¤È¤­¤Î¾õÂÖ¤ÈƱ¤¸¤Ç¤¢¤ë¡£2²ó¤Î¥ë¡¼¥×¤Î°ã¤¤¤Ï -max ¤ÎÃͤ¬matchpos ¤Ç¤¢¤ë¤« off+2 (¤¹¤Ê¤ï¤Á2)¤Ç¤¢¤ë¤«¤Î°ã¤¤¤·¤«¤Ê¤¤¤è¤¦¤À¡£ +上記は一般化した場合だが、今回(初回)の場合、h や off の値は、hval であ +り、0 だった。2回目ループのときの状態と同じである。2回のループの違いは +max の値がmatchpos であるか off+2 (すなわち2)であるかの違いしかないようだ。 -¤³¤³¤Ï¡¢¾ò·ï¤ò¾¯¤Ê¤¯¤¹¤ë¤¿¤á¤Ë¤³¤Î¾ì¹ç¤À¤±¤Ë¤·¤Ü¤Ã¤Æ½èÍý¤ò¹Í¤¨¤è¤¦¡£ -while ¥ë¡¼¥×¤Î2²ó¤Î¸Æ¤Ó½Ð¤·¤ò¹Ô¤¦ºÝ¤Î¾õÂ֤ϰʲ¼¤ÎÄ̤ê¤Ë½ñ¤­Ä¾¤»¤ë¡£ +ここは、条件を少なくするためにこの場合だけにしぼって処理を考えよう。 +while ループの2回の呼び出しを行う際の状態は以下の通りに書き直せる。 ---------------------------------------------------------------------------- -< 1²óÌÜ > +< 1回目 > h = hval off = 0 max = maxmatch scan_pos = hash[hval] - scan_end = pos - dicsiz (¤¢¤ë¤¤¤Ï¡¢0) + scan_end = pos - dicsiz (あるいは、0) matchlen = 2 matchpos = pos -< 2²óÌÜ > +< 2回目 > h = hval off = 0 max = 2 scan_pos = hash[hval] - scan_end = pos - dicsiz (¤¢¤ë¤¤¤Ï¡¢0) + scan_end = pos - dicsiz (あるいは、0) matchlen = ? matchpos = ? ---------------------------------------------------------------------------- -¤¦¡¼¤ó¡¢¤Þ¤À¡¢¤¹¤Ã¤­¤ê¤·¤Ê¤¤¡£²¿¤¬¤¹¤Ã¤­¤ê¤·¤Ê¤¤¤«¤È¤¤¤¦¤È scan_end ¤Î -ÃͤÀ¡£¤³¤ì¤¬²¿¤ò°ÕÌ£¤¹¤ë¤Î¤«¤¬¤è¤¯¤ï¤«¤é¤Ê¤¤¡£scan_pos ¤Ï¡¢¤ï¤«¤ë¤Î¤«¡© -¤È¤¤¤¦¤È¡¢¤ï¤«¤ë¡£hash[hval]¤À¤«¤é¸½ºß¤Îʸ»úÎó¤ÈƱ¤¸Ê¸»úÎó¤Î¼­½ñ¾å¤Î°Ì -ÃÖ¤À¡£¤µ¤é¤Ë¡¢¸½»þÅÀ¤Ç¤Ï get_next() ¤Ç¡¢hval ¤ò¹¹¿·¤·¤Æ¤«¤é insert() -¤ò¹Ô¤Ã¤Æ¤¤¤Ê¤¤¤Î¤Ç¡¢hash[hval] ¤Ë¤Ï²¿¤âÆþ¤Ã¤Æ¤¤¤Ê¤¤¡£¤¹¤Ê¤ï¤Á 0 ¤À¡£ +うーん、まだ、すっきりしない。何がすっきりしないかというと scan_end の +値だ。これが何を意味するのかがよくわからない。scan_pos は、わかるのか? +というと、わかる。hash[hval]だから現在の文字列と同じ文字列の辞書上の位 +置だ。さらに、現時点では get_next() で、hval を更新してから insert() +を行っていないので、hash[hval] には何も入っていない。すなわち 0 だ。 scan_end = (pos > dicsiz) ? pos + off - dicsiz : off; -¤ò¹Í¤¨¤è¤¦¡£off ¤Ï¡¢0 ¤À¤«¤é +を考えよう。off は、0 だから scan_end = (pos > dicsiz) ? pos - dicsiz : 0; -¤Ê¤ï¤±¤À¡£¤µ¤é¤Ë¡¢pos¤Ï¸½»þÅÀ¤Ç dicbit+1 ¤Ç¤¢¤ë¤«¤é¡¢1 ¤À¡£¿Þ¤Ë½ñ¤³¤¦¡£ +なわけだ。さらに、posは現時点で dicbit+1 であるから、1 だ。図に書こう。 ---------------------------------------------------------------------------- @@ -1148,8 +1148,8 @@ while +-------------+-------------+-------------+-------------+---+ ^ ^ `-pos(=dicsiz+1) | | - | scan_end ¤Ï¤³¤ÎÊÕ(1) - scan_pos ¤Ï¤³¤ÎÊÕ(0) + | scan_end はこの辺(1) + scan_pos はこの辺(0) h = hval off = 0 @@ -1157,22 +1157,22 @@ while ---------------------------------------------------------------------------- -¤Ä¤¤¤Ë¡¢text[] ¥Ð¥Ã¥Õ¥¡¤Îº¸È¾Ê¬¤Ë»Ø¤·¤«¤«¤ë¡£¤³¤ì¤¬²¿¤Ê¤Î¤«¤Ï¸½»þÅÀ¤Ç -¤ÏÌÀ³Î¤Ë½ñ¤¤¤Æ¤Ê¤«¤Ã¤¿¤¬Í½ÁÛ¤¹¤ë¤È¤³¤Îº¸È¾Ê¬¤Ï¥º¥Ð¥ê¼­½ñ¤À¡£¸À¤¤ÀÚ¤Ã¤Æ -¤ä¤í¤¦¡£º£¤Þ¤Ç¼­½ñ¤é¤·¤¤(dicsiz¤Î¥µ¥¤¥º¤ò»ý¤Ä)¥Ð¥Ã¥Õ¥¡¤Ï hash[] ¤ä -prev[] ¤¬¤¢¤Ã¤¿¤¬¡¢hash[], prev[] ¤ÎÍÑÅӤϤ⤦ÌÀ³Î¤Ç¤¢¤ë¡£¼­½ñ¤È¤Ê¤êÆÀ -¤ë¥Ð¥Ã¥Õ¥¡¤Ï¤â¤¦¤³¤Î text[] ¤·¤«¤Ê¤¤¤Î¤À¡£ +ついに、text[] バッファの左半分に指しかかる。これが何なのかは現時点で +は明確に書いてなかったが予想するとこの左半分はズバリ辞書だ。言い切って +やろう。今まで辞書らしい(dicsizのサイズを持つ)バッファは hash[] や +prev[] があったが、hash[], prev[] の用途はもう明確である。辞書となり得 +るバッファはもうこの text[] しかないのだ。 -¤µ¤é¤Ë¡¢º¸È¾Ê¬¤Ë¸Â¤é¤º¤³¤Î text[] Á´ÂΤ¬¼­½ñ¤Ç¤¢¤í¤¦¤ÈͽÁÛ¤¹¤ë¡£¤³¤ì¤Ï -¤¿¤À¤Î´ª¤À¤¬ text[] ¤Ï´Ä¾õ¥Ð¥Ã¥Õ¥¡¤Ê¤Î¤Ç¤Ï¤Ê¤«¤í¤¦¤«¤È¹Í¤¨¤Æ¤¤¤ë¡£ +さらに、左半分に限らずこの text[] 全体が辞書であろうと予想する。これは +ただの勘だが text[] は環状バッファなのではなかろうかと考えている。 -# ºÇ½é¤ÎÊý¤Ç prev[] ¤¬¼­½ñ¤À¤ÈͽÁÛ¤·¤¿¤¬´Ö°ã¤Ã¤¿Í½ÁÛ¤ò¤·¤Æ¤¤¤¿¤³¤È¤Ë¤³ -# ¤Î»þÅÀ¤Çµ¤¤Å¤¤¤¿¡£prev[] ¤¬¼­½ñ¤ÈƱ¤¸¥µ¥¤¥º¤ò»ý¤ÄÍýͳ¤Ï¤Þ¤À¤è¤¯¤ï¤« -# ¤é¤Ê¤¤¡£ +# 最初の方で prev[] が辞書だと予想したが間違った予想をしていたことにこ +# の時点で気づいた。prev[] が辞書と同じサイズを持つ理由はまだよくわか +# らない。 -¤³¤Î»þÅÀ¤Ç¤Ï¤Þ¤À scan_pos ¤ä scan_end ¤Î¿¿¤Î°ÕÌ£¤Ï¤ï¤«¤é¤Ê¤¤¡£off ¤Î¤³ -¤È¤ò̵»ë¤·¤Æ¤¤¤ë¤«¤éͽÁÛ¤âΩ¤Á¤Ë¤¯¤¤¤¬¡¢¤Ò¤È¤Þ¤º½é´ü¾õÂÖ¤¬¤É¤¦¤¤¤Ã¤¿¤â -¤Î¤«¤Ï¤ï¤«¤Ã¤¿¤Î¤Ç¤³¤Î¤Þ¤Þ¡¢while ¥ë¡¼¥×Éô¤ò¸«¤Æ¤ß¤¿¤¤¤È»×¤¦¡£ +この時点ではまだ scan_pos や scan_end の真の意味はわからない。off のこ +とを無視しているから予想も立ちにくいが、ひとまず初期状態がどういったも +のかはわかったのでこのまま、while ループ部を見てみたいと思う。 while (scan_pos > scan_end) { chain++; @@ -1193,7 +1193,7 @@ prev[] scan_pos = prev[scan_pos & (dicsiz - 1)]; } -¤Þ¤º¡¢if ʸ¤Î¾ò·ï¤òËþ¤¿¤µ¤Ê¤¤¾ì¹ç¤À¤±¤ò¹Í¤¨¤ë¡£ +まず、if 文の条件を満たさない場合だけを考える。 while (scan_pos > scan_end) { chain++; @@ -1205,61 +1205,61 @@ prev[] } -off¤Ï 0 ¤Ê¤Î¤Ç¡¢text[scan_pos + matchlen] != text[pos + matchlen] ¤È¤¤ -¤¦¾ò·ï¤Î¾ì¹ç¤òÁÛÄꤹ¤ë¤ï¤±¤À¤¬¡¢ +offは 0 なので、text[scan_pos + matchlen] != text[pos + matchlen] とい +う条件の場合を想定するわけだが、 text[scan_pos + matchlen] -¤È +と text[pos + matchlen] -¤òÈæ¤Ù¤Æ¤¤¤ë +を比べている -text[scan_pos] ¼­½ñ¾å¤Îʸ»úÎó¤Î*ÀèƬ* -text[pos] ¸½ºß¤Îʸ»úÎó¤Î*ÀèƬ* +text[scan_pos] 辞書上の文字列の*先頭* +text[pos] 現在の文字列の*先頭* -¤òÈæ¤Ù¤Ê¤¤¤Î¤Ï matchlen ¤¬Á°¤ËͽÁÛ¤·¤¿¡ÖºÇÄã¸Â°ìÃפ·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤Ä¹¤µ-1¡× -¤À¤«¤é¤Ç¤¢¤í¤¦¡£¸½»þÅÀ¤Ç¡¢matchlen ¤¬ 2 ¤À¤«¤é +を比べないのは matchlen が前に予想した「最低限一致しなければならない長さ-1」 +だからであろう。現時点で、matchlen が 2 だから text[scan_pos + 0] == text[pos + 0] text[scan_pos + 1] == text[pos + 1] -¤Ç¤¢¤Ã¤¿¤È¤·¤Æ¤â¡¢ +であったとしても、 text[scan_pos + 2] != text[pos + 2] -¤Ç¤¢¤ì¤Ð¡¢¡ÖºÇÄã¸Â°ìÃפ·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤Ä¹¤µ¡×¤È¤¤¤¦¾ò·ï¤òËþ¤¿¤µ¤Ê¤¤¤Î -¤Ç¤¢¤ë¡£¤Ê¤Î¤Ç matchlen ¤Î°ÌÃÖ¤«¤éÀè¤ËÈæ³Ó¤·¤Æ̵Â̤ÊÈæ³Ó¤ò¤·¤Ê¤¤¤è¤¦¤Ë -¤·¤Æ¤¤¤ë¡£¸å¤Ç¤Á¤ã¤ó¤È¤·¤¿Èæ³Ó¤Î½èÍý¤¬½Ð¤ÆÍè¤ë¤À¤í¤¦¡£¤³¤Î¤è¤¦¤Ê½èÍý¤Ï -½èÍý¤È¤·¤Æ¤Ï¸úΨ¤¬Îɤ¤¤Î¤À¤¬¡¢¤³¤È¥½¡¼¥¹Íý²ò¤È¸À¤¦ÅÀ¤Ç¤Ï¾éŤǤ¢¤ë¡£¤ï -¤«¤ê¤Ë¤¯¤¤¤Î¤À¡£»ÅÊý¤Ê¤¤¤Î¤À¤±¤É¡£ +であれば、「最低限一致しなければならない長さ」という条件を満たさないの +である。なので matchlen の位置から先に比較して無駄な比較をしないように +している。後でちゃんとした比較の処理が出て来るだろう。このような処理は +処理としては効率が良いのだが、ことソース理解と言う点では冗長である。わ +かりにくいのだ。仕方ないのだけど。 -# matchlen ¤Î°ÕÌ£¤ÎͽÁۤϤɤ¦¤ä¤éÅö¤¿¤Ã¤Æ¤¤¤ë¤è¤¦¤À¡£matchlen ¤ÏºÇû°ì -# Ã×Ĺ¤Ç¡¢minmatchlen ¤Ã¤È̾ÉÕ¤±¤Æ¤âÎɤµ¤½¤¦¤ÊÊÑ¿ô¤À¡£ +# matchlen の意味の予想はどうやら当たっているようだ。matchlen は最短一 +# 致長で、minmatchlen っと名付けても良さそうな変数だ。 -¤½¤·¤Æ¡¢Èæ³Ó¤Ë¼ºÇÔ¤·¤¿¤é scan_pos ¤ò¹¹¿·¤¹¤ë¡£ +そして、比較に失敗したら scan_pos を更新する。 scan_pos = prev[scan_pos & (dicsiz - 1)]; -¥Ï¥Ã¥·¥å¤Î¥Á¥§¡¼¥ó¤ò¤¿¤É¤Ã¤Æ¤¤¤ë¡¢¤Ä¤Þ¤ê¼¡¤Î¸õÊä¤ò¼­½ñ¤«¤é¼è¤ê½Ð¤·¤Æ¤¤ -¤ë¤ï¤±¤À¡£¤³¤³¤Þ¤Ç¤Ç¡¢while ¥ë¡¼¥×¤Î½èÍýÆâÍƤÎÁÛÁü¤Ï¤Ä¤¤¤¿¡£¤³¤Î¥ë¡¼¥× -¤Ï¼­½ñ¤«¤é(ºÇĹ)°ìÃפ¹¤ëʸ»úÎó¤òõ¤·¤Æ¤¤¤ë¤Î¤À¤í¤¦¡£ +ハッシュのチェーンをたどっている、つまり次の候補を辞書から取り出してい +るわけだ。ここまでで、while ループの処理内容の想像はついた。このループ +は辞書から(最長)一致する文字列を探しているのだろう。 -½çÈÖ¤¬Á°¸å¤·¤¿¤¬¡¢while ¥ë¡¼¥×¤Îæ½Ð¾ò·ï¤ò¸«¤Æ¤ß¤ë +順番が前後したが、while ループの脱出条件を見てみる while (scan_pos > scan_end) { -¤³¤ì¤Ï¤É¤¦¤¤¤¦¤³¤È¤À¤í¤¦¡© scan_pos ¤Ï¡¢¥Ï¥Ã¥·¥å¤Î¥Á¥§¡¼¥ó¤ò¤¿¤É¤Ã¤ÆƱ -¤¸¥Ï¥Ã¥·¥åÃͤò»ý¤Äʸ»úÎó¤Î°ÌÃÖ¤òõ¤¹¡¢¤³¤ÎÃͤϤÀ¤ó¤À¤ó¤È¾®¤µ¤¯¤Ê¤Ã¤Æ¹Ô -¤¯¤â¤Î¤Ê¤Î¤À¤í¤¦¤«¡© -¤½¤ÎÄ̤ê¤Ç¤¢¤ë¡£hash[] ¤Ø¤Î³ÊǼ¤Ï¥Õ¥¡¥¤¥ë¤«¤é¼è¤Ã¤ÆÍ褿ʸ»úÎó¤ò½ç¤Ë³Ê -Ǽ¤·¤Æ¹Ô¤¯¤Î¤Ç¥Á¥§¡¼¥ó¤Î±ü¤Ë¤Ï¡¢¤è¤êÁ°¤ÎÊý¤Î°ÌÃÖ¤¬½ñ¤«¤ì¤Æ¤¤¤ë¤Ï¤º¤À¡£ -µÕ¤Ë¥Á¥§¡¼¥ó¤ÎÀõ¤¤Éôʬ¤Ë¤Ï¤è¤ê¸½ºß°ÌÃ֤˶ᤤ°ÌÃÖ¤¬½ñ¤«¤ì¤Æ¤¤¤ë¤Î¤À¤í -¤¦¡£¤Ç¤Ï¡¢¤½¤Î¶­³¦ scan_end ¤Ï¤É¤¦¤ä¤Ã¤Æ¤ï¤«¤ë¤Î¤À¤í¤¦¤«¡©¤³¤ì¤Ï¸å¤Ç¤Þ -¤¿¸¡¾Ú¤·¤è¤¦¡£ +これはどういうことだろう? scan_pos は、ハッシュのチェーンをたどって同 +じハッシュ値を持つ文字列の位置を探す、この値はだんだんと小さくなって行 +くものなのだろうか? +その通りである。hash[] への格納はファイルから取って来た文字列を順に格 +納して行くのでチェーンの奥には、より前の方の位置が書かれているはずだ。 +逆にチェーンの浅い部分にはより現在位置に近い位置が書かれているのだろ +う。では、その境界 scan_end はどうやってわかるのだろうか?これは後でま +た検証しよう。 -¤Ç¤Ï¡¢½èÍý¤ÎËܼÁ if ʸ¤ÎÃæ¤ò¸«¤ë»ö¤Ë¤·¤è¤¦ +では、処理の本質 if 文の中を見る事にしよう if (text[scan_pos + matchlen - off] == text[pos + matchlen]) { { @@ -1275,22 +1275,22 @@ text[scan_pos + 2] != text[pos + 2] } } -ºÇ½é¤Î°ÕÌ£¤â¤Ê¤¯¥Ö¥í¥Ã¥¯¤Ë¤Ê¤Ã¤Æ¤¤¤ëÉôʬ¤ò¸«¤ë¡¢ +最初の意味もなくブロックになっている部分を見る、 { a = text + scan_pos - off; b = text + pos; for (len = 0; len < max && *a++ == *b++; len++); } -¤³¤Î½èÍý¤Ç¤Ï a, b ¤È¤¤¤¦¤¤¤«¤Ë¤â¶É½ê¤Ê̾Á°¤ÎÊÑ¿ô¤¬»È¤ï¤ì¤Æ¤¤¤ë¡£¤³¤ì¤Ï¡¢ -ËÜÅö¤Ë¤³¤Î¥Ö¥í¥Ã¥¯Æâ¤Ç¶É½êŪ¤Ê¤â¤Î¤Î¤è¤¦¤À¡£¤Ê¤é¤ÐÄêµÁ°ÌÃ֤⤳¤Î¥Ö¥í¥Ã -¥¯Æâ¤Ë¤·¤ÆËÜÅö¤Ë¶É½êŪ¤Ë¤·¤ÆÍߤ·¤«¤Ã¤¿¡£ +この処理では a, b といういかにも局所な名前の変数が使われている。これは、 +本当にこのブロック内で局所的なもののようだ。ならば定義位置もこのブロッ +ク内にして本当に局所的にして欲しかった。 -¤µ¤é¤Ë¡¢¤³¤Î½èÍý¤Ïñ¤Ëʸ»úÎó a, b ¤òÈæ³Ó¤·¤Æ¤¤¤ë¤À¤±¤Î¤è¤¦¤À¡£memcmp() -¤Ç¤Ï¤Þ¤º¤¤¤Î¤«¤È¸À¤¦¤È¤³¤³¤Çµá¤á¤Æ¤¤¤ë¤â¤Î¤¬¡Ö¤É¤³¤Þ¤Ç°ìÃפ·¤¿¤«(len)¡× -¤Î¤è¤¦¤Ê¤Î¤Ç¡¢memcmp() ¤Ç¤ÏÌòÉÔ­¤À¡£ +さらに、この処理は単に文字列 a, b を比較しているだけのようだ。memcmp() +ではまずいのかと言うとここで求めているものが「どこまで一致したか(len)」 +のようなので、memcmp() では役不足だ。 -¤½¤Î¼¡¤Î½èÍý¡¢ +その次の処理、 if (len > matchlen) { matchpos = scan_pos - off; @@ -1299,34 +1299,34 @@ text[scan_pos + 2] != text[pos + 2] } } -¤Ç¡¢matchlen (ºÇÄã°ìÃ×Ĺ)¤è¤êÂ礭¤¤¾ì¹ç¤Ë¾ò·ï¤òËþ¤¿¤¹¡£¾ò·ï¤òËþ¤¿¤µ¤Ê -¤±¤ì¤Ð¡¢scan_pos ¤ò¹¹¿·¤·¡¢¼¡¤Î¥ë¡¼¥×¤Ë°Ü¤ë¡£¤Ç¤Ï¡¢¾ò·ï¤òËþ¤¿¤¹¾ì¹ç¤ò -¸«¤Æ¤ß¤è¤¦¡£¤Þ¤ººÇû°ìÃ׍ΰìÃ×¾ò·ï¤òËþ¤¿¤·¤¿¾ì¹ç¡¢matchpos ¤È -matchlen ¤ò¹¹¿·¤¹¤ë¡£ +で、matchlen (最低一致長)より大きい場合に条件を満たす。条件を満たさな +ければ、scan_pos を更新し、次のループに移る。では、条件を満たす場合を +見てみよう。まず最短一致長の一致条件を満たした場合、matchpos と +matchlen を更新する。 -matchpos ¤Ï¥Þ¥Ã¥Á¤·¤¿°ÌÃÖ¡¢ -matchlen ¤Ï¥Þ¥Ã¥Á¤·¤¿Ä¹¤µ +matchpos はマッチした位置、 +matchlen はマッチした長さ -¤Ç¡¢matchlen ¤¬ max ¤Ê¤éºÇĹ°ìÃ׍Ë㤷¤Æ¤¤¤ë¤Î¤Ç¡¢¤³¤ì°Ê¾åõº÷¤Ï¤·¤Ê -¤¤¡£matchlen ¤ÏºÇû°ìÃ׍Ǥ¢¤ê¤Ê¤¬¤é¡¢°ìÃ×Ĺ¤Ç¤â¤¢¤ëÊÑ¿ô¤Î¤è¤¦¤À¡£ -(¤É¤¦¤ä¤é°ÊÁ°¤Î2¤Ä¤ÎͽÁۤϤɤÁ¤é¤âÅö¤¿¤Ã¤Æ¤¤¤¿ÌÏÍÍ) +で、matchlen が max なら最長一致長に達しているので、これ以上探索はしな +い。matchlen は最短一致長でありながら、一致長でもある変数のようだ。 +(どうやら以前の2つの予想はどちらも当たっていた模様) -¤È¤Ë¤«¤¯ while ¥ë¡¼¥×Éô¤Î½ÐÎϤϡ¢¤³¤Î matchpos ¤È matchlen ¤Î¤è¤¦¤À¡£ -Á°¤Ë½ñ¤¤¤¿Ä̤ꤳ¤Î¥ë¡¼¥×¤Ï¡ÖºÇĹ°ìÃ×ʸ»úÎó¤òµá¤á¤ë½èÍý¡×¤À¡£ +とにかく while ループ部の出力は、この matchpos と matchlen のようだ。 +前に書いた通りこのループは「最長一致文字列を求める処理」だ。 -match_insert() ¤ÎÁ´ÂΤò¤â¤¦°ìÅÙ¸«¤Æ¤ß¤è¤¦¡£¤¿¤À¤·¡¢°Ê²¼¤Î½ñ¤­´¹¤¨¤ò¹Ô¤¦¡£ +match_insert() の全体をもう一度見てみよう。ただし、以下の書き換えを行う。 -o while ¥ë¡¼¥×Éô¤Ï search_dict(pos, scan_pos, scan_end, max) ¤È¤¤¤¦´Ø¿ô - ¤ËÃÖ¤­´¹¤¨¤¿¤â¤Î¤È¤¹¤ë¡£ +o while ループ部は search_dict(pos, scan_pos, scan_end, max) という関数 + に置き換えたものとする。 -o ËöÈø¤Î insert() ¤ÈƱÅù¤Î½èÍý¤ò¹Ô¤Ã¤Æ¤¤¤ëÉôʬ¤â insert() ¤Î¸Æ¤Ó½Ð¤·¤Ë - ¤¹¤êÂؤ¨¤è¤¦¡£(match_insert() ´Ø¿ô¤ÎÃæ¤Ç insert() ½èÍý¤òËÜÅö¤Ë¹Ô¤¦¤Ù - ¤­¤â¤Î¤Ê¤Î¤«¤É¤¦¤«¤¬µ¿Ìä¤À¤¬) +o 末尾の insert() と同等の処理を行っている部分も insert() の呼び出しに + すり替えよう。(match_insert() 関数の中で insert() 処理を本当に行うべ + きものなのかどうかが疑問だが) -o chain ¤È¤¤¤¦ÊÑ¿ô¤Ë¤«¤«¤ï¤ë½èÍý¤â±£¤·¤¿(search_dictÆâ¤Ç¹Ô¤¦) +o chain という変数にかかわる処理も隠した(search_dict内で行う) -o for ¥ë¡¼¥×¤Ï¡¢2²ó¤·¤«¤Þ¤ï¤é¤Ê¤¤¤Î¤Ç¡¢2 Å٤Πsearch_dict() ¤Î¸Æ¤Ó½Ð¤· - ¤Ë½ñ¤­´¹¤¨¤ë +o for ループは、2回しかまわらないので、2 度の search_dict() の呼び出し + に書き換える static void match_insert() { @@ -1356,40 +1356,40 @@ static void match_insert() insert(); } -¤À¤¤¤Ö¤¹¤Ã¤­¤ê¤·¤¿(¤¬¡¢¤Þ¤ÀÈË»¨¤À)¡£¤Þ¤À¡¢off ¤Ë¤«¤«¤ï¤ëÉôʬ¤¬¤è¤¯Ê¬¤« -¤é¤Ê¤¤¤¬¡¢¤Ò¤È¤Þ¤ºÎɤ¤¤À¤í¤¦¡£¤³¤Î´Ø¿ô¤Î²òÀϤϤ³¤ì¤Ç½ª¤Ã¤ÆÎɤ¤¤À¤í¤¦¤«¡£ +だいぶすっきりした(が、まだ繁雑だ)。まだ、off にかかわる部分がよく分か +らないが、ひとまず良いだろう。この関数の解析はこれで終って良いだろうか。 -¤¤¤ä¡¢Îɤ¯¤Ê¤¤¡£´Î¿´¤Î match_insert() ¤Î½ÐÎϤ¬¤è¤¯¤ï¤«¤é¤Ê¤¤¡£¤³¤Î´Ø¿ô -¤Ï¡ÖºÇĹ°ìÃ×ʸ»úÎó¤òõ¤·¡¢hash ¤ò¹¹¿·¤¹¤ë½èÍý¡×(¤¯¤É¤¤¤è¤¦¤À¤¬¡¢hash¤ò -¹¹¿·¤¹¤ë¤Ï;·×¤Ë»×¤¦)¤Ê¤Î¤À¤í¤¦¤¬¡¢ºÇĹ°ìÃ×ʸ»úÎ󤬸«¤Ä¤«¤é¤Ê¤«¤Ã¤¿¤È -¤¤¤¦¤Î¤Ï¤É¤¦È½ÃǤµ¤ì¤ë¤Î¤À¤í¤¦¡© +いや、良くない。肝心の match_insert() の出力がよくわからない。この関数 +は「最長一致文字列を探し、hash を更新する処理」(くどいようだが、hashを +更新するは余計に思う)なのだろうが、最長一致文字列が見つからなかったと +いうのはどう判断されるのだろう? -¤Þ¤º¡¢search_dict() ¤Ç¸«¤Ä¤«¤é¤Ê¤«¤Ã¤¿¾ì¹ç¡¢matchlen ¤Ï¹¹¿·¤µ¤ì¤Ê¤¤ -(matchpos ¤Ï¡¢pos ¤Ë¤Ê¤ë)¡£¤½¤·¤Æ¡¢¤ª¤½¤é¤¯ 2 ÅÙÌܤΠsearch_dict() ¤Î -¸Æ¤Ó½Ð¤·¤¬¹Ô¤ï¤ì¤ë¡£¤¬¡¢too_flag[] ¤È¤¤¤¦¤Î¤Ç¡¢È½ÃǤǤ­¤½¤¦¤Êµ¤¤â¤¹¤ë -¤¬¤³¤ì¤Ï¤à¤·¤í¥Ï¥Ã¥·¥å¤Î¥Á¥§¡¼¥ó¤ò¤¿¤É¤ê¤¹¤®¤ë¤Î¤ò»ß¤á¤ë¤¿¤á¤Î¥Õ¥é¥°¤Ç -¤¢¤ë¤è¤¦¤Ë»×¤¨¤ë¡£ +まず、search_dict() で見つからなかった場合、matchlen は更新されない +(matchpos は、pos になる)。そして、おそらく 2 度目の search_dict() の +呼び出しが行われる。が、too_flag[] というので、判断できそうな気もする +がこれはむしろハッシュのチェーンをたどりすぎるのを止めるためのフラグで +あるように思える。 -2ÅÙÌܤΠsearch_dict()¤Ç¡¢max ¤ÎÃͤ¬ÊѤï¤ë¤Î¤¬¸°¤À¤í¤¦¤«¡©¡£º£²ó¤Î¾ì¹ç¡¢ -max ¤Ï 256 ¤«¤é 2 ¤Ë¤Ê¤ë¡£ºÇĹ°ìÃ×Ĺ¤È¤·¤Æ 2 ¤¬¸Â³¦Ãͤˤʤë¤È¡¢ -search_dict() ¤ÎÆ°ºî¤ÏÊѤï¤ë¤À¤í¤¦¤«¡©¤¤¤ä¡¢¤ä¤Ï¤êÊѤï¤é¤Ê¤¤¡£¤É¤¦¤Ë¤â -¤³¤Î´Ø¿ô¤À¤±¤Ç¤Ï¸«¤Ä¤«¤Ã¤¿¤«¸«¤Ä¤«¤é¤Ê¤«¤Ã¤¿¤«¤È¤¤¤¦È½ÃǤϤǤ­¤Ê¤¤¤è¤¦ -¤À¡£(ËÜÅö¤Ï¤ï¤«¤Ã¤Æ¤¤¤ë¤Ï¤º¤Ê¤Î¤Ë¤½¤Î¾ðÊó¤òľÀܳ°¤Ë»ý¤Á½Ð¤·¤Æ¤¤¤Ê¤¤) +2度目の search_dict()で、max の値が変わるのが鍵だろうか?。今回の場合、 +max は 256 から 2 になる。最長一致長として 2 が限界値になると、 +search_dict() の動作は変わるだろうか?いや、やはり変わらない。どうにも +この関数だけでは見つかったか見つからなかったかという判断はできないよう +だ。(本当はわかっているはずなのにその情報を直接外に持ち出していない) -µ¤»ý°­¤¤¤¬¤ä¤Ï¤ê¤³¤Î´Ø¿ô¤Î²òÀϤò½ª¤¨¡¢¼¡¤Ë°Ü¤ë»ö¤Ë¤·¤è¤¦¡£ +気持悪いがやはりこの関数の解析を終え、次に移る事にしよう。 -(H) ¤Ç¤¢¤ë¡£°ÊÁ°¡¢ +(H) である。以前、 -(H) matchlen > lastmatchlen || lastmatchlen < THRESHOLD ¤Ê¤é +(H) matchlen > lastmatchlen || lastmatchlen < THRESHOLD なら -(H.1) output() ¤¹¤ë¡£(¥Þ¥Ã¥Á¤·¤Ê¤«¤Ã¤¿¤é¤½¤Î¤Þ¤Þ½ÐÎϤ·¤Æ¤¤¤ë¤Î¤À¤í¤¦¡£¤¿¤Ö¤ó) -(H.2) ¤½¤¦¤Ç¤Ê¤±¤ì¤Ð(¥Þ¥Ã¥Á¤·¤¿¤Ê¤é)¡¢output()¤·¡¢²¿¤«¤¤¤í¤¤¤í¤¹¤ë¡£ +(H.1) output() する。(マッチしなかったらそのまま出力しているのだろう。たぶん) +(H.2) そうでなければ(マッチしたなら)、output()し、何かいろいろする。 -¤Ã¤ÈͽÁÛ¤·¤¿Éôʬ¤À¡£º£¤ä match_insert() ¤Ï¡¢²òÀϺѤߤÀ¤«¤é¤³¤ì¤Î¿¿µ¶¤¬ -¤ï¤«¤ë¤«¡©¤È¤¤¤¦¤È¤ä¤Ã¤Ñ¤ê¡¢¤ï¤«¤é¤Ê¤¤¡£¤¿¤À¡¢ +っと予想した部分だ。今や match_insert() は、解析済みだからこれの真偽が +わかるか?というとやっぱり、わからない。ただ、 matchlen > lastmatchlen -¤È¤¤¤¦¤Î¤Ï¡¢¼­½ñ¤«¤éʸ»úÎ󤬸«¤Ä¤«¤Ã¤¿¾ì¹ç¤Î¾ò·ï¤Ë¤Ê¤ê¤½¤¦¤À¤«¤é¡¢¤ä¤Ï¤ê -¤³¤ì¤ÏͽÁÛ¤¬µÕ¤À¤í¤¦¤«¡©¤È¤Ë¤«¤¯¡¢Èæ³ÓŪ´Êñ¤½¤¦¤Ê¡¢(H.1) ¤«¤é¸«¤è¤¦¡£ +というのは、辞書から文字列が見つかった場合の条件になりそうだから、やはり +これは予想が逆だろうか?とにかく、比較的簡単そうな、(H.1) から見よう。 if (matchlen > lastmatchlen || lastmatchlen < THRESHOLD) { /* (H.1) */ @@ -1397,31 +1397,31 @@ search_dict() count++; } else { -¤É¤¦¤â¡¢Ê¸»ú text[pos-1] ¤ò½ÐÎϤ·¤Æ¤¤¤ë¤À¤±¤Î¤è¤¦¤Ë»×¤¨¤ë¡£Ê¸»ú¤Î½ÐÎÏ -¤Ï¡¢slide ¼­½ñË¡¤Ç¤Ï¡Ö¼­½ñ¤«¤é¸«¤Ä¤«¤é¤Ê¤«¤Ã¤¿¾ì¹ç¡×¤ò°ÕÌ£¤¹¤ë¤«¤é¡¢¤ä -¤Ï¤êºÇ½é¤ÎͽÁۤϤ¢¤Ã¤Æ¤½¤¦¤Ê¤Î¤À¤¬¡¦¡¦¡¦»ÅÊý¤Ê¤¤¤Î¤Ç¡¢output()¤Î½èÍý¤ò -ÇÁ¤¤¤Æ¸«¤è¤¦¡£¤³¤ì¤Ï¡¢lh5, 6, 7 ¤Î¾ì¹ç¡¢huf.c:output_st1(c, p) ¤Ç¤¢¤ë¡£ -¸½»þÅÀ¤Ç½èÍý¤ÎÆâÍƤò¸«¤Æ¤â¤ï¤±¤¬¤ï¤«¤é¤Ê¤¤¤¬¡¢·ëÏÀ¤«¤é¸À¤¦¤ÈÂè°ì°ú¿ô c -¤Ï¡¢Ê¸»ú¤Ç¡¢ÂèÆó°ú¿ô p ¤Ï¡¢°ÌÃ֤Ǥ¢¤ë¡£ËÁƬ¤Î decode ½èÍý¤Ç¡¢Ê¸»ú c ¤Ï -Ťµ¤â·ó¤Í¤Æ¤¤¤ë¤ÈÀâÌÀºÑ¤ß¤Ê¤Î¤Ç¡¢(¤½¤·¤Æ¡¢text[pos-1] ¤Ë¤Ï¸½»þÅÀ¤Çʸ -»ú¤½¤Î¤â¤Î¤·¤«½ñ¤«¤ì¤Æ¤¤¤Ê¤¤)¤³¤ì¤Ï¤ä¤Ï¤êʸ»ú¤ò½ÐÎϤ·¤Æ¤¤¤ë½èÍý¤À¡£¤Ä -¤Þ¤ê¡Ö¸«¤Ä¤«¤é¤Ê¤«¤Ã¤¿¾ì¹ç¡×¤Î½èÍý¤À¡£ +どうも、文字 text[pos-1] を出力しているだけのように思える。文字の出力 +は、slide 辞書法では「辞書から見つからなかった場合」を意味するから、や +はり最初の予想はあってそうなのだが・・・仕方ないので、output()の処理を +覗いて見よう。これは、lh5, 6, 7 の場合、huf.c:output_st1(c, p) である。 +現時点で処理の内容を見てもわけがわからないが、結論から言うと第一引数 c +は、文字で、第二引数 p は、位置である。冒頭の decode 処理で、文字 c は +長さも兼ねていると説明済みなので、(そして、text[pos-1] には現時点で文 +字そのものしか書かれていない)これはやはり文字を出力している処理だ。つ +まり「見つからなかった場合」の処理だ。 -¤Ê¤¼¡¢pos-1 ¤Ê¤Î¤À¤í¤¦¡©³Î¤«¤Ë Huffman coding ¤Ëʸ»ú¤òÅϤ¹¤Î¤Ï¤³¤ì¤¬½é -¤á¤Æ¤Ç¡¢¸½ºß pos ¤Î°ÌÃ֤ϥХåե¡¤Î1ʸ»ú¿Ê¤ó¤À°ÌÃ֤ˤ¢¤ë¡£pos-1 ¤Ï½ÐÎÏ -¤·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¤Î¤ÏÅöÁ³¤À¡£¤È¤¤¤¦¤³¤È¤Ï pos ¤Ï¾ï¤Ë¡Ö̤½ÐÎÏʸ»ú¤Î°Ì -ÃÖ + 1¡×¤Ê¤Î¤«¤â¤·¤ì¤Ê¤¤¡£ +なぜ、pos-1 なのだろう?確かに Huffman coding に文字を渡すのはこれが初 +めてで、現在 pos の位置はバッファの1文字進んだ位置にある。pos-1 は出力 +しなければならないのは当然だ。ということは pos は常に「未出力文字の位 +ç½® + 1」なのかもしれない。 -¼¡¤Î count++ ¤ò¸«¤ë¡£count ¤Ï¤É¤¦¤ä¤é¤³¤Î´Ø¿ô¤ÎÊÑ¿ô¤Ç¤Ï¤Ê¤¤¤é¤·¤¤¡¢º¤¤Ã -¤¿»ö¤Ë¶É½êÊÑ¿ô¤Î̾Á°¤Ã¤Ý¤¤¤¬¥°¥í¡¼¥Ð¥ëÊÑ¿ô¤À¡£¤³¤ì¤Ï¤è¤í¤·¤¯¤Ê¤¤¡£¤Á¤ç¤Ã -¤È grep ¤·¤¿¤À¤±¤Ç¤Ï¡¢Â¾¤Ë¤É¤³¤Ç¤³¤ÎÊÑ¿ô¤ò»È¤Ã¤Æ¤¤¤ë¤Î¤«¤ï¤«¤é¤Ê¤«¤Ã¤¿¡£ -¤Þ¤¢¡¢º£ 1 ʸ»ú¤òÅϤ·¤¿½ê¤Ê¤Î¤Ç¡¢ÆþÎÏʸ»ú¿ô¤Ê¤Î¤À¤È²¾Äꤷ¤Æ¤ª¤³¤¦¡£¤³ -¤ÎÊÑ¿ô¤¬ÂçÀª¤Ë±Æ¶Á¤òÍ¿¤¨¤ë»ö¤Ï¤Ê¤¤¤À¤í¤¦¤«¤é¤³¤ì°Ê¾å¤Ï¸«¤Ê¤¤¤È¸À¤¦¤Î¤â -¥¢¥ê¤À¡£ +次の count++ を見る。count はどうやらこの関数の変数ではないらしい、困っ +た事に局所変数の名前っぽいがグローバル変数だ。これはよろしくない。ちょっ +と grep しただけでは、他にどこでこの変数を使っているのかわからなかった。 +まあ、今 1 文字を渡した所なので、入力文字数なのだと仮定しておこう。こ +の変数が大勢に影響を与える事はないだろうからこれ以上は見ないと言うのも +アリだ。 -# ¤½¤Î¸å¡¢dhuf.c:decode_p_dyn() ¤Ç¤Î¤ß count ¤ò»ÈÍѤ·¤Æ¤¤¤ë»ö¤¬¤ï¤«¤Ã¤¿¡£ +# その後、dhuf.c:decode_p_dyn() でのみ count を使用している事がわかった。 -¼¡¤Ï (H.2) ¤Ç¤¢¤ë¡£¤³¤ì¤¬¤Þ¤¿Æñ²ò¤Ê¤Î¤À¤¬¤æ¤Ã¤¯¤êÊÒÉÕ¤±¤è¤¦¡£ +次は (H.2) である。これがまた難解なのだがゆっくり片付けよう。 } else { /* (H.2) */ @@ -1439,26 +1439,26 @@ search_dict() if (matchlen > remainder) matchlen = remainder; } -¤Þ¤º¡¢output() ¤ËÅϤ·¤Æ¤¤¤ë°ú¿ô¤Ï¡¢¤½¤ì¤¾¤ì¡ÖŤµ¡×¤È¡Ö°ÌÃ֡פǤ¢¤í¤¦ -¤³¤È¤ÏͽÁۺѤߤÀ¡£¤µ¤é¤Ë UCHAR_MAX{255} + 1 - THRESHOLD{3} ¤À¤«¤é +まず、output() に渡している引数は、それぞれ「長さ」と「位置」であろう +ことは予想済みだ。さらに UCHAR_MAX{255} + 1 - THRESHOLD{3} だから - Ťµ lastmatchlen + 253 - °ÌÃÖ lastmatchoffset & (dicsiz-1) + 長さ lastmatchlen + 253 + 位置 lastmatchoffset & (dicsiz-1) -¤È¤Ê¤Ã¤Æ¤¤¤ë¡£ËÁƬ¤Î decode() ¤Î²òÀϤǡ¢Ä¹¤µ¤Ï 253 ¤ò­¤¹»ö¤Ï³ÎǧºÑ¤ß -¤À(-lhs- ¤Î¾ì¹ç 254 ¤ò­¤¹¤È¤¤¤¦Æ°ºî¤¬¡¢encoding Éôʬ¤Ç¤Ï¹Í褵¤ì -¤Æ¤Ê¤¤¤Î¤Ï¡¢-lhs- ¤Î encoding µ¡Ç½¤¬¤Ê¤¤¤«¤é¤À)¡£¤È¤³¤í¤Ç¡¢°ìÃ×Ĺ -lastmatchlen ¤Ï 3 °Ê¾å¤Ç½é¤á¤Æ 255 ¤ò±Û¤¨¤ë¤³¤È¤¬¤Ç¤­¤ë¡£°ÊÁ°Í½ÁÛ¤·¤¿¡¢ -THRESHOLD ¤Î°ÕÌ£¡ÖºÇÄã¸Â°ìÃפ·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤Ä¹¤µ¡×¤Ï¤¢¤Ã¤Æ¤¤¤ë¤é¤·¤¤¡£ +となっている。冒頭の decode() の解析で、長さは 253 を足す事は確認済み +だ(-lhs- の場合 254 を足すという動作が、encoding 部分では考慮され +てないのは、-lhs- の encoding 機能がないからだ)。ところで、一致長 +lastmatchlen は 3 以上で初めて 255 を越えることができる。以前予想した、 +THRESHOLD の意味「最低限一致しなければならない長さ」はあっているらしい。 -¤â¤¦°ìÅÀ¡¢Ãí°Õ¤·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¤Î¤Ï¡¢½ÐÎϤ·¤Æ¤¤¤ë¤Î¤¬ lastmatchlen ¤È -lastmatchoffset ¤Ç¤¢¤ë¡£¤³¤ì¤é¤Ï¡¢match_insert() ¤Î¤È¤­¤Ë¤Ï¹¹¿·¤·¤Æ¤¤ -¤Ê¤¤(last¡Á¤Î¹¹¿·¤Ï¼¡¤Î¥ë¡¼¥×¤ÎÀèƬ (E) ¤Ç¹Ô¤ï¤ì¤ë)¡£ÀèÄø (H.1) ¤Î¤È¤­ -¤â½ñ¤­½Ð¤·¤Æ¤¤¤¿¤Î¤Ï¡¢text[pos-1] ¤Ç¤¢¤Ã¤¿¡£pos °ÌÃ֤ϰìÊâÀèÆɤߤ·¤¿°Ì -ÃÖ¤ò»Ø¤¹¤é¤·¤¤¡£¤³¤Î¤è¤¦¤Ê½èÍý¤ò¹Ô¤¦¾ì¹ç¡¢ºÇ¸å¤ËÄ´À°¤¬É¬ÍפʤϤº¤À(¤Ç -¤Ê¤¤¤ÈºÇ¸å¤Îʸ»ú¤¬½ÐÎϤµ¤ì¤Ê¤¤)¡£¤½¤ÎÄ´À°¤Ï¤É¤³¤Ç¹Ô¤ï¤ì¤ë¤Î¤À¤í¤¦¡© +もう一点、注意しなくてはならないのは、出力しているのが lastmatchlen と +lastmatchoffset である。これらは、match_insert() のときには更新してい +ない(last〜の更新は次のループの先頭 (E) で行われる)。先程 (H.1) のとき +も書き出していたのは、text[pos-1] であった。pos 位置は一歩先読みした位 +置を指すらしい。このような処理を行う場合、最後に調整が必要なはずだ(で +ないと最後の文字が出力されない)。その調整はどこで行われるのだろう? -¤µ¤Æ¡¢¸å³¤Î½èÍý¤À¤¬¡¢<Ťµ¡¢°ÌÃÖ>¤Î¥Ú¥¢¤ò½ÐÎϤ·¤¿¸å¤Ï¡¢ +さて、後続の処理だが、<長さ、位置>のペアを出力した後は、 --lastmatchlen; @@ -1467,51 +1467,51 @@ lastmatchoffset count++; } -¤È¤¤¤¦½èÍý¤ò¹Ô¤Ã¤Æ¤¤¤ë¡£get_next() ¤Ï¡¢pos ¤ò¿Ê¤á¤ë½èÍý¡¢insert() ¤Ï¼­ -½ñ¤ËÅÐÏ¿¤¹¤ë½èÍý¤À¤«¤é¡¢¤³¤ì¤Ïʸ»úÎó¤òÆɤßÈô¤Ð¤·¤Æ¤¤¤ë½èÍý¤À¡£³Î¤«¤Ë -lastmatchlen ʬ¤Î¾ðÊó¤Ï½ÐÎϺѤߤÀ¤«¤é¡¢¤³¤ì¤ÏǼÆÀ¤Ç¤¢¤ë¡£lastmatchlen -¤ò 1 ;ʬ¤Ë°ú¤¤¤Æ¤¤¤ë¤Î¤¬Ææ¤À¤¬¤³¤ì¤Ï pos ¤¬°ìÊâÀè¤Ë¿Ê¤ó¤Ç¤¤¤ë¤«¤é¤Ç¤¢ -¤í¤¦¤ÈͽÁÛ¤¹¤ë¡£¤Ä¤Þ¤ê¡¢¤³¤Î¸å¤Ï pos ¤Î°ÌÃ֤Ϥޤ¿¡Ö¸½ºß°ÌÃ֡פËÌá¤ë¡£ -¤Ê¤ë¤Û¤É¡¢ÀèÄøÄ´À°¤¬É¬ÍפȽñ¤¤¤¿¤¬¤³¤³¤Ç¹Ô¤ï¤ì¤Æ¤¤¤ë¤é¤·¤¤¡£ºÙÉô¤ÏÉÔÌÀ -¤À¤¬¾¯¤Ê¤¯¤È¤â¼­½ñ¤Ëʸ»úÎ󤬸«¤Ä¤«¤Ã¤¿¾ì¹ç¤ÏºÇ¸å¤Þ¤Ç½ÐÎϤµ¤ì¤ë¤è¤¦¤À¡£ +という処理を行っている。get_next() は、pos を進める処理、insert() は辞 +書に登録する処理だから、これは文字列を読み飛ばしている処理だ。確かに +lastmatchlen 分の情報は出力済みだから、これは納得である。lastmatchlen +を 1 余分に引いているのが謎だがこれは pos が一歩先に進んでいるからであ +ろうと予想する。つまり、この後は pos の位置はまた「現在位置」に戻る。 +なるほど、先程調整が必要と書いたがここで行われているらしい。細部は不明 +だが少なくとも辞書に文字列が見つかった場合は最後まで出力されるようだ。 -¼¡¤Ë¿Ê¤â¤¦ +次に進もう get_next(); matchlen = THRESHOLD - 1; match_insert(); if (matchlen > remainder) matchlen = remainder; -¤»¤Ã¤«¤¯ pos ¤¬¸½ºß¤Î°ÌÃÖ¤ËÌá¤Ã¤¿¤Î¤Ë¡¢get_next() ¤Ç¤Þ¤¿ÀèÆɤߤµ¤ì¤¿¡£ -¤¦¡¼¤à¡£¤½¤·¤Æ¡¢matchlen ¤Ï½é´ü²½¤µ¤ì¤ë¡£°ìÃ×¾ðÊó¤Ï¤¹¤Ç¤Ë½ÐÎϺѤߤÀ¤« -¤é¤³¤ì¤Ï¤¦¤Ê¤º¤±¤ë¡£¤½¤·¤Æ¡¢match_insert() ¤¬¸Æ¤Ð¤ì¤ë¡£¤³¤Î»þÅÀ¤ÇºÆÅÙ -¼­½ñ¤¬¸¡º÷¤µ¤ì¤ë¡£pos ¤Ï¤Þ¤¿1ʸ»ú¿Ê¤ó¤Ç¤¤¤ë¤Î¤À¤«¤é¡¢¤³¤ì¤ÏÀèÄø(½é´ü¾õ -ÂÖ)¤Îmatch_insert() ¤ÈÂ纹¤Ê¤¤½èÍý¤À¡£(¤½¤Îľ¸å¤Îifʸ¤Ï¶­³¦¾ò·ï¤Ê¤Î¤Ç -̵»ë) +せっかく pos が現在の位置に戻ったのに、get_next() でまた先読みされた。 +うーむ。そして、matchlen は初期化される。一致情報はすでに出力済みだか +らこれはうなずける。そして、match_insert() が呼ばれる。この時点で再度 +辞書が検索される。pos はまた1文字進んでいるのだから、これは先程(初期状 +態)のmatch_insert() と大差ない処理だ。(その直後のif文は境界条件なので +無視) -¤½¤¦¤·¤Æ¡¢¤Þ¤¿¼¡¤Î¥ë¡¼¥×¤Ë°Ü¤ë¡£¤³¤Î¤È¤­Â³¤±¤¶¤Þ get_next(), -match_insert() ¤¬¹Ô¤ï¤ì¤ë¡£¤É¤¦¤ä¤é pos ¤Ï¼¡¤Î¥ë¡¼¥×¤«¤é¤Ï¡¢ 2 ʸ»úʸ -Àè¤Ë¿Ê¤ó¤Ç¤·¤Þ¤¦¤è¤¦¤À¡£¤Ê¤¼¤À¤í¤¦¡© +そうして、また次のループに移る。このとき続けざま get_next(), +match_insert() が行われる。どうやら pos は次のループからは、 2 文字文 +先に進んでしまうようだ。なぜだろう? -# ¸å¤Ç¤ï¤«¤Ã¤¿»ö¤À¤¬¡¢while (--lastmatchlen > 0) ¤Î¥ë¡¼¥×²ó¿ô¤òÆÉ¤ß´Ö -# °ã¤¨¤¿¡£Î㤨¤Ð¡¢lastmatchlen ¤¬ 1 ¤Ê¤é¡¢¤³¤Î while ¥ë¡¼¥×Æâ¤Ç¤Ï -# get_next() ¤Ï1²ó¤â¸Æ¤Ð¤ì¤Ê¤¤¡£ +# 後でわかった事だが、while (--lastmatchlen > 0) のループ回数を読み間 +# 違えた。例えば、lastmatchlen が 1 なら、この while ループ内では +# get_next() は1回も呼ばれない。 -¤É¤¦¤Ë¤â¥½¡¼¥¹¤ò¸«¤¿¤À¤±¤Ç²òÆɤ¹¤ë¤Ë¤Ï¡¢¤³¤Î¤¢¤¿¤ê¤¬¸Â³¦¤Î¤è¤¦¤À¡£¤É¤¦ -¤·¤Æ¤âºÙÉô¤¬¤ï¤«¤é¤Ê¤¤¤·¡¢»ö¼Â¤¬¸«¤¨¤Ê¤¤¤«¤éͽÁÛ¤ÎÀѤ߽Ťͤ¬¤¿¤Þ¤Ã¤ÆÉÔ -°Â¤Ë¤Ê¤ë¡£ +どうにもソースを見ただけで解読するには、このあたりが限界のようだ。どう +しても細部がわからないし、事実が見えないから予想の積み重ねがたまって不 +安になる。 -¼Â¤Ï¡¢¤â¤¦¾¯¤·¥Þ¥á¤Ë¿Þ¤òµ¯¤³¤·¤ÆÆɤ߿ʤó¤Ç¹Ô¤±¤Ð¤â¤Ã¤È¤ï¤«¤ë¤³¤È¤¬¤¢¤Ã -¤¿¤À¤í¤¦¤È»×¤¦¤Î¤À¤¬¡¢¤½¤ì¤ÏÌÌÅݤÀ¤·¡¢´Ö°ã¤¨¤ë²ÄǽÀ­¤¬¤¢¤ë(¤³¤³¤Þ¤Ç¤Ç -²¿ÅÙ¤«Äˤ¤»×¤¤¤ò¤·¤¿)¡£°Ê¹ß¤Ï¡¢¤¤¤¯¤Ä¤«¤Î¥Ç¡¼¥¿¤ò¼ÂºÝ¤Ë°µ½Ì¤µ¤»¤½¤ÎÆ° -¤­¤ò¥Ç¥Ð¥Ã¥¬¤ÇÄɤ¦¤³¤È¤Ç¡¢¤³¤ì¤Þ¤Ç¤Î²òÀÏ·ë²Ì¤ò¸¡¾Ú¤·¤Æ¤ß¤è¤¦¡£ +実は、もう少しマメに図を起こして読み進んで行けばもっとわかることがあっ +ただろうと思うのだが、それは面倒だし、間違える可能性がある(ここまでで +何度か痛い思いをした)。以降は、いくつかのデータを実際に圧縮させその動 +きをデバッガで追うことで、これまでの解析結果を検証してみよう。 -¡¦¡¦¡¦¤Ã¤È¡¢¤½¤ÎÁ°¤Ë¡¢¤³¤³¤Þ¤Ç¤Ç¤¹¤Ù¤Æ¤Î´Ø¿ô¤òÌÖÍ夷¤Æ¤·¤Þ¤Ã¤¿¤È»×¤Ã¤Æ -¤¤¤¿¤Î¤À¤¬¡¢°ì¤Ä˺¤ì¤Æ¤¤¤¿¤â¤Î¤¬¤¢¤Ã¤¿¡£update() ¤À¡£¤³¤Î´Ø¿ô¤Ï¡¢ -get_next() ¤Ç¸Æ¤Ó½Ð¤µ¤ì¤Æ¤¤¤¿¤Î¤À¤¬¡¢°ÊÁ°¤Ï̵»ë¤·¤Æ¤¤¤¿¡£Àè¤Ë¤³¤³¤ò¸« -¤Æ¤ª¤³¤¦¡£ +・・・っと、その前に、ここまでですべての関数を網羅してしまったと思って +いたのだが、一つ忘れていたものがあった。update() だ。この関数は、 +get_next() で呼び出されていたのだが、以前は無視していた。先にここを見 +ておこう。 -¤Þ¤º¡¢get_next() ¤òºÆ·Ç¤¹¤ë¡£ +まず、get_next() を再掲する。 static void get_next() { @@ -1522,9 +1522,9 @@ static void get_next() hval = ((hval << 5) ^ text[pos + 2]) & (unsigned)(HSHSIZ - 1); } -remainder ¤È pos ¤ò¿Ê¤á¤¿¸å¡¢pos ¤¬ txtsiz - maxmatch ¤Ë㤷¤Æ¤·¤Þ¤Ã¤¿ -¾ì¹ç(pos == 2 * 2^dicbit ¤Î¾ì¹ç)¤Ë¸Æ¤Ó½Ð¤µ¤ì¤ë¤è¤¦¤À¡£¤Ä¤Þ¤ê¡¢°Ê²¼¤Î¿Þ -¤Î¾õÂÖ¤À¡£¤³¤ì¤¬¡¢update() ¤ò¸Æ¤Ó½Ð¤¹»þ¤Î½é´ü¾õÂÖ¤À¡£ +remainder と pos を進めた後、pos が txtsiz - maxmatch に達してしまった +場合(pos == 2 * 2^dicbit の場合)に呼び出されるようだ。つまり、以下の図 +の状態だ。これが、update() を呼び出す時の初期状態だ。 ---------------------------------------------------------------------------- @@ -1542,7 +1542,7 @@ remainder ---------------------------------------------------------------------------- -¤Ç¤Ï¡¢update() ¤ËÆþ¤ë¡£ +では、update() に入る。 static void update() { @@ -1579,15 +1579,15 @@ static void update() } } -ÀèƬ¤Ç¡¢¤Ê¤¼¤« memmove() ¤ò for ¥ë¡¼¥×¤Ç½ñ¤­´¹¤¨¤Æ¤¤¤ë¡£¤Ê¤¼¤³¤Î¤è¤¦¤Ê -¤³¤È¤ò¹Ô¤Ã¤Æ¤¤¤ë¤Î¤À¤í¤¦¡£for ¥ë¡¼¥×¤ò¸«¤Æ¤ß¤Æ¤â¤ä¤Ã¤Æ¤¤¤ë¤³¤È¤ÏÊѤï¤é -¤Ê¤¤¡£Ææ¤À¤¬¡¢¤È¤Ë¤«¤¯¡¢text[] ¤Î±¦È¾Ê¬(maxmatch Éôʬ¤â´Þ¤à) ¤òº¸¤Ë°Ü -¤·¤Æ¤¤¤ë¡£ +先頭で、なぜか memmove() を for ループで書き換えている。なぜこのような +ことを行っているのだろう。for ループを見てみてもやっていることは変わら +ない。謎だが、とにかく、text[] の右半分(maxmatch 部分も含む) を左に移 +している。 -¼¡¤Ë fread_crc() ¤Ç¡¢¿·¤¿¤Ë¥Õ¥¡¥¤¥ë¤òÆɤ߹þ¤à¡£º£ÅÙ¤ÎÆɤ߹þ¤ß°ÌÃÖ¤Ï -&text[txtsiz - dicsiz] ¤Ç¡¢Ä¹¤µ¤Ï dicsiz ¤Ç¤¢¤ë¡£ÅöÁ³¡¢remainder ¤â¹¹ -¿·¤·¤Æ¤¤¤ë¡£encoded_origsize ¤Ï°ÊÁ°¤ÈƱÍÍ̵»ë¡£pos ¤Ï dicsiz ʬ¸º¤é¤µ -¤ì¤Æ¤¤¤ë¡£¤³¤ì¤Ï¤Ä¤Þ¤ê¿Þ¼¨¤¹¤ë¤È¡¢°Ê²¼¤Î¾õÂ֤ˤʤë¤È¸À¤¦»ö¤À +次に fread_crc() で、新たにファイルを読み込む。今度の読み込み位置は +&text[txtsiz - dicsiz] で、長さは dicsiz である。当然、remainder も更 +新している。encoded_origsize は以前と同様無視。pos は dicsiz 分減らさ +れている。これはつまり図示すると、以下の状態になると言う事だ ---------------------------------------------------------------------------- @@ -1603,21 +1603,21 @@ static void update() <-------------------------------> remainder - |------- °ÊÁ°¤Î¥Ç¡¼¥¿ ---------|--- ¿·¤·¤¤¥Ç¡¼¥¿ ---------| + |------- 以前のデータ ---------|--- 新しいデータ ---------| ---------------------------------------------------------------------------- -°Ê¹ß¡¢¥Õ¥¡¥¤¥ë¤ÎÆɤ߹þ¤ß¤Ï¾ï¤Ë¤³¤Î update()¤Ç¤·¤«¹Ô¤ï¤ì¤Ê¤¤¡£pos ¤Ï¡¢ -½é´ü¾õÂÖ¤ÈƱ¤¸°ÌÃ֤ʤΤǡ¢½é´ü¾õÂÖ¤¬ºÆ¸½¤µ¤ì¤Æ¤¤¤ë¡£¤³¤³¤Þ¤Ç¤Ç¡¢ -maxmatch ¤ÎÎΰè¤Ï¤Ê¤ó¤À¤í¤¦¤È»×¤¦¤¬¡¢¤ª¤½¤é¤¯ÀèÆɤߤΤ¿¤á¤À¤í¤¦¡£¤½¤ì -¤é¤·¤¤½èÍý¤Ï¡¢match_insert() ¤ÎËÁƬ¤Ë¤¢¤Ã¤¿(¤¬¡¢¸½»þÅÀ¤Ç¾ÜºÙ¤Ë¤Ï¿¨¤ì¤Æ -¤¤¤Ê¤¤)¡£ +以降、ファイルの読み込みは常にこの update()でしか行われない。pos は、 +初期状態と同じ位置なので、初期状態が再現されている。ここまでで、 +maxmatch の領域はなんだろうと思うが、おそらく先読みのためだろう。それ +らしい処理は、match_insert() の冒頭にあった(が、現時点で詳細には触れて +いない)。 -# maxmatch ʬ¤Î;ʬ¤ÊÎΰè¤Ï¡¢pos ¤Î°ÌÃÖ¤«¤éºÇÂç maxmatch ŤÎʸ»úÎó¾È -# ¹ç¤ò¹Ô¤¦¤¿¤á¤ËɬÍפÊÎΰ衣ÀèÆɤߤȤϤޤ¿¸«Åö³°¤ì¤Ê¤³¤È¤ò½ñ¤¤¤¿¤â¤Î¤À¡£ -# ¤Á¤ç¤Ã¤È¹Í¤¨¤ì¤Ð¤ï¤«¤ë¤³¤È¤Ê¤Î¤Ë¡¦¡¦ +# maxmatch 分の余分な領域は、pos の位置から最大 maxmatch 長の文字列照 +# 合を行うために必要な領域。先読みとはまた見当外れなことを書いたものだ。 +# ちょっと考えればわかることなのに・・ -update() ¤Î»Ä¤ê¤ò¸«¤ë¡£ +update() の残りを見る。 for (i = 0; i < HSHSIZ; i++) { j = hash[i]; @@ -1629,40 +1629,40 @@ update() prev[i] = (j > dicsiz) ? j - dicsiz : NIL; } -ÆâÍƤϡ¢ÁÛÁü¤¬¤Ä¤¯¤Î¤Ç¾ÜºÙ¤Ï¾Êά¤·¤è¤¦¡£Ã±¤Ë°ÊÁ°¤Î¥Ç¡¼¥¿¤¬°ÜÆ°¤·¤¿¤Î¤Ç¡¢ -¥Ï¥Ã¥·¥åÃͤò¹¹¿·¤·¤Æ¤¤¤ë¤À¤±¤À¡£¤·¤«¤·¡¢¤³¤ì¤Ï¤Ê¤«¤Ê¤«ÌµÂ̤ʽèÍý¤À¡£ +内容は、想像がつくので詳細は省略しよう。単に以前のデータが移動したので、 +ハッシュ値を更新しているだけだ。しかし、これはなかなか無駄な処理だ。 -°ÊÁ°¡¢text[] ¤Ï´Ä¾õ¥Ð¥Ã¥Õ¥¡¤À¤í¤¦¤ÈͽÁÛ¤·¤¿¤¬Í½ÁÛ¤¬¤Ï¤º¤ì¤¿¤³¤È¤¬¤ï¤«¤Ã -¤¿¡£´Ä¾õ¥Ð¥Ã¥Õ¥¡¤Ë¤·¤Æ¤¤¤ì¤Ð¡¢¤³¤Î¥Ï¥Ã¥·¥å¤Î½ñ¤­´¹¤¨¤ÏÉÔÍפˤǤ­¤¿¤À¤í -¤¦¤È»×¤¦¤Î¤À¤¬¡¦¡¦¡¦ -# ¤½¤Î¤«¤ï¤ê¡¢°ÌÃÖ¤ÎÂç¾®Èæ³Ó¤¬ÈË»¨¤Ë¤Ê¤é¤Ê¤¤¤Î¤Ç¡¢¤³¤ì¤Ï¤³¤ì¤ÇÎɤ¤¤Î¤« -# ¤â¤·¤ì¤Ê¤¤¡£¤É¤Á¤é¤¬Í¥¤ì¤Æ¤¤¤ë¤«¤Ï¼Â¸³¤·¤Æ¤ß¤Ê¤±¤ì¤Ð¤ï¤«¤é¤Ê¤¤¤À¤í¤¦¡£ +以前、text[] は環状バッファだろうと予想したが予想がはずれたことがわかっ +た。環状バッファにしていれば、このハッシュの書き換えは不要にできただろ +うと思うのだが・・・ +# そのかわり、位置の大小比較が繁雑にならないので、これはこれで良いのか +# もしれない。どちらが優れているかは実験してみなければわからないだろう。 -¤³¤ì¤Ç¡¢°ì±þ¤Ï slide.c ¤òÌÖÍ夹¤ë»ö¤¬¤Ç¤­¤¿¡£¤Þ¤ÀÉÔÌÀ¤ÊÅÀ¤Ï¿¤¤¤¬¡¢¥Ç -¥Ð¥Ã¥¬¤Ç¼ÂºÝ¤Î½èÍý¤òÄɤ¤¤«¤±¤ì¤Ð¤Þ¤¿¤ï¤«¤ë¤³¤È¤¬¤¢¤ë¤À¤í¤¦¡£ +これで、一応は slide.c を網羅する事ができた。まだ不明な点は多いが、デ +バッガで実際の処理を追いかければまたわかることがあるだろう。 -¡¦¡¦¡¦¤·¤Ð¤·¡¢µÙ©¡¦¡¦¡¦ +・・・しばし、休息・・・ -¤µ¤Æ¡¢¥Ç¥Ð¥Ã¥¬¤Ç¤È°ÊÁ°¤Ï¹Í¤¨¤Æ¤¤¤¿¤Î¤À¤¬¡¢¤¢¤­¤é¤á¤ë¤Î¤Ï¤Þ¤ÀÁᤤ(¸µµ¤ -¤¬½Ð¤¿¤é¤·¤¤)¡¢¤½¤â¤½¤âºÇ½é¤Ë¡Ö¥Ç¥Ð¥Ã¥¬¤ò»È¤ï¤º¤Ë¤É¤³¤Þ¤Ç²òÆɤǤ­¤ë¤«¡×¤Ã -¤ÈËÁƬ¤Ë½ñ¤¤¤Æ¤¿¤Î¤Ë¤¿¤Ã¤¿2²ó¤ÎÄÌÆɤǤ⤦¤¢¤­¤é¤á¤è¤¦¤È¤·¤Æ¤¤¤¿¡£¤¬¡¢ -¤³¤³¤Þ¤Ç½ñ¤¤¤Æ¤­¤¿Ëܽñ¤ò²¿ÅÙ¤«ÆɤßÊÖ¤·¤¿¤¬¡¢¤Þ¤À¤Þ¤À¸¡Æ¤¤Î;ÃϤϤ¢¤ë¡£ +さて、デバッガでと以前は考えていたのだが、あきらめるのはまだ早い(元気 +が出たらしい)、そもそも最初に「デバッガを使わずにどこまで解読できるか」っ +と冒頭に書いてたのにたった2回の通読でもうあきらめようとしていた。が、 +ここまで書いてきた本書を何度か読み返したが、まだまだ検討の余地はある。 -¤Þ¤º¡¢match_insert() ¤Î½èÍý¤Ç¤ï¤«¤é¤Ê¤«¤Ã¤¿Éôʬ¤ò²òÆɤ·¤è¤¦¡£¼Â¤Ï¡¢¤³ -¤ì¤Ë´Ø¤·¤Æ¤Ï¤É¤¦¤·¤Æ¤â¤ï¤«¤é¤ºÇº¤ó¤Ç¤¤¤¿¤È¤³¤í¡¢Lha for UNIX ¤Î¥á¥ó¥Æ -¥Ê¤Ç¤¢¤ë²¬Ëܤµ¤ó¤Ë¶µ¤¨¤Æ¤â¤é¤¦¤³¤È¤¬¤Ç¤­¤¿(¤¢¤ê¤¬¤È¤¦¤´¤¶¤¤¤Þ¤¹)¤½¤ÎÆâ -ÍƤò³Îǧ¤·¤Ä¤Ä match_insert() ¤ò¸«¤ë¤³¤È¤Ë¤¹¤ë¡£ +まず、match_insert() の処理でわからなかった部分を解読しよう。実は、こ +れに関してはどうしてもわからず悩んでいたところ、Lha for UNIX のメンテ +ナである岡本さんに教えてもらうことができた(ありがとうございます)その内 +容を確認しつつ match_insert() を見ることにする。 -¤Þ¤º¤Ï¡¢Éü½¬¤À¡£Ä̾ï¤Î¾õÂ֤˴ؤ·¤Æ¤Ï match_insert() ¤Î²òÆɤϺѤó¤Ç¤¤¤ë¡£ -match_insert() ¤Ï¡¢text[pos] ¤«¤é»Ï¤Þ¤ëʸ»úÎó¤ò¼­½ñ¤«¤é¸¡º÷¤·¡¢¸«¤Ä¤«¤Ã -¤¿°ÌÃ֤ȰìÃ׍ò matchpos, matchlen ¤ËÀßÄꤹ¤ë½èÍý¤À¡£¤½¤·¤Æ¡¢¤Ä¤¤¤Ç¤Ë -insert() ¤Ç¡¢text[pos] ¤Î°ÌÃÖ¤ò¥Ï¥Ã¥·¥åÇÛÎó¤Ëµ­Ï¿¤·¡¢¼¡²ó¤Î¸¡º÷¤ËÈ÷¤¨ -¤ë¤³¤È¤â¤·¤Æ¤¤¤ë¡£ +まずは、復習だ。通常の状態に関しては match_insert() の解読は済んでいる。 +match_insert() は、text[pos] から始まる文字列を辞書から検索し、見つかっ +た位置と一致長を matchpos, matchlen に設定する処理だ。そして、ついでに +insert() で、text[pos] の位置をハッシュ配列に記録し、次回の検索に備え +ることもしている。 -¤Ç¤Ï¡¢ÉÔÌÀ¤ÊÉôʬ¤Ï¤Ê¤ó¤À¤Ã¤¿¤«¤È¤¤¤¦¤È too_flag[] ¤Þ¤ï¤ê¤Ç¤¢¤ë¡£ -too_flag ¤Î¥Õ¥é¥°¤¬Î©¤Ã¤Æ¤¤¤ë¤È¡£¼­½ñ¸¡º÷¤ÎÍê¤ê¤È¤Ê¤ë¥Ï¥Ã¥·¥åÃͤòÊѹ¹ -¤·¤Æ¤¤¤ë¡£¤³¤ÎÉôʬ¤¬¤Þ¤Ã¤¿¤¯³§Ìܸ¡Æ¤¤¬¤Ä¤«¤Ê¤«¤Ã¤¿¤Î¤À¡£¤³¤ì¤Ë´Ø¤·¤Æ¥½¡¼ -¥¹¤òÆɤ߿ʤá¤è¤¦¡£°Ê²¼¥½¡¼¥¹¤òºÆ·Ç¤¹¤ë¡£ +では、不明な部分はなんだったかというと too_flag[] まわりである。 +too_flag のフラグが立っていると。辞書検索の頼りとなるハッシュ値を変更 +している。この部分がまったく皆目検討がつかなかったのだ。これに関してソー +スを読み進めよう。以下ソースを再掲する。 static void match_insert() { @@ -1715,30 +1715,30 @@ static void match_insert() hash[hval] = pos; } -¤Þ¤º¡¢too_flag[] ¤Ï¡¢ºÇ½é¤¹¤Ù¤Æ¤ÎÍ×ÁǤ¬ 0 ¤Ç¤¢¤ë¡£¤½¤·¤Æ¡¢¿·¤¿¤Ë¥Õ¥¡¥¤ -¥ë¤òÆɤà¤È¤­(update())¤â 0 ¤ËºÆ½é´ü²½¤µ¤ì¤ë¤Î¤À¤Ã¤¿¡£¤³¤Î¥Õ¥é¥°¤¬Î©¤Ä -¾ò·ï¤Ï¤È¤¤¤¦¤È¡¢¤³¤Î match_insert() ¤ÎÃæ¤À¤±¤Ç¤¢¤ë¡£¤½¤Î½èÍý¤Ï +まず、too_flag[] は、最初すべての要素が 0 である。そして、新たにファイ +ルを読むとき(update())も 0 に再初期化されるのだった。このフラグが立つ +条件はというと、この match_insert() の中だけである。その処理は if (chain >= LIMIT) too_flag[h] = 1; -¤³¤ÎÉôʬ¤À¤±¤À¡£chain ¤¬ LIMIT°Ê¾å¤Ë¤Ê¤Ã¤¿¤é h (¤³¤ì¤Ï¸¡º÷ÂоݤΥϥå·¥å -ÃͤÀ)¤Ë´Ø¤·¤Æ¡¢¥Õ¥é¥°¤òΩ¤Æ¤ë¡£chain ¤Ï while ¥ë¡¼¥×(¤³¤ì¤Ïʸ»úÎó¤Î¾È -¹ç¤ò¹Ô¤¦½èÍý)¤Î¥ë¡¼¥×²ó¿ô¤À¡£h ¤Ë´Ø¤·¤Æ¤Î¸¡º÷¤¬ LIMIT{256} °Ê¾å¤Î¾ì¹ç -¤Ë too_flag[h] ¤Î¥Õ¥é¥°¤¬Î©¤Ã¤Æ¤¤¤ë¡£ +この部分だけだ。chain が LIMIT以上になったら h (これは検索対象のハッシュ +値だ)に関して、フラグを立てる。chain は while ループ(これは文字列の照 +合を行う処理)のループ回数だ。h に関しての検索が LIMIT{256} 以上の場合 +に too_flag[h] のフラグが立っている。 -while ¥ë¡¼¥×¤Ï°ìÃ×ʸ»úÎó¤Î°ìÃ׍¬ºÇĹ°ìÃ׍Ë㤹¤ë¤«¡¢¼­½ñ¤òºÇ¸å¤Þ¤Ç -õº÷¤¹¤ë¤Þ¤Ç¥ë¡¼¥×¤¹¤ë¡£¤Ä¤Þ¤ê¡¢¤¢¤ë¥Ï¥Ã¥·¥å h ¤Ë´Ø¤·¤Æ¤½¤Î¥Á¥§¡¼¥ó¤¬ -256 °Ê¾å¤Î¤â¤Î¤Ë´Ø¤·¤Æ¤Ï¡¢too_flag[h] ¤¬ 1 ¤Ë¤Ê¤Ã¤Æ¤¤¤ë¡£ +while ループは一致文字列の一致長が最長一致長に達するか、辞書を最後まで +探索するまでループする。つまり、あるハッシュ h に関してそのチェーンが +256 以上のものに関しては、too_flag[h] が 1 になっている。 -¤Ç¤Ï¡¢¤½¤Î¤è¤¦¤Ê h ¤Ë´Ø¤·¤Æ¡¢match_insert() ¤¬¤É¤Î¤è¤¦¤Ê½èÍý¤Ë¤Ê¤Ã¤Æ¤¤ -¤ë¤«¤ò¸«¤ë¡£¤Þ¤º½é´ü²½Éôʬ +では、そのような h に関して、match_insert() がどのような処理になってい +るかを見る。まず初期化部分 max = maxmatch; /* MAXMATCH; */ if (matchlen < THRESHOLD - 1) matchlen = THRESHOLD - 1; matchpos = pos; -¤³¤ì¤Ï¡¢¤È¤ê¤¢¤¨¤ºÌµ»ë¡£ +これは、とりあえず無視。 off = 0; for (h = hval; too_flag[h] && off < maxmatch - THRESHOLD; ) { @@ -1746,15 +1746,15 @@ while } if (off == maxmatch - THRESHOLD) off = 0; -Ä̾ï off ¤Ï¡¢0 ¤Ê¤Î¤À¤¬¡¢too_flag[h] ¤¬ 1 ¤Ç¤¢¤ë¤â¤Î¤Ë´Ø¤·¤Æ¤ÏÃͤ¬ÊѤï -¤ë¡£¸¡º÷ÂоݤȤʤëʸ»úÎó text[pos](¤Î¥Ï¥Ã¥·¥åÃÍ) hval ¤Ë´Ø¤·¤Æ¡¢ -too_flag[h] ¤¬Î©¤Ã¤Æ¤¤¤ì¤Ð¡¢(¤³¤Î¥Ï¥Ã¥·¥å¤Î¥Á¥§¡¼¥ó¤¬ 256 °Ê¾å¤Ç¤¢¤ë¤³ -¤È¤¬»öÁ°¤Ë¤ï¤«¤Ã¤Æ¤¤¤ì¤Ð) +通常 off は、0 なのだが、too_flag[h] が 1 であるものに関しては値が変わ +る。検索対象となる文字列 text[pos](のハッシュ値) hval に関して、 +too_flag[h] が立っていれば、(このハッシュのチェーンが 256 以上であるこ +とが事前にわかっていれば) h = ((h << 5) ^ text[pos + (++off) + 2]) & (unsigned)(HSHSIZ - 1); -¤Ç¡¢¸¡º÷ÂоݤȤʤë¥Ï¥Ã¥·¥åÃͤòÊѹ¹¤·¤Æ¤¤¤ë¡£¤³¤Î¥Ï¥Ã¥·¥åÃͤ¬¼¨¤¹¤Î¤Ï¸µ -¤Î¸¡º÷ÂоÝʸ»úÎó¤Î 1 ʸ»úÀè¤À¡£ +で、検索対象となるハッシュ値を変更している。このハッシュ値が示すのは元 +の検索対象文字列の 1 文字先だ。 ---------------------------------------------------------------------------- @@ -1769,20 +1769,20 @@ text | | | | | | | | ---------------------------------------------------------------------------- -¸µ¤Î¸¡º÷ÂоÝʸ»úÎ󤬿ޤΠa ¤À¤È¤¹¤ë¤È¡¢¤³¤ì¤ò¿Þ¤Î b ¤Ë¤·¤Æ¤¤¤ë¡£½é´ü²½ -Éô¤Î¥ë¡¼¥×¤Ï¡¢¤â¤·¤³¤Î b ¤Î¥Ï¥Ã¥·¥å¥Á¥§¡¼¥ó¤Ë´Ø¤·¤Æ too_flag[h] ¤¬¤µ¤é -¤Ë 1 ¤Ç¤¢¤ë¤Ê¤é¤µ¤é¤Ë Àè¤Îʸ»úÎó¤ò¥Ï¥Ã¥·¥åÃͤȤ¹¤ë¤è¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤ë¡£ -(¤³¤ì¤Ï¸µ¤Î pos ¤Î 2 ʸ»úÀè¤ò¼¨¤¹¡£¿Þ¤Î c ¤ÎÉôʬ¤À) h ¤Ï¡¢pos+off ¤«¤é -¤Î3ʸ»ú¤Î¥Ï¥Ã¥·¥åÃͤò¼¨¤¹¤â¤Î¤È¸À¤¦»ö¤À¡£ +元の検索対象文字列が図の a だとすると、これを図の b にしている。初期化 +部のループは、もしこの b のハッシュチェーンに関して too_flag[h] がさら +に 1 であるならさらに 先の文字列をハッシュ値とするようになっている。 +(これは元の pos の 2 文字先を示す。図の c の部分だ) h は、pos+off から +の3文字のハッシュ値を示すものと言う事だ。 -¤¿¤À¤·¡¢h ¤¬¤¢¤Þ¤ê¤Ë¤âÀè¤ÎÊý¤ò¸«¤ë¤è¤¦¤Ê¥Ï¥á¤Ë¤Ê¤ì¤Ð(off ¤¬ maxmatch - -THRESHOLD) off ¤Ï 0 ¤ËºÆÀßÄꤵ¤ì¤ë¤¬¡¢¤³¤Î¤È¤­ h ¤Ï¤½¤Î¤Þ¤Þ¤À¡£¤³¤Î°Õ -Ì£¤Ï¤Þ¤À¤ï¤«¤é¤Ê¤¤¤¬¡¢¥Ð¥°¤Ê¤Î¤Ç¤Ï¤Ê¤¤¤«¤ÈÁÛÁü¤·¤Æ¤¤¤ë(h = hval ¤ËºÆÀß -Äꤹ¤ëɬÍפ¬¤¢¤ë¤À¤í¤¦) +ただし、h があまりにも先の方を見るようなハメになれば(off が maxmatch - +THRESHOLD) off は 0 に再設定されるが、このとき h はそのままだ。この意 +味はまだわからないが、バグなのではないかと想像している(h = hval に再設 +定する必要があるだろう) -¤Ç¤Ï off = 1 ¤À¤È¤·¤ÆËܽèÍý¤ò¸«¤ë¤³¤È¤Ë¤·¤è¤¦¡£³°Â¦¤Î for ¥ë¡¼¥×¤Ë´Ø¤· -¤Æ¤Ï¡¢while ¥ë¡¼¥×¤ò2²ó¼Â¹Ô¤¹¤ë¤«¤É¤¦¤«¤À¤±¤Î¤â¤Î¤À¤Ã¤¿¡£¤Ê¤Î¤Ç¡¢ -while ¥ë¡¼¥×Éô¤À¤±¤ò¸«¤Æ¤ß¤è¤¦¡£ +では off = 1 だとして本処理を見ることにしよう。外側の for ループに関し +ては、while ループを2回実行するかどうかだけのものだった。なので、 +while ループ部だけを見てみよう。 chain = 0; scan_pos = hash[h]; @@ -1809,12 +1809,12 @@ while if (chain >= LIMIT) too_flag[h] = 1; -scan_pos, scan_end ¤Ë´Ø¤·¤Æ¤Ï¸¡º÷³«»Ï°ÌÃ֤ȽªÎ»°ÌÃ֤ȸÀ¤¦»ö¤Ç¤â¤¦Îɤ¤ -¤À¤í¤¦¡£¤Ç¡¢ºÇ½é¤Î if ¤Î¾ò·ï¤ËÃåÌܤ¹¤ë¡£ +scan_pos, scan_end に関しては検索開始位置と終了位置と言う事でもう良い +だろう。で、最初の if の条件に着目する。 if (text[scan_pos + matchlen - off] == text[pos + matchlen]) { -¤³¤ì¤¬¿¿¤È¤Ê¤ë¾õÂÖ¤ò¿Þ¼¨¤·¤è¤¦¡£ +これが真となる状態を図示しよう。 ---------------------------------------------------------------------------- @@ -1828,27 +1828,27 @@ text | | |x'| | | | |x | | | | ---------------------------------------------------------------------------- -¤Þ¤º¡¢if ¾ò·ï¤Îº¸ÊÕ +まず、if 条件の左辺 text[scan_pos + matchlen - off] -matchlen ¤Ï¡¢match_insert() ¤ËÆþ¤ëľÁ°¤Ë 2 ¤Ë½é´ü²½¤µ¤ì¤Æ¤¤¤ë(ºÇ½é¤Ï) -¤Î¤Ç¡¢¾È¹ç¤¹¤ë¤Î¤Ï¿Þ¤Î x' ¤À¡£ +matchlen は、match_insert() に入る直前に 2 に初期化されている(最初は) +ので、照合するのは図の x' だ。 -if ¾ò·ï¤Î±¦ÊÕ +if 条件の右辺 text[pos + matchlen] -¤³¤ì¤Ï¡¢¿Þ¤Î x ¤Î°ÌÃÖ¤À¡£x' == x ¤Ê¤é¤ÐËܳÊŪ¤Ë¾È¹ç¤ò³«»Ï¤¹¤ë¡£ +これは、図の x の位置だ。x' == x ならば本格的に照合を開始する。 { a = text + scan_pos - off; b = text + pos; for (len = 0; len < max && *a++ == *b++; len++); } -¤³¤³¤ÇÈæ³Ó¤·¤Æ¤¤¤ë¤Î¤Ï¡¢¿Þ¤Î a ¤È b ¤À¡£b ¤Ï¡¢off ¤¬¤É¤Î¤è¤¦¤Ê¾ì¹ç¤Ç¤â -ÊѤï¤é¤Ê¤¤¤¬¡¢a ¤Ï¡¢off ¤¬Â礭¤±¤ì¤ÐÂ礭¤¤Äøº¸Â¦¤ò»Ø¤¹¡£off ¤¬Î㤨¤Ð -3 ¤Ç¤¢¤ë¤È¤­¤Î¾ì¹ç¤â¸«¤Æ¤ß¤è¤¦¡£ +ここで比較しているのは、図の a と b だ。b は、off がどのような場合でも +変わらないが、a は、off が大きければ大きい程左側を指す。off が例えば +3 であるときの場合も見てみよう。 ---------------------------------------------------------------------------- @@ -1861,48 +1861,48 @@ text | x'| | | | | | |x | | | | ---------------------------------------------------------------------------- -·ë¶ÉÈæ³Ó¤·¤Æ¤¤¤ë¤Î¤Ï¡¢pos ¤«¤é¤Îʸ»úÎó¤Î¥Ï¥Ã¥·¥åÃͤòµá¤á¤¿¾ì¹ç¤È²¿¤âÊÑ -¤ï¤é¤Ê¤¤¡£off ¤Ç¤¤¤¯¤éÀè¤ò¸«¤è¤¦¤È¤âÈæ³Ó¤¹¤ë¤Î¤Ï pos ¤Î°ÌÃÖ¤À¡£¤Ê¤¼¤³ -¤Î¤è¤¦¤Ê¤³¤È¤ò¤¹¤ë¤Î¤À¤í¤¦¤«¡©¤³¤ì¤ÏºÇ½é¤É¤¦¤·¤Æ¤â¤ï¤«¤é¤Ê¤«¤Ã¤¿¤Î¤À¤¬¡¢ -ÀâÌÀ¤ò¼õ¤±¤ÆǼÆÀ¤·¤¿¡£ +結局比較しているのは、pos からの文字列のハッシュ値を求めた場合と何も変 +わらない。off でいくら先を見ようとも比較するのは pos の位置だ。なぜこ +のようなことをするのだろうか?これは最初どうしてもわからなかったのだが、 +説明を受けて納得した。 -¤³¤ì¤Ïñ¤Ë¸úΨ(®ÅÙ)¤Î¤¿¤á¤È¤¤¤¦¤³¤È¤À¡£¤â¤·¡¢¿Þ¤Î b ¤Ë´Ø¤·¤Æ¾È¹çʸ»ú -Îó¤Î¸õÊ䤬¤¢¤Þ¤ê¤Ë¤â¿¤¤¾ì¹ç(too_flag[h]=1)¡¢¥Ï¥Ã¥·¥å¤Î¥Á¥§¡¼¥ó¤ò²¿ÅÙ -¤â¤¿¤É¤ë»ö¤Ë¤Ê¤ë¤Î¤Ç¸úΨ¤¬°­¤¤¡£¤·¤«¤·¡¢¼­½ñ¸¡º÷¤Î¥­¡¼¤ò²¿Ê¸»ú¤«¿Ê¤á¤ë -»ö¤Ç¡¢¤³¤Î²ÄǽÀ­¤ò¸º¤é¤¹»ö¤¬¤Ç¤­¤ë¡£¾¯¤Ê¤¯¤È¤âºÇ°­¤Î 256 ¤è¤ê¤Ï¥Þ¥·¤Ë -¤Ê¤ë¤è¤¦¤Ê¤â¤Î¤òÁª¤ó¤Ç¤¤¤ë¡£¤½¤¦¤·¤Æ¡¢¤³¤Î while ¥ë¡¼¥×¤Î¥ë¡¼¥×²ó¿ô¤ò -¸º¤é¤·¤Æ¤¤¤ë¤Î¤À¡£¤É¤¦¤»Ãµ¤·¤¿¤¤¤Î¤ÏºÇĹ°ìÃ×ʸ»úÎó¤Ê¤Î¤ÇÀè¤ÎÊý¤Îʸ»úÎó -¤¬°ìÃפ·¤Ê¤¤¤ÈÏäˤʤé¤Ê¤¤¤Î¤À¤«¤é¤³¤ì¤Ï¹çÍýŪ¤À¡£ +これは単に効率(速度)のためということだ。もし、図の b に関して照合文字 +列の候補があまりにも多い場合(too_flag[h]=1)、ハッシュのチェーンを何度 +もたどる事になるので効率が悪い。しかし、辞書検索のキーを何文字か進める +事で、この可能性を減らす事ができる。少なくとも最悪の 256 よりはマシに +なるようなものを選んでいる。そうして、この while ループのループ回数を +減らしているのだ。どうせ探したいのは最長一致文字列なので先の方の文字列 +が一致しないと話にならないのだからこれは合理的だ。 -¤³¤ì¤Ç¡¢³°Â¦¤Î for ¥ë¡¼¥×¤âǼÆÀ¤À¡£¤³¤ì¤Ï while ¥ë¡¼¥×¤ò¤¢¤ë¾ò·ï¤Ç¤ä¤ê -ľ¤¹½èÍý¤À¤Ã¤¿¡£ +これで、外側の for ループも納得だ。これは while ループをある条件でやり +直す処理だった。 if (matchlen > off + 2 || off == 0) break; -ºÇĹ°ìÃ׍¬¸«¤Ä¤«¤ë¤«¡¢¤¢¤ë¤¤¤Ï off ¤¬ 0 ¤Ç¤¢¤ì¤Ð¤µ¤Ã¤µ¤È¤³¤Î½èÍý¤Ï½ª -¤ë¤Î¤À¤¬¡¢¤â¤· off ¤ò¿Ê¤á¤Æ¾È¹ç¤ò¹Ô¤Ã¤Æ¤¤¤¿¤È¤·¤Æ¡¢ºÇĹ°ìÃ×ʸ»úÎ󤬸« -¤Ä¤«¤é¤Ê¤«¤Ã¤¿¤È¤¹¤ë¤È +最長一致長が見つかるか、あるいは off が 0 であればさっさとこの処理は終 +るのだが、もし off を進めて照合を行っていたとして、最長一致文字列が見 +つからなかったとすると max = off + 2; off = 0; h = hval; -¤È¤¤¤¦¾ò·ï¤Ç¾È¹ç¤ò¤ä¤êľ¤¹¡£¤³¤ì¤Ï¸µ¤Îʸ»úÎó¤Ç¾È¹ç¤ò¤ä¤êľ¤¹¤È¤¤¤¦¤³¤È -¤À¤«¤é¤Ä¤Þ¤ê¤Ï¡¢ºÇ°­¤Î¥Ï¥Ã¥·¥å¥Á¥§¡¼¥ó¤ò»ÅÊý¤Ê¤¤¤«¤éé¤êľ¤½¤¦¤È¸À¤¦»ö -¤À¡£¤µ¤é¤Ë¡¢pos ¤«¤é pos+off+3 ¤Þ¤Ç¤Îʸ»úÎ󤬡¢¼­½ñ¤«¤é¸«¤Ä¤«¤é¤Ê¤«¤Ã -¤¿¤Î¤Ç¡¢ºÇÂç°ìÃ׍ò off + 2 ¤È¤·¤Æ¾ò·ï¤ò´Ë¤á¤Æ¤¤¤ë(¤Ê¤¼¤³¤ì¤¬¾ò·ï¤ò´Ë -¤á¤ë»ö¤Ë¤Ê¤ë¤«¤È¸À¤¦¤È while ¥ë¡¼¥×¤ÏºÇÂç°ìÃ׍Îʸ»úÎ󤬸«¤Ä¤«¤Ã¤¿¤é -¤¹¤°¤ËÈ´¤±¤ë¤«¤é¤À)¡£ +という条件で照合をやり直す。これは元の文字列で照合をやり直すということ +だからつまりは、最悪のハッシュチェーンを仕方ないから辿り直そうと言う事 +だ。さらに、pos から pos+off+3 までの文字列が、辞書から見つからなかっ +たので、最大一致長を off + 2 として条件を緩めている(なぜこれが条件を緩 +める事になるかと言うと while ループは最大一致長の文字列が見つかったら +すぐに抜けるからだ)。 -¤È¤³¤í¤Ç¡¢match_insert() ¤ÎÀè¤Î½èÍý¤Ï°Ê²¼¤Î½ñ¤­´¹¤¨¤ò¹Ô¤¦¤È¤â¤¦¾¯¤·¸« -¤ä¤¹¤¯¤Ê¤ë¡£(¤È»×¤¦)¡£ +ところで、match_insert() の先の処理は以下の書き換えを行うともう少し見 +やすくなる。(と思う)。 -o scan_beg ¤È¤¤¤¦ÊÑ¿ô¤òÍÑ°Õ¤·¡¢¤³¤ì¤ò scan_pos - off ¤Ë¤¹¤ë¡£ -o scan_end ¤Ï¡¢pos - dicsiz ¤Ë¤¹¤ë¡£ -o while ¾ò·ï¤ò while (scan_pos != NIL && scan_beg > scan_end) ¤Ë¤¹¤ë¡£ +o scan_beg という変数を用意し、これを scan_pos - off にする。 +o scan_end は、pos - dicsiz にする。 +o while 条件を while (scan_pos != NIL && scan_beg > scan_end) にする。 -°Ê²¼ +以下 unsigned int scan_pos = hash[h]; int scan_beg = scan_pos - off; @@ -1945,50 +1945,50 @@ text | | x'| | | | | | |x | | | | scan_end |----| - scan_beg ¤ÎÍ­¸úÈÏ°Ï + scan_beg の有効範囲 |----------------- dicsiz ------------------| ---------------------------------------------------------------------------- -scan_beg, scan_end ¤ÎÈϰϤâ¤ï¤«¤ê¤ä¤¹¤¤¤·¡¢hash[h] ¤¬ NIL ¤Î¾ì¹ç¤Î½èÍý -¤âÌÀ¼¨Åª¤À¡£¤³¤Î½ñ¤­´¹¤¨¤ò¹Ô¤¦¾ì¹ç¡¢scan_beg ¤¬Éé¤ÎÃͤˤʤë²ÄǽÀ­¤¬¤¢ -¤ë¡£¸µ¤â¤È¤Î½èÍý¤Ç¤Ï scan_end Åù¤ÎÊÑ¿ô¤ò unsigned ¤Ë¤·¤Æ¤¤¤ë¤Î¤Ç¡¢¤³¤ì -¤é¤ò int ¤Ë¤·¤Æ while ¾ò·ï¤ÇÉé¤Î scan_beg ¤ò¤Ï¤¸¤«¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¤³¤È -¤ËÃí°Õ¡£¤½¤¦¤¹¤ë¤È¡¢scan_pos != NIL ¤ÏɬÍפʤ¯¤Ê¤ë¤Î¤À¤¬¤ï¤«¤ê¤ä¤¹¤µ¤ò -Äɵᤷ¤¿¡£ +scan_beg, scan_end の範囲もわかりやすいし、hash[h] が NIL の場合の処理 +も明示的だ。この書き換えを行う場合、scan_beg が負の値になる可能性があ +る。元もとの処理では scan_end 等の変数を unsigned にしているので、これ +らを int にして while 条件で負の scan_beg をはじかなければならないこと +に注意。そうすると、scan_pos != NIL は必要なくなるのだがわかりやすさを +追求した。 -¤³¤ì¤Ç match_insert() ¤Î²òÆɤϽª¤ê¤À¡£match_insert() ¤Î½èÍý¤È¤Ï°Ê²¼¤Î -Ä̤ê¤À¡£ +これで match_insert() の解読は終りだ。match_insert() の処理とは以下の +通りだ。 ---------------------------------------------------------------------------- - match_insert() ¤Ï¡¢text[pos] ¤«¤é»Ï¤Þ¤ëʸ»úÎó¤Ë°ìÃפ¹¤ëʸ»úÎó¤ò¼­½ñ - ¤«¤é¸¡º÷¤·¡¢¸«¤Ä¤«¤Ã¤¿°ÌÃ֤ȰìÃ׍ò matchpos, matchlen ¤ËÀßÄꤹ¤ë¡£ + match_insert() は、text[pos] から始まる文字列に一致する文字列を辞書 + から検索し、見つかった位置と一致長を matchpos, matchlen に設定する。 - ¤â¤·¡¢ºÇĹ°ìÃ×ʸ»úÎ󤬸«¤Ä¤«¤é¤Ê¤±¤ì¤Ð matchpos ¤Ï¡¢pos ¤ËÀßÄꤵ¤ì¡¢ - matchlen ¤Ï¹¹¿·¤µ¤ì¤Ê¤¤¡£(¼Â¤Ï¡¢matchpos = pos ¤Î¾ðÊó¤ÏÆä˻Ȥï¤ì¤Æ¤Ê¤¤) + もし、最長一致文字列が見つからなければ matchpos は、pos に設定され、 + matchlen は更新されない。(実は、matchpos = pos の情報は特に使われてない) - ¸«¤Ä¤«¤Ã¤¿¾ì¹ç¡¢matchlen ¤Ï¸Æ¤Ó½Ð¤·Á°¤Î matchlen ¤è¤ê¤âÂ礭¤¯¤Ê¤ë¡£ - (¸Æ¤Ó½Ð¤·Á°¤Ç¤Ï matchlen ¤Î°ÕÌ£¤ÏºÇÄã¸Â°ìÃפ·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤Ê¸»úÎó - Ĺ¤Ç¡¢¾È¹ç¾ò·ï¤Î°ì¤Ä¤Ë¤Ê¤Ã¤Æ¤¤¤ë) + 見つかった場合、matchlen は呼び出し前の matchlen よりも大きくなる。 + (呼び出し前では matchlen の意味は最低限一致しなくてはならない文字列 + 長で、照合条件の一つになっている) - ¤³¤Î´Ø¿ô¤ÎÆþÎÏ¤Ï + この関数の入力は matchlen pos - ½ÐÎÏ¤Ï + 出力は matchlen matchpos - ¤È¤¤¤Ã¤¿¤È¤³¤í¡£ + といったところ。 - ¤µ¤é¤Ë¡¢insert() ¤ÈƱÍͤνèÍý¤Ç¡¢pos ¤Î°ÌÃÖ¤ò¥Ï¥Ã¥·¥åÇÛÎó¤Ëµ­Ï¿¤·¡¢ - ¼¡²ó¤Î¸¡º÷¤ËÈ÷¤¨¤ë¡£¤³¤ì¤Ï¤Ä¤¤¤Ç¤Î½èÍý¡£ + さらに、insert() と同様の処理で、pos の位置をハッシュ配列に記録し、 + 次回の検索に備える。これはついでの処理。 ---------------------------------------------------------------------------- -¤³¤ì¤òƧ¤Þ¤¨¤¿¾å¤Ç½èÍýÆâÍƤòºÆÆɤ·¤è¤¦¡£(E) ¡Á (H) ¤À¡£ +これを踏まえた上で処理内容を再読しよう。(E) 〜 (H) だ。 /* (E) */ lastmatchlen = matchlen; lastmatchoffset = pos - matchpos - 1; @@ -2005,42 +2005,42 @@ scan_beg, scan_end count++; } else { -(H) ¤Î¾ò·ï¤È¤Ï²¿¤Ê¤Î¤«¤ò¸«¤ë¡£¤³¤Î¾ò·ï¤¬¿¿¤Ê¤é¡¢Ê¸»úÎó¤ò¤½¤Î¤Þ¤Þ½ÐÎϤ¹ -¤ë¤Î¤À¤¬¡¢ÁÇľ¤Ë slide ¼­½ñË¡¤Î½èÍý¤ò¹Í¤¨¤ì¤Ð¤³¤Î¾ò·ï¤Ï¡Ö¼­½ñ¤«¤é¸«¤Ä -¤«¤é¤Ê¤«¤Ã¤¿¾ì¹ç¡×¤È¤Ê¤ë¡£¤¬¡¢¼ÂºÝ¤Ë¤Ï¤â¤¦¾¯¤·Ê£»¨¤À¡£ +(H) の条件とは何なのかを見る。この条件が真なら、文字列をそのまま出力す +るのだが、素直に slide 辞書法の処理を考えればこの条件は「辞書から見つ +からなかった場合」となる。が、実際にはもう少し複雑だ。 /* (H) */ if (matchlen > lastmatchlen || lastmatchlen < THRESHOLD) { -matchlen ¤Ï¡¢pos ¤Î°ÌÃÖ¤Îʸ»úÎ󤬸«¤Ä¤«¤Ã¤¿¼­½ñ¾å¤ÎŤµ -lastmatchlen ¤Ï¡¢pos-1 ¤Î°ÌÃÖ¤Îʸ»úÎ󤬸«¤Ä¤«¤Ã¤¿¼­½ñ¾å¤ÎŤµ +matchlen は、pos の位置の文字列が見つかった辞書上の長さ +lastmatchlen は、pos-1 の位置の文字列が見つかった辞書上の長さ -¤Ç¤¢¤ë¤È¤¹¤ë¤È¡¢¤³¤Î¾ò·ï¤Ï¡¢¡Öpos ¤Î°ÌÃ֤Ǹ«¤Ä¤«¤Ã¤¿Ä¹¤µ¤¬¡¢pos-1 ¤Î°Ì -Ã֤Ǹ«¤Ä¤«¤Ã¤¿Ä¹¤µ¤è¤ê¤âŤ±¤ì¤Ð¡×¤Ã¤È¤Ê¤ë¡£ +であるとすると、この条件は、「pos の位置で見つかった長さが、pos-1 の位 +置で見つかった長さよりも長ければ」っとなる。 -¤³¤ì¤Ï¤Ä¤Þ¤ê¡¢pos-1 ¤È pos ¤Î¥Ë²Õ½ê¤Ë´Ø¤·¤Æ¼­½ñ¤ò¸¡º÷¤·¤Æ¡¢¤è¤êŤ¯¥Þ¥Ã -¥Á¤¹¤ëÊý¤òÁª¤Ü¤¦¤È¤·¤Æ¤¤¤ë¤ï¤±¤À¡£matchlen ¤ÎÊý¤¬Ä¹¤¤¤Ê¤é 1 ¤ÄÁ° -(pos-1)¤Îʸ»ú¤Ï¤½¤Î¤Þ¤Þ½ÐÎϤ·¡¢¼¡¤Î¥ë¡¼¥×¤Ë°Ü¤ë(¤â¤·¡¢¼¡¤Îʸ»ú¤¬ -¤µ¤é¤ËŤ¯¥Þ¥Ã¥Á¤¹¤ë¤Ê¤é¡£¤Þ¤¿¤½¤Î¤Þ¤Þ½ÐÎϤµ¤ì¤ë) +これはつまり、pos-1 と pos のニ箇所に関して辞書を検索して、より長くマッ +チする方を選ぼうとしているわけだ。matchlen の方が長いなら 1 つ前 +(pos-1)の文字はそのまま出力し、次のループに移る(もし、次の文字が +さらに長くマッチするなら。またそのまま出力される) -¤³¤Î¾ò·ï¤Ç¡Ö¸«¤Ä¤«¤é¤Ê¤«¤Ã¤¿¾ì¹ç¡×¤È¤¤¤¦¤Î¤Ï¤É¤¦É½¤µ¤ì¤Æ¤¤¤ë¤«¤ò¹Í¤¨¤ë¡£ -¤â¤·¡¢pos ¤Îʸ»úÎ󤬼­½ñ¤Ë¤Ê¤±¤ì¤Ð pos - 1 ¤Îʸ»úÎó¤Ï¡¢¤É¤¦¤¹¤Ù¤­¤«¤È -¤¤¤¦¤È¡Öpos-1 ¤Îʸ»úÎ󤬸«¤Ä¤«¤Ã¤Æ¤Ê¤±¤ì¤Ð¡£¤½¤Î¤Þ¤Þ½ÐÎÏ¡£¼­½ñ¤Ë¤¢¤Ã¤¿ -¤Ê¤é ¤Î¥Ú¥¢¤ò½ÐÎϡפäȤʤé¤Ê¤±¤ì¤Ð¤Ê -¤é¤Ê¤¤¡£ +この条件で「見つからなかった場合」というのはどう表されているかを考える。 +もし、pos の文字列が辞書になければ pos - 1 の文字列は、どうすべきかと +いうと「pos-1 の文字列が見つかってなければ。そのまま出力。辞書にあった +なら のペアを出力」っとならなければな +らない。 -lastmatchlen ¤Ï¡¢½é´ü¾õÂÖ¤Ç¤Ï THRESHOLD - 1 ¤Ç¤¢¤Ã¤¿¤Î¤Ç¡¢¸«¤Ä¤«¤é¤Ê¤«¤Ã -¤¿¤È¤¤¤¦¾ò·ï¤Ï (H) ¤Î±¦Â¦¤Î¾ò·ï lastmatchlen < THRESHOLD ¤Ç¤Þ¤ºÉ½¤µ¤ì -¤Æ¤¤¤ë¡£ +lastmatchlen は、初期状態では THRESHOLD - 1 であったので、見つからなかっ +たという条件は (H) の右側の条件 lastmatchlen < THRESHOLD でまず表され +ている。 -¤Ç¤Ï¡¢Î㤨¤Ð lastmatchlen ¤¬ 5 ¤Ç¤¢¤Ã¤¿¤È¤·¤è¤¦¡£¤³¤Î¤È¤­ (E) ¤Î½èÍý¤Ç -matchlen ¤Ï lastmatchlen - 1 ¤Ä¤Þ¤ê¡¢4 ¤ËÀßÄꤵ¤ì¤ë¡£¤½¤·¤Æ¡¢match_insert() -¤Ç¼¡¤Îʸ»úÎ󤬤⤷¼­½ñ¤«¤é¸«¤Ä¤«¤é¤Ê¤±¤ì¤Ð matchlen ¤Ï¹¹¿·¤µ¤ì¤Ê¤¤¤Î¤Ç +では、例えば lastmatchlen が 5 であったとしよう。このとき (E) の処理で +matchlen は lastmatchlen - 1 つまり、4 に設定される。そして、match_insert() +で次の文字列がもし辞書から見つからなければ matchlen は更新されないので matchlen < lastmatchlen -¤È¤Ê¤ë¡£¤³¤Î¤è¤¦¤Ê¾ò·ï(Á°²ó¸«¤Ä¤«¤ê¡¢º£²ó¸«¤Ä¤«¤é¤Ê¤¤)¾ì¹ç¤Ë¸Â¤ê¡¢(H.2) -¤Î½èÍý¤¬¼Â¹Ô¤µ¤ì¤ë¤è¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤ë¡£¤Ç¤Ï¡¢(H.2) ¤Î½èÍý¤òÄɤ¤¤«¤±¤è¤¦¡£ +となる。このような条件(前回見つかり、今回見つからない)場合に限り、(H.2) +の処理が実行されるようになっている。では、(H.2) の処理を追いかけよう。 -¤Þ¤º¡¢¤³¤Î¾õÂÖ¤ò¿Þ¼¨¤¹¤ë¡£ +まず、この状態を図示する。 ---------------------------------------------------------------------------- @@ -2073,63 +2073,63 @@ text | | | | | | | | | | | | | | 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 ¤Î¾È¹ç·ë²Ì¤ÈÈæ³Ó¤µ¤ì¤ë¤Î¤Ç¤³¤Î½èÍý¤âÁÛÁü¤¬¤Ä¤¯¡£ - -ºÇ½é¡¢¤É¤¦¤Ë¤â¤³¤Î½èÍýÆâÍƤ¬Íý²ò¤Ç¤­¤Ê¤«¤Ã¤¿¤Î¤À¤¬¡Ö¸½ºß¤Îʸ»úÎó¤È¡¢¼¡ -¤Îʸ»úÎó¤Î¤½¤ì¤¾¤ì¤Ç¼­½ñ¤ò¸¡º÷¤·¡¢¤è¤êŤ¯¸«¤Ä¤«¤Ã¤¿Êý¤ò»È¤¦¡×¤È¤¤¤¦ºÇ -Ŭ²½¤ò¹Ô¤Ã¤Æ¤¤¤ë»ö¤¬¤ï¤«¤Ã¤Æ¤·¤Þ¤Ã¤¿¸å¤Ï²òÆɤϴÊñ¤À¤Ã¤¿¡£(¼Â¤Ï¤³¤Î»ö -¼Â¤â¶µ¤¨¤Æ¤â¤é¤Ã¤¿»ö¤À¡£Á´Á³¥À¥á¤¸¤ã¤ó) - -¤µ¤Æ¡¢¤³¤ì¤Ç°ìÄ̤ê¤Î²òÀϤϺѤó¤À¤ï¤±¤À¤¬¡¢¤³¤³¤Þ¤Ç¤Î²òÀÏÆâÍƤòÆɤßľ¤· -¤Æ¤ß¤ë¤È¡¢°Ê²¼¤ÎÅÀ¤¬¤Þ¤À¤Ò¤Ã¤«¤«¤ë¡£ - -1. ¥Ï¥Ã¥·¥å´Ø¿ô¤ÏºÇŬ¤Ê¤Î¤«¡©ÆÃ¤Ë HSHSIZ{2^15} ¤ÏºÇŬ¤Ê¤Î¤«¡© -2. too_flag[] ¤Ï¡¢¼ÂºÝ¤Ë¾È¹ç¤ò¹Ô¤¤¥ë¡¼¥×¤¬LIMIT¤ò±Û¤¨¤¿¾ì¹ç¤Ë - ÀßÄꤵ¤ì¤ë¡£¤·¤«¤·¡¢¥Ï¥Ã¥·¥å¤Î¥Á¥§¡¼¥ó¤òºî¤ëºÝ¤Ë¥Á¥§¡¼¥ó¤Î - ¸Ä¿ô¤ò¤¢¤é¤«¤¸¤á¿ô¤¨¤Æ¤ª¤±¤Ð°ìÅÙ¤Îõº÷¤¹¤é¤â¹Ô¤ï¤ì¤º¡£¤è¤ê - Á᤯½èÍý¤µ¤ì¤Ê¤¤¤À¤í¤¦¤«¡© - -¸½¾õ¡¢1, 2 ¤È¤â¼Â»Ü¤·¤Æ¤ß¤¿¤È¤³¤íÆäË®Å٤βþÁ±¤Ï¸«¤é¤ì¤Ê¤«¤Ã¤¿¡£ÆÃ¤Ë -1 ¤Ï¡¢Èù̯¤Ê¤È¤³¤í¤¬¤¢¤ê¤Û¤È¤ó¤É¤Î½ñ¤­´¹¤¨¤ÏÀ­Ç½¤ò°­¤¯¤¹¤ë¤À¤±¤À¤Ã¤¿¡£ -¤Ê¤«¤Ê¤«¶½Ì£¿¼¤¤¤â¤Î¤¬¤¢¤ë¡£ - -¤³¤ì¤Ïº£¸å¤Î²ÝÂê¤È¤·¤Æ¤¤¤º¤ì¤Þ¤¿¸¡¾Ú¤·¤è¤¦¡£¤½¤í¤½¤í slide.c ¤âË°¤­¤Æ -¤­¤¿¤Î¤Ç¤Ò¤È¤Þ¤º¤Ï¤³¤ì¤Ç½ª¤ê¤Ë¤·¤¿¤¤¡£ +まず、<長さ, 位置> のペアを出力する。これはいいだろう。出力する「位置」 +は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 の照合結果と比較されるのでこの処理も想像がつく。 + +最初、どうにもこの処理内容が理解できなかったのだが「現在の文字列と、次 +の文字列のそれぞれで辞書を検索し、より長く見つかった方を使う」という最 +適化を行っている事がわかってしまった後は解読は簡単だった。(実はこの事 +実も教えてもらった事だ。全然ダメじゃん) + +さて、これで一通りの解析は済んだわけだが、ここまでの解析内容を読み直し +てみると、以下の点がまだひっかかる。 + +1. ハッシュ関数は最適なのか?特に HSHSIZ{2^15} は最適なのか? +2. too_flag[] は、実際に照合を行いループがLIMITを越えた場合に + 設定される。しかし、ハッシュのチェーンを作る際にチェーンの + 個数をあらかじめ数えておけば一度の探索すらも行われず。より + 早く処理されないだろうか? + +現状、1, 2 とも実施してみたところ特に速度の改善は見られなかった。特に +1 は、微妙なところがありほとんどの書き換えは性能を悪くするだけだった。 +なかなか興味深いものがある。 + +これは今後の課題としていずれまた検証しよう。そろそろ slide.c も飽きて +きたのでひとまずはこれで終りにしたい。 -bit Æþ½ÐÎϥ롼¥Á¥ó (crcio.c) +bit 入出力ルーチン (crcio.c) --------------------------- -¤³¤ì¤«¤é Huffman Ë¡¤Î²òÆɤ˰ܤë¤Î¤À¤¬¡¢¤½¤ÎÁ°½àÈ÷¤È¤·¤Æ bit Æþ½ÐÎϥ롼 -¥Á¥ó¤Î²òÆɤò¹Ô¤¦¡£Huffman Ë¡¤Î¼ÂÁõ¤Ç¤Ïɬ¤º bit Æþ½ÐÎϽèÍý¤¬É¬Íפˤʤ롣 -LHa for UNIX ¤â¤â¤Á¤í¤óÎã³°¤Ç¤Ï¤Ê¤¯¡¢Huffman Ë¡¤Î¼ÂÁõ¤ò²òÆɤ¹¤ë¤Ë¤¢¤¿ -¤ê¤³¤ÎÉôʬ¤Î½èÍýÆâÍƤϤϤ䭤ꤵ¤»¤Æ¤ª¤¤¤¿Êý¤¬Îɤ¤¤È¹Í¤¨¤¿¤Î¤À¡£ +これから Huffman 法の解読に移るのだが、その前準備として bit 入出力ルー +チンの解読を行う。Huffman 法の実装では必ず bit 入出力処理が必要になる。 +LHa for UNIX ももちろん例外ではなく、Huffman 法の実装を解読するにあた +りこの部分の処理内容ははっきりさせておいた方が良いと考えたのだ。 -LHa for UNIX version 1.14i ¤Ç¤Ï bit Æþ½ÐÎϥ롼¥Á¥ó¤Ï crcio.c ¤ÇÄêµÁ¤µ -¤ì¤Æ¤¤¤ë¡£(¤³¤Î¤è¤¦¤Ê¥Õ¥¡¥¤¥ë̾¤Ë¸ºß¤¹¤ë¤Î¤Ï°Õ³°¤Ê»ö¤À¡£ºÇ¶á¤Î LHa -for UNIX ¤Ç¤Ï¡¢»ä¤¬ bitio.c ¤È¤¤¤¦¥Õ¥¡¥¤¥ë¤òÀߤ±¡¢bit Æþ½ÐÎϥ롼¥Á¥ó¤Ï -¤½¤³¤ËÀÚ¤ê½Ð¤·¤¿) +LHa for UNIX version 1.14i では bit 入出力ルーチンは crcio.c で定義さ +れている。(このようなファイル名に存在するのは意外な事だ。最近の LHa +for UNIX では、私が bitio.c というファイルを設け、bit 入出力ルーチンは +そこに切り出した) -crcio.c ¤Î¤¦¤Á bit Æþ½ÐÎϥ롼¥Á¥ó¤Ï fillbuf(), getbits(), putcode(), -putbits(), init_getbits(), init_putbits() ¤Î 6 ´Ø¿ô¤À¡£ +crcio.c のうち bit 入出力ルーチンは fillbuf(), getbits(), putcode(), +putbits(), init_getbits(), init_putbits() の 6 関数だ。 -¤Þ¤º¡¢½é´ü²½ÍѤΠinit_getbits(), init_putbits() ¤ò¸«¤è¤¦¡£ +まず、初期化用の init_getbits(), init_putbits() を見よう。 void init_getbits( /* void */ ) @@ -2151,20 +2151,20 @@ init_putbits( /* void */ ) getc_euc_cache = EOF; } -¤½¤ì¤¾¤ì bit ÆþÎÏ¡¢bit ½ÐÎϤò¹Ô¤¦¤¿¤á¤Î½é´ü²½½èÍý¤À¡£CHAR_BIT ¤È¤¤¤¦¤Î -¤Ï 8 ¤Ç¡¢char ·¿¤Î bit ¥µ¥¤¥º¤òɽ¤·¤Æ¤¤¤ë¤é¤·¤¤¡£¾ÜºÙ¤Ï¤ï¤«¤é¤Ê¤¤¤¬½é´ü -¾õÂ֤ϤȤˤ«¤¯¤³¤ì¤À¡£¤³¤³¤Ç»ÈÍѤµ¤ì¤Æ¤¤¤ëÊÑ¿ô¤Ï¡¢ +それぞれ bit 入力、bit 出力を行うための初期化処理だ。CHAR_BIT というの +は 8 で、char 型の bit サイズを表しているらしい。詳細はわからないが初期 +状態はとにかくこれだ。ここで使用されている変数は、 static unsigned char subbitbuf, bitcount; -¤¬¡¢crcio.c ¤ÇÄêµÁ¤µ¤ì¤Æ¤ª¤ê¡¢ +が、crcio.c で定義されており、 EXTERN unsigned short bitbuf; -¤¬¡¢lha.h ¤ÇÄêµÁ¤µ¤ì¤Æ¤¤¤ë(EUC ¤Ê¤ó¤¿¤é¤ÏËܼÁ¤Ç¤Ï¤Ê¤¤Ìµ»ë¤·¤è¤¦)¡£¥°¥í¡¼ -¥Ð¥ëÊÑ¿ô¤È¸À¤¦¤Î¤Ï´÷¤à¤Ù¤­¤â¤Î¤À¤¬¡¢¤È¤Ë¤«¤¯»ÈÍѤµ¤ì¤Æ¤¤¤ëÊÑ¿ô¤È½é´üÃÍ -¤ò³Îǧ¤·¤¿¤Î¤Ç¼¡¤Ë°Ü¤í¤¦¡£init_getbits() ¤Ç¡¢Áá® fillbuf() ¤¬¸Æ¤Ð¤ì¤Æ -¤¤¤ë¡£¤³¤Î½èÍýÆâÍƤò¸«¤ë¡£ +が、lha.h で定義されている(EUC なんたらは本質ではない無視しよう)。グロー +バル変数と言うのは忌むべきものだが、とにかく使用されている変数と初期値 +を確認したので次に移ろう。init_getbits() で、早速 fillbuf() が呼ばれて +いる。この処理内容を見る。 void fillbuf(n) /* Shift bitbuf n bits left, read n bits */ @@ -2190,31 +2190,31 @@ fillbuf(n) /* Shift bitbuf n bits left, read n bits */ subbitbuf <<= n; } -¤Þ¤º¡¢½é´ü¾õÂ֤Ȥ·¤Æ +まず、初期状態として bitbuf = 0; subbitbuf = 0; bitcount = 0; -¤Ç¤¢¤ê¡¢fillbuf ¤Î°ú¿ô n ¤Ë¤Ï 2 * CHAR_BIT ¤¬Í¿¤¨¤é¤ì¤¿¤Î¤À¤Ã¤¿¡£¤¤¤­ -¤Ê¤ê while ¾ò·ï¤òËþ¤¿¤¹¤Î¤Ç¥ë¡¼¥×Éô¤Î²òÀϤò¹Ô¤ï¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¯¤Ê¤ë¤¬¡¢ -¤Ò¤È¤Þ¤º¤³¤ì¤ò̵»ë¤·¤ÆºÇ¸å¤Î 3 ¹Ô (D) ¤ËÃåÌܤ¹¤ë¡£¾ò·ï¤Ï¾¯¤Ê¤¤Êý¤¬Îɤ¤ -¤Î¤À¡£ +であり、fillbuf の引数 n には 2 * CHAR_BIT が与えられたのだった。いき +なり while 条件を満たすのでループ部の解析を行わなくてはならなくなるが、 +ひとまずこれを無視して最後の 3 行 (D) に着目する。条件は少ない方が良い +のだ。 /* (D) */ bitcount -= n; bitbuf = (bitbuf << n) + (subbitbuf >> (CHAR_BIT - n)); subbitbuf <<= n; -bitbuf << n, subbitbuf << n ¤µ¤ì¤Æ¤¤¤ë¤Î¤Ç¡¢bitbuf, subbitbuf ¤ò n ¥Ó¥Ã -¥Èº¸¤Ë¤º¤é¤¹½èÍý¤Î¤è¤¦¤À¡£¤µ¤é¤Ë bitbuf ¤Ë¤Ï¡¢subbitbuf ¤ò n ¥Ó¥Ã¥È¤º -¤é¤·¤¿¤È¤­¤Ë°î¤ì¤¿Éôʬ¤ò bitbuf ¤Ë¥»¥Ã¥È¤·¤Æ¤¤¤ë¡£¤Ã¤È¡¢ +bitbuf << n, subbitbuf << n されているので、bitbuf, subbitbuf を n ビッ +ト左にずらす処理のようだ。さらに bitbuf には、subbitbuf を n ビットず +らしたときに溢れた部分を bitbuf にセットしている。っと、 (subbitbuf >> (CHAR_BIT - n)) -¤ÎÉôʬ¤ò·Ú¤¯ÀâÌÀ¤·¤¿¤¬¡¢¿Þ¼¨¤·¤Æ³Îǧ¤·¤Æ¤ª¤³¤¦¡£ +の部分を軽く説明したが、図示して確認しておこう。 -subbitbuf ¤Ï unsigned char ¤Ê¤Î¤Ç 8 bit ¤ÎÊÑ¿ô¤À¡£ +subbitbuf は unsigned char なので 8 bit の変数だ。 ---------------------------------------------------------------------------- 7 6 5 4 3 2 1 0 @@ -2224,16 +2224,16 @@ subbitbuf <-- n --> ---------------------------------------------------------------------------- -n ¤¬Î㤨¤Ð 3 ¤Î¾ì¹ç¡¢CHAR_BIT - n ¤Ï¡¢5 ¤À¤«¤é subbitbuf ¤ò 5 ¥Ó¥Ã¥È±¦ -¤Ë¤º¤é¤·¤¿Ãͤò¼è¤Ã¤Æ¤¤¤ë¡£¤Ä¤Þ¤ê¡¢¿Þ¤Î 7, 6, 5 ¥Ó¥Ã¥ÈÌܤ¬°ìÈÖ±¦¤ËÍè¤ë -¤è¤¦¤Ë¤Ê¤Ã¤Æ¤ª¤ê¡¢¤³¤ÎÃͤò bitbuf ¤Ë­¤·¤Æ¤¤¤ë¡£(C¸À¸ì¤Ç¤Ï¡¢unsigned -¤ÊÊÑ¿ô¤ò±¦¤Ë¥·¥Õ¥È¤¹¤ë¤È¾å°Ì¥Ó¥Ã¥È¤Ë¤Ï 0 ¤¬Æþ¤ë) +n が例えば 3 の場合、CHAR_BIT - n は、5 だから subbitbuf を 5 ビット右 +にずらした値を取っている。つまり、図の 7, 6, 5 ビット目が一番右に来る +ようになっており、この値を bitbuf に足している。(C言語では、unsigned +な変数を右にシフトすると上位ビットには 0 が入る) -fillbuf() ¤Î¸åȾ 3 ¹Ô(¤¤¤ä¡¢¸åȾ2¹Ô¤«)¤Ï¡£·ë¶É bitbuf ¤È subbitbuf ¤ò -°ì¤Ä¤Î bitbuf ¤È¤ß¤Ê¤·¤Æ n ¥Ó¥Ã¥Èº¸¤Ë¤º¤é¤·¤Æ¤¤¤ë¤³¤È¤¬¤ï¤«¤ë¡£ +fillbuf() の後半 3 行(いや、後半2行か)は。結局 bitbuf と subbitbuf を +一つの bitbuf とみなして n ビット左にずらしていることがわかる。 ---------------------------------------------------------------------------- -<¥Ó¥Ã¥È¥Ð¥Ã¥Õ¥¡Á´ÂÎ¿Þ (ͽÁÛ)> +<ビットバッファ全体図 (予想)> 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -2245,39 +2245,39 @@ fillbuf() ---------------------------------------------------------------------------- -¤³¤Î¤È¤­¡¢ÅöÁ³¿Þ¤Î x, y, z ¤ÎÉôʬ(n = 3 ¤ÏÎã¤È¤·¤Æ¤ÎÃͤÀ)¤¬¶õ¤¯»ö¤Ë¤Ê¤ë¡£ -bitcount ¤È¤¤¤¦ÊÑ¿ô¤¬ n °ú¤«¤ì¤Æ¤¤¤¿¤¬¡¢¤³¤ì¤Ï bit ¥Ð¥Ã¥Õ¥¡Á´ÂΤÎÍ­¸ú -¤Ê¥Ó¥Ã¥È¿ô¤òɽ¤·¤Æ¤¤¤ë¤Î¤Ç¤Ï¤Ê¤¤¤«¤ÈͽÁÛ¤·¤Æ¤ª¤¯¡£¤¹¤Ê¤ï¤Á¿Þ¤Î¾õÂ֤ʤé -21 ¤À¡£while ¥ë¡¼¥×¤Ï(´Ø¿ô̾¤«¤é)¤³¤Î¶õ¤­Éôʬ¤òËä¤á¤ë½èÍý¤Ê¤Î¤Ç¤Ï¤Ê¤¤ -¤«¤ÈŬÅö¤ËͽÁۤǤ­¤ë¡£¤Ç¤Ï¡¢while ¥ë¡¼¥×¤ò¸«¤è¤¦¡£¤â¤¦°ìÅÙ½é´üÃͤò³Îǧ -¤·¡¢ºÇ½é¤Ë¹Ô¤ï¤ì¤ë½èÍýÆâÍƤò¸«¤è¤¦¡£ +このとき、当然図の x, y, z の部分(n = 3 は例としての値だ)が空く事になる。 +bitcount という変数が n 引かれていたが、これは bit バッファ全体の有効 +なビット数を表しているのではないかと予想しておく。すなわち図の状態なら +21 だ。while ループは(関数名から)この空き部分を埋める処理なのではない +かと適当に予想できる。では、while ループを見よう。もう一度初期値を確認 +し、最初に行われる処理内容を見よう。 -ºÇ½é¡¢ +最初、 bitbuf = 0; subbitbuf = 0; bitcount = 0; -¤Ç¤¢¤ë¤«¤é¡¢bit¥Ð¥Ã¥Õ¥¡¤Ï¶õ¤Ã¤Ý¤À¡£ÅöÁ³ fillbuf(2 * CHAR_BIT) ¤µ¤ì¤ë¤È -while ¾ò·ï¤òËþ¤¿¤¹¡£¤­¤Ã¤È 16 bit ¤À¤± bit¥Ð¥Ã¥Õ¥¡¤¬Êä½¼¤µ¤ì¤ë¤Ï¤º(¤Ä -¤Þ¤ê¡¢bitbuf ¤¤¤Ã¤Ñ¤¤¡¢subbitbuf ¶õ)¤À¡£ +であるから、bitバッファは空っぽだ。当然 fillbuf(2 * CHAR_BIT) されると +while 条件を満たす。きっと 16 bit だけ bitバッファが補充されるはず(つ +まり、bitbuf いっぱい、subbitbuf 空)だ。 /* (A) */ while (n > bitcount) { n -= bitcount; -¤Ç¡¢¥Ó¥Ã¥È¥Ð¥Ã¥Õ¥¡¤¬ÊÝ»ý¤¹¤ë bit ¿ô°Ê¾å¤òÍ׵ᤵ¤ì¤¿¤Î¤Ç¡¢¥ë¡¼¥×¤ËÆþ¤ë¡£ -n -= bitcount ¤Ç¡¢ËÜÅö¤Ë­¤ê¤Ê¤¤Éôʬ¤¬²¿¥Ó¥Ã¥È¤Ê¤Î¤«¤òÆÀ¤Æ¤¤¤ë¡£¤³¤³¤Ç -¤Ï 16 ¤À¡£¼¡¤Î¹Ô +で、ビットバッファが保持する bit 数以上を要求されたので、ループに入る。 +n -= bitcount で、本当に足りない部分が何ビットなのかを得ている。ここで +は 16 だ。次の行 /* (B) */ bitbuf = (bitbuf << bitcount) + (subbitbuf >> (CHAR_BIT - bitcount)); -¤³¤ì¤ÏÀèÄø¤â½Ð¤ÆÍ褿½èÍý¤À¡£¥Ó¥Ã¥È¥Ð¥Ã¥Õ¥¡Á´ÂΤò bitcount ʬº¸¤Ë¤º¤é¤· -¤Æ¤¤¤ë(¤¿¤À¤·¡¢¤Þ¤À subbitbuf ¤Ï¤º¤é¤µ¤ì¤Æ¤¤¤Ê¤¤)¡£¤³¤Î»þÅÀ¤ÇͽÁÛ¤¬¾¯ -¤·Ê¤¤µ¤ì¤¿¡£8 - bitcount ¤Ç subbitbuf ¤ò¤º¤é¤·¤Æ¤¤¤ë¤«¤é bitcount ¤ÏºÇ -Âç 8 ¤ÎÃͤ·¤«»ý¤¿¤Ê¤¤¤À¤í¤¦¤È¤¤¤¦¤³¤È¤À¡£¤É¤¦¤¤¤¦¤³¤È¤«¡¢¹Í¤¨¤Æ¤ß¤ë¡¦¡¦¡¦ -¹Í¤¨¤Æ¤â¤ï¤«¤é¤Ê¤«¤Ã¤¿¤Î¤Ç¼¡¤Ë¿Ê¤â¤¦¡£ +これは先程も出て来た処理だ。ビットバッファ全体を bitcount 分左にずらし +ている(ただし、まだ subbitbuf はずらされていない)。この時点で予想が少 +し覆された。8 - bitcount で subbitbuf をずらしているから bitcount は最 +大 8 の値しか持たないだろうということだ。どういうことか、考えてみる・・・ +考えてもわからなかったので次に進もう。 /* (C) */ if (compsize != 0) { @@ -2288,12 +2288,12 @@ n -= bitcount subbitbuf = 0; bitcount = CHAR_BIT; -compsize ¤È¤¤¤¦¤Î¤¬½Ð¤ÆÍ褿¤¬¡¢¤³¤ÎÃͤ¬¤É¤¦¤¢¤í¤¦¤È¤â subbitbuf ¤Ï8 ¥Ó¥Ã -¥ÈËä¤á¤é¤ì¡£bitcount ¤Ï 8 ¤ËÀßÄꤵ¤ì¤Æ¤¤¤ë¡£¤ï¤«¤Ã¤¿ bitcount ¤Ï¡¢ -subbitbuf ¤ËÊÝ»ý¤¹¤ë bit ¿ô¤À¡£¿Þ¤òÄûÀµ¤·¤è¤¦¡£ +compsize というのが出て来たが、この値がどうあろうとも subbitbuf は8 ビッ +ト埋められ。bitcount は 8 に設定されている。わかった bitcount は、 +subbitbuf に保持する bit 数だ。図を訂正しよう。 ---------------------------------------------------------------------------- -<¥Ó¥Ã¥È¥Ð¥Ã¥Õ¥¡Á´ÂοÞ> +<ビットバッファ全体図> 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -2306,43 +2306,43 @@ subbitbuf ---------------------------------------------------------------------------- -¤³¤Î¿Þ¤òƧ¤Þ¤¨¤Æ¤â¤¦°ìÅÙ½é´ü¾õÂ֤ǤνèÍýÆâÍƤòÄɤ¤¤«¤±¤ë¡£ +この図を踏まえてもう一度初期状態での処理内容を追いかける。 -¤Þ¤º¡¢(A) ¤Ç¡¢subbitbuf ¤Ï¶õ¤Ê¤Î¤Ç¡¢bitcount ¤Ï 0 ¤À¡£Í׵ᤷ¤¿ bit ¿ô -n{16} ¤è¤ê¾®¤µ¤¤¤Î¤Ç¥ë¡¼¥×¤ËÆþ¤ë¡£n ¤Ï 16 ¤Î¤Þ¤Þ¤À¡£ +まず、(A) で、subbitbuf は空なので、bitcount は 0 だ。要求した bit 数 +n{16} より小さいのでループに入る。n は 16 のままだ。 -(B) ¤Ç¡¢subbitbuf ¤Ë»Ä¤Ã¤Æ¤¤¤ë bit ¤ò bitbuf ¤Ë¤º¤é¤·¤Æ¤¤¤ë¡£º£¤Ï¤Þ¤À -¶õ¤Ê¤Î¤Çbitbuf ¤Ï¤³¤³¤Ç¤â¤Þ¤À¶õ¤À¡£ +(B) で、subbitbuf に残っている bit を bitbuf にずらしている。今はまだ +空なのでbitbuf はここでもまだ空だ。 -(C) ¤Ç¡¢¥Õ¥¡¥¤¥ë¤«¤é¥Ç¡¼¥¿¤ò8 ¥Ó¥Ã¥ÈÆɤà(compsize ¤Ï¾ï¤Ë0¤Ç¤Ï¤Ê¤¤¤È¹Í¤¨ -¤ë)¡£bitcount ¤Ï 8 ¤Ë¤Ê¤ë¡£¤³¤Î»þÅÀ¤Ç bit¥Ð¥Ã¥Õ¥¡Á´ÂÎ¤Ï subbitbuf ¤À¤± -Ãͤ¬Æþ¤Ã¤¿¾õÂ֤ˤʤ롣 +(C) で、ファイルからデータを8 ビット読む(compsize は常に0ではないと考え +る)。bitcount は 8 になる。この時点で bitバッファ全体は subbitbuf だけ +値が入った状態になる。 -¼¡¤Î¥ë¡¼¥×¤Ë°Ü¤í¤¦¡£(A) ¤Ç¡¢subbitbuf ¤Ï¤¤¤Ã¤Ñ¤¤¤Ç¤¢¤ë¤¬Í׵ᤷ¤¿ n{16} -¤è¤ê¤Ï¾®¤µ¤¤¤Î¤Ç¡¢¤Þ¤À¥ë¡¼¥×¤Ï³¤¯¡£n ¤Ï¤³¤³¤Ç 8 ¤Ë¤Ê¤ë¡£ +次のループに移ろう。(A) で、subbitbuf はいっぱいであるが要求した n{16} +よりは小さいので、まだループは続く。n はここで 8 になる。 -(B) ¤Ç¡¢subbitbuf ¤Ë»Ä¤Ã¤Æ¤¤¤ë bit (8 bit ¤À)¤ò bitbuf ¤Ë¤º¤é¤·¤Æ¤¤¤ë¡£ -º£ÅÙ¤Ï subbitbuf Á´ÂΤ¬ bitbuf ¤Ë°Ü¤Ã¤Æ¤¤¤ë¤Î¤ÈƱ¤¸¤À¡£(¤Ä¤Þ¤ê¡¢bitbuf +(B) で、subbitbuf に残っている bit (8 bit だ)を bitbuf にずらしている。 +今度は subbitbuf 全体が bitbuf に移っているのと同じだ。(つまり、bitbuf = subbitbuf) -(C) ¤Ç¡¢¤Þ¤¿ subbitbuf ¤Ï 8 bit Êä½¼¤µ¤ì¤ë¡£ +(C) で、また subbitbuf は 8 bit 補充される。 -(A) ¤Ç¡¢n{8} > bitcount{8} ¤Ïµ¶¤Ê¤Î¤Ç¥ë¡¼¥×¤¬½ª¤ë¡£ +(A) で、n{8} > bitcount{8} は偽なのでループが終る。 -(D) ¤Ç¡¢subbitbuf ¤Ë»Ä¤Ã¤Æ¤¤¤ë bit ¤Ï¤¹¤Ù¤Æ bitbuf ¤Ë°Ü¤ë¡£bitbuf ¤Ï 16 -bit ¤¤¤Ã¤Ñ¤¤¤Ë¤Ê¤ë¡£bitcount ¤Ï 0 ¤À¡£ +(D) で、subbitbuf に残っている bit はすべて bitbuf に移る。bitbuf は 16 +bit いっぱいになる。bitcount は 0 だ。 -¤³¤Î½èÍý·ë²Ì¤«¤é fillbuf(n) ¤Ï¡¢bitbuf ¤Ë n ¥Ó¥Ã¥ÈÆɤ߹þ¤à½èÍý¤À¤È¸À¤¨ -¤ë¡£°ú¿ô¤Ë»ØÄê¤Ç¤­¤ë n ¤¬ºÇÂç 16 ¥Ó¥Ã¥È¤Ç¤¢¤ë¤³¤È¤Ë¤âµ¤¤Å¤¤¤ÆÎɤ¤¤À¤í -¤¦¡£½èÍýÆâÍƤò³Îǧ¤·¤Æ¤ß¤ì¤Ð¤ï¤«¤ë¡£ +この処理結果から fillbuf(n) は、bitbuf に n ビット読み込む処理だと言え +る。引数に指定できる n が最大 16 ビットであることにも気づいて良いだろ +う。処理内容を確認してみればわかる。 -¤³¤³¤Ç¡¢subbitbuf ¤ÎÍÑÅӤ˵¤¤Å¤¤¤¿¡£¥Õ¥¡¥¤¥ë¤«¤é¤ÎÆɤ߹þ¤ß¤¬ 8 ¥Ó¥Ã¥È -ñ°Ì¤Ç¤·¤«¤Ç¤­¤Ê¤¤¤Î¤Ç¡¢¤½¤ì¤òÊ䤦¤¿¤á¤ÎÊݸÍѥХåե¡¤Ç¤¢¤í¤¦¡£Î㤨¤Ð -1 ¥Ó¥Ã¥È¤À¤± bitbuf ¤ò fill ¤·¤¿¤±¤ì¤Ð subbitbuf ¤Ë 7 bit »Ä¤·¡¢1 bit -¤À¤± bitbuf ¤ËÀßÄꤵ¤ì¤ë(³Îǧ¤·¤Æ¤ß¤ì¤Ð¤ï¤«¤ë) +ここで、subbitbuf の用途に気づいた。ファイルからの読み込みが 8 ビット +単位でしかできないので、それを補うための保存用バッファであろう。例えば +1 ビットだけ bitbuf を fill したければ subbitbuf に 7 bit 残し、1 bit +だけ bitbuf に設定される(確認してみればわかる) -fillbuf() ¤¬¤ï¤«¤Ã¤¿¤Î¤Ç¡¢¤½¤ì¤òÍøÍѤ·¤Æ¤¤¤ë getbits() ¤ÎÆâÍƤò³Îǧ¤· -¤è¤¦¡£ +fillbuf() がわかったので、それを利用している getbits() の内容を確認し +よう。 unsigned short getbits(n) @@ -2357,40 +2357,40 @@ getbits(n) x = bitbuf >> (2 * CHAR_BIT - n); -¤Ï¡¢3 ÅÙ¤â½Ð¤ÆÍ褿¤Î¤Ç +は、3 度も出て来たので buf >> (sizeof(buf)*8 - n) -¤ò buf ¤Î¾å°Ì n ¥Ó¥Ã¥È¤òÆÀ¤ë¼°¤À¤È¤·¤Æ¥Þ¥¯¥í¤Ë¤·¤Æ¤âÎɤ¤¤À¤í¤¦¡£(¤¬¡¢ -Îɤ¤Ì¾Á°¤¬»×¤¤ÉÕ¤«¤Ê¤¤¤Î¤Ç¤½¤¦¤Ï¤·¤Ê¤¤)¡£¤È¤Ë¤«¤¯¡¢bitbuf ¤Î¾å°Ì n ¥Ó¥Ã¥È -¤ò²¼°Ì n ¥Ó¥Ã¥È¤È¤·¤Æ x ¤ËÂåÆþ¤·¤Æ¤¤¤ë¡£¤½¤Î¸å¤Ç¡¢ +を buf の上位 n ビットを得る式だとしてマクロにしても良いだろう。(が、 +良い名前が思い付かないのでそうはしない)。とにかく、bitbuf の上位 n ビット +を下位 n ビットとして x に代入している。その後で、 fillbuf(n); -¤·¤Æ¤¤¤ë¡£n bit ¤ò x ¤ËÅϤ·¤¿¤Î¤Ç bitbuf ¤«¤é¾å°Ì n ¥Ó¥Ã¥È¤ò¼Î¤Æ¤Æ¡¢n -¥Ó¥Ã¥ÈÊä½¼¤¹¤ë¡£¤³¤³¤Ç¡¢bitbuf ¤Ï¾ï¤Ë¤¤¤Ã¤Ñ¤¤¤Î¾õÂ֤ˤʤäƤ¤¤ë¤³¤È¤¬ -¤ï¤«¤ë¡£(¥Õ¥¡¥¤¥ë¤ÎËöÈøÉÕ¶á¤Î¾ì¹ç¡¢Àµ³Î¤Ë bitbuf ¤Ë²¿¥Ó¥Ã¥È»Ä¤Ã¤Æ¤¤¤ë -¤«¤¬È½ÃǤǤ­¤Ê¤¤¤¬¡¢¼ïÌÀ¤«¤·¤ò¤¹¤ë¤È¤³¤Î¤³¤È¤Ï LHa ¤Î½èÍýÆâÍÆ¤Ë¤È¤Ã¤Æ -¤Ï¤É¤¦¤Ç¤â¤¤¤¤¤³¤È¤À¡£getbits() ¤Ï decode ½èÍý¤Ç»È¤ï¤ì¤ë¤Î¤À¤¬¡¢decode -½èÍý¤Ï²¿¥Ó¥Ã¥È¤Î¾ðÊó¤ò decode ¤¹¤ëɬÍפ¬¤¢¤ë¤«¤ò¾¤Î¾ðÊ󤫤餢¤é¤«¤¸¤á -ÆÀ¤Æ¤¤¤ë) +している。n bit を x に渡したので bitbuf から上位 n ビットを捨てて、n +ビット補充する。ここで、bitbuf は常にいっぱいの状態になっていることが +わかる。(ファイルの末尾付近の場合、正確に bitbuf に何ビット残っている +かが判断できないが、種明かしをするとこのことは LHa の処理内容にとって +はどうでもいいことだ。getbits() は decode 処理で使われるのだが、decode +処理は何ビットの情報を decode する必要があるかを他の情報からあらかじめ +得ている) -¼¡¤Ë°Ü¤í¤¦º£ÅÙ¤Ï putcode() ¤À¡£put ¤Î¾ì¹ç¤Þ¤º¤Ï¡¢init_putbits() ¤Ç -½é´ü²½¤¬¹Ô¤ï¤ì¤Æ¤¤¤ë¡£¤½¤ÎÃͤϰʲ¼¤À¡£ +次に移ろう今度は putcode() だ。put の場合まずは、init_putbits() で +初期化が行われている。その値は以下だ。 bitcount = CHAR_BIT; subbitbuf = 0; getc_euc_cache = EOF; -getc_euc_cache ¤Ï̵»ë¤À¡£bitcount ¤È subbitbuf ¤ÎÃͤ¬ÀßÄꤵ¤ì¡¢bitbuf -¤ÏÍøÍѤµ¤ì¤Ê¤¤¡£ÀèÄø¤È¤Ï°ã¤¤ subbitbuf ¤¬¶õ¤Ê¤Î¤Ëbitcount ¤¬ 8 ¤Ê¤Î¤Ç¡¢ -bitcount ¤Î»È¤ï¤ìÊý¤¬Â¿¾¯°Û¤Ê¤ë¤è¤¦¤À¡£get ¤Î¾ì¹ç¤Ï¡¢bitcount ¤Ï¡¢ -subbitbuf ¤ËÊÝ»ý¤¹¤ë bit ¿ô¤À¤Ã¤¿¤¬º£ÅÙ¤Ï subbitbuf ¤Î¶õ¤­ bit ¿ô¤À¤í -¤¦¤ÈͽÁÛ¤·¤Æ¤ª¤³¤¦¡£ +getc_euc_cache は無視だ。bitcount と subbitbuf の値が設定され、bitbuf +は利用されない。先程とは違い subbitbuf が空なのにbitcount が 8 なので、 +bitcount の使われ方が多少異なるようだ。get の場合は、bitcount は、 +subbitbuf に保持する bit 数だったが今度は subbitbuf の空き bit 数だろ +うと予想しておこう。 -¤½¤·¤Æ¡¢putcode(n, x) ¤ò¸«¤ë¡£¼Â¤Ï¥½¡¼¥¹¤ò¸«¤ë¤È¤ï¤«¤ë¤Î¤À¤¬¡¢¤â¤¦°ì¤Ä -¤Î½ÐÎϥ롼¥Á¥ó putbits() ¤Ï¡¢putcode() ¤Î¸Æ¤Ó½Ð¤·¤Ë½ñ¤­´¹¤¨²Äǽ¤À¡£ -putbits() ¤Ï¡¢ +そして、putcode(n, x) を見る。実はソースを見るとわかるのだが、もう一つ +の出力ルーチン putbits() は、putcode() の呼び出しに書き換え可能だ。 +putbits() は、 void putbits(n, x) /* Write rightmost n bits of x */ @@ -2401,7 +2401,7 @@ putbits(n, x) /* Write rightmost n bits of x */ putcode(n, x); } -¤Ã¤È½ñ¤­´¹¤¨¤é¤ì¤ë¤Î¤À¡£¤Ê¤Î¤Ç¡¢putcode() ¤ÎÆâÍƤòÀè¤Ë³Îǧ¤¹¤ë¤ï¤±¤À¡£ +っと書き換えられるのだ。なので、putcode() の内容を先に確認するわけだ。 void putcode(n, x) /* Write rightmost n bits of x */ @@ -2433,42 +2433,42 @@ putcode(n, x) /* Write rightmost n bits of x */ bitcount -= n; } -½èÍýÆâÍƤ¬ fillbuf() ¤Î¤È¤­¤È»÷¤Æ¤¤¤ë¡£¤Þ¤º¤Ï¡¢ÀèÄø¤ÈƱÍÍ¤Ë while ¾ò·ï -¤ò̵»ë¤·¤Æ¹Í¤¨¤Æ¤ß¤ë¡£(D) ¤À¡£ +処理内容が fillbuf() のときと似ている。まずは、先程と同様に while 条件 +を無視して考えてみる。(D) だ。 /* (D) */ subbitbuf += x >> (USHRT_BIT - bitcount); bitcount -= n; -¤³¤Î¼°¤Ï¤â¤¦ 4 ÅÙÌܤÀ¡£¤Þ¤º¡¢x ¤Î¾å°Ì bitcount ¥Ó¥Ã¥È¤òÆÀ¤Æ¡¢subbitbuf -¤Ë­¤·¤Æ¤¤¤ë¡£bitcount ¤Ï¡¢ÀèÄø subbitbuf ¤Î¶õ¤­¤Ç¤¢¤í¤¦¤ÈͽÁÛ¤·¤¿¤¬¡¢ -n °ú¤«¤ì¤Æ¤¤¤ë¤Î¤Ç¡¢Ëä¤á¤¿Ê¬¶õ¤­¤¬¸º¤Ã¤Æ¤¤¤ë¤ï¤±¤À¡£Í½ÁÛ¤ÏÅö¤¿¤Ã¤Æ¤¤¤ë -¤À¤í¤¦¡£¤³¤Î»þÅÀ¤Ç¤³¤Î´Ø¿ô¤¬ x ¤Î¾å°Ì¥Ó¥Ã¥È¤òÍøÍѤ¹¤ë¤³¤È¤¬¤ï¤«¤ë¡£¥³ -¥á¥ó¥È¤Ë rightmost n bits of x ¤È½ñ¤«¤ì¤Æ¤¤¤ë¤¬ÏǤ蘆¤ì¤Æ¤Ï¤¤¤±¤Ê¤¤¡£ -¿¤¯¤Î¾ì¹ç¡¢¥³¥á¥ó¥È¤Ï¤»¤¤¤¼¤¤¥Ò¥ó¥È¤È¤·¤Æ¤Î¾ðÊó¤Ç¤·¤«¤Ê¤¤¡£¿®ÍѤ·¤Æ¤Ï -¤¤¤±¤Ê¤¤¤â¤Î¤Ê¤Î¤À¡£(¥³¥á¥ó¥È¤Ï¤¢¤Þ¤ê¥Ç¥Ð¥Ã¥°¤µ¤ì¤Ê¤¤¡£¥³¥á¥ó¥È¤¬¾Ü¤· -¤±¤ì¤Ð¾Ü¤·¤¤Äø¥³¥á¥ó¥È¤Ï¥¨¥ó¥Ð¥°¤·¤ä¤¹¤¤¡£µ¿¤Ã¤Æ¤«¤«¤í¤¦¡£¤³¤ì¤ÏËܽñ¤Ë -¤â¸À¤¨¤ë¡£¤¹¤Ù¤Æ¤ò±­¤Î¤ß¤Ë¤·¤Æ¤Ï¤¤¤±¤Ê¤¤¤Î¤À) +この式はもう 4 度目だ。まず、x の上位 bitcount ビットを得て、subbitbuf +に足している。bitcount は、先程 subbitbuf の空きであろうと予想したが、 +n 引かれているので、埋めた分空きが減っているわけだ。予想は当たっている +だろう。この時点でこの関数が x の上位ビットを利用することがわかる。コ +メントに rightmost n bits of x と書かれているが惑わされてはいけない。 +多くの場合、コメントはせいぜいヒントとしての情報でしかない。信用しては +いけないものなのだ。(コメントはあまりデバッグされない。コメントが詳し +ければ詳しい程コメントはエンバグしやすい。疑ってかかろう。これは本書に +も言える。すべてを鵜のみにしてはいけないのだ) -¤Ç¤Ï¡¢½èÍýÆâÍƤ˰ܤ롣¤Þ¤º¤Ï (A) +では、処理内容に移る。まずは (A) /* (A) */ while (n >= bitcount) { n -= bitcount; -subbitbuf ¤Î¶õ¤­¤¬ n °Ê²¼¤Ç¤¢¤ì¤Ð¥ë¡¼¥×¤ËÆþ¤ë¡£subbitbuf °ì¤Ä¤Ç¤Ïn ¥Ó¥Ã -¥ÈÁ´Éô¤ÏÏŤ¨¤Ê¤¤¤«¤é¥ë¡¼¥×¤Ç¾®¹ï¤ß¤Ë½èÍý¤·¤è¤¦¤È¤¤¤¦¤³¤È¤À¤í¤¦(¤â¤¦Á´ -ÂΤνèÍýÆâÍƤÎͽÁۤϤĤ¤¤Æ¤¤¤ë) -n ¤«¤é bitcount °ú¤¤¤Æ¤¤¤ë¤Î¤Ç¡¢n ¥Ó¥Ã¥È¤Î¤¦¤Á¤³¤ì¤«¤é bitcount ʬ¤Ï -½èÍý¤µ¤ì¤ë¤³¤È¤ò¤³¤³¤Ç¤µ¤Ã¤µ¤Èµ­Ï¿¤·¤Æ¼¡¤Î¥ë¡¼¥×¤ËÈ÷¤¨¤Æ¤¤¤ë¡£ +subbitbuf の空きが n 以下であればループに入る。subbitbuf 一つではn ビッ +ト全部は賄えないからループで小刻みに処理しようということだろう(もう全 +体の処理内容の予想はついている) +n から bitcount 引いているので、n ビットのうちこれから bitcount 分は +処理されることをここでさっさと記録して次のループに備えている。 /* (B) */ subbitbuf += x >> (USHRT_BIT - bitcount); x <<= bitcount; -x ¤Î¾å°Ì bitcount ¥Ó¥Ã¥È¤ò subbitbuf ¤Ë­¤·¤Æ¤¤¤ë¡£subbitbuf ¤Î¶õ¤­¤¬ -¤³¤ì¤ÇËä¤Þ¤Ã¤¿¡£subbitbuf ¤Ï¤â¤¦¤¤¤Ã¤Ñ¤¤¤À¡£x ¤ò bitcount ¥·¥Õ¥È¤¹¤ë¤³ -¤È¤Ç subbitbuf ¤ËÅϤ·¤¿ x ¤Î¾å°Ì¥Ó¥Ã¥È¤ò¼Î¤Æ¤Æ¤¤¤ë¡£ +x の上位 bitcount ビットを subbitbuf に足している。subbitbuf の空きが +これで埋まった。subbitbuf はもういっぱいだ。x を bitcount シフトするこ +とで subbitbuf に渡した x の上位ビットを捨てている。 /* (C) */ if (compsize < origsize) { @@ -2484,32 +2484,32 @@ x subbitbuf = 0; bitcount = CHAR_BIT; -compsize ¤Ï̵»ë¤·¤Æ¤âÎɤ¤¡£½èÍý¤ÎËܼÁ¤Ç¤Ï¤Ê¤¤¤«¤é¤À¤¬¡¢¤¹¤°¤Ë¤ï¤«¤ë¤Î¤Ç -°ì±þÀâÌÀ¤¹¤ë¤È¡¢ +compsize は無視しても良い。処理の本質ではないからだが、すぐにわかるので +一応説明すると、 if (compsize < origsize) { ... else unpackable = 1; -¤Ç¡¢°µ½Ì¥Õ¥¡¥¤¥ë¥µ¥¤¥º¤¬¸µ¤Î¥Õ¥¡¥¤¥ë¥µ¥¤¥º¤ò¾å²ó¤Ã¤¿¤È¤­¤Ë -½èÍý¤ò½ª¤ë¤è¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤ë(unpackable = 1 ¤·¤Æ¡¢Â¾¤Î²Õ½ê¤Ç¤³¤ÎÊÑ¿ô¤ò´Æ»ë¤¹¤ë¡£ -unpackable == 1 ¤Ê¤é½èÍý¤òÃæÃǤ¹¤ë) +で、圧縮ファイルサイズが元のファイルサイズを上回ったときに +処理を終るようになっている(unpackable = 1 して、他の箇所でこの変数を監視する。 +unpackable == 1 なら処理を中断する) -¤È¤Ë¤«¤¯ (C) ¤Î»þÅÀ¤Ç¤Ïɬ¤º subbitbuf ¤¬¤¤¤Ã¤Ñ¤¤¤Ë¤Ê¤ë¤Î¤Ç 1 ¥Ð¥¤¥È¤ò -¥Õ¥¡¥¤¥ë¤Ë½ñ¤­½Ð¤·¤Æ¤¤¤ë¡£¤½¤Î¸å¡¢subbitbuf = 0, bitcount = 8 ¤È¤·¤Æ -subbitbuf ¤ò¶õ¤±¤Æ¼¡¤Î¥ë¡¼¥×¤ËÈ÷¤¨¤Æ¤¤¤ë¡£ +とにかく (C) の時点では必ず subbitbuf がいっぱいになるので 1 バイトを +ファイルに書き出している。その後、subbitbuf = 0, bitcount = 8 として +subbitbuf を空けて次のループに備えている。 -¤â¤¦¤¤¤¤¤À¤í¤¦¡£putcode() ¤Ï¡¢ÏÀÍýŪ¤Ë¤Ï x ¤Î¤¦¤Á¾å°Ì n ¥Ó¥Ã¥È¤ò½ÐÎϤ¹ -¤ë½èÍý¤À¡£°ú¿ô n ¤Î¾å¸Â¤¬ x ¤ÎºÇÂç¥Ó¥Ã¥È¥µ¥¤¥º 16 ¤Ë¤Ê¤ë¤Î¤ÏÀâÌÀ¤¹¤ë¤Þ -¤Ç¤â¤Ê¤¤¤À¤í¤¦¡£ +もういいだろう。putcode() は、論理的には x のうち上位 n ビットを出力す +る処理だ。引数 n の上限が x の最大ビットサイズ 16 になるのは説明するま +でもないだろう。 -putcode() ¤Ï¼ÂÁõ¤È¤·¤Æ¡¢subbitbuf ¤È x ¤ò°ì¤Ä¤Ë·Ò¤²¤Æ n bit º¸¤Ë¤º¤é¤· -¤Æ¤¤¤ë½èÍý¤À¤È¹Í¤¨¤Æ¤âÎɤ¤¡£¤½¤¦¤·¤Æ¡¢subbitbuf ¤¬¤¤¤Ã¤Ñ¤¤¤Ë¤Ê¤Ã¤¿¤é¤½ -¤ÎÅÔÅÙ(1 ¥Ð¥¤¥È¤º¤Ä)¥Õ¥¡¥¤¥ë¤Ë½ñ¤­½Ð¤¹¤Î¤À¡£ +putcode() は実装として、subbitbuf と x を一つに繋げて n bit 左にずらし +ている処理だと考えても良い。そうして、subbitbuf がいっぱいになったらそ +の都度(1 バイトずつ)ファイルに書き出すのだ。 ---------------------------------------------------------------------------- -<¥Ó¥Ã¥È¥Ð¥Ã¥Õ¥¡Á´ÂοÞ> +<ビットバッファ全体図> - <--- º¸¤Ë¤º¤é¤¹ + <--- 左にずらす 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -2522,136 +2522,136 @@ putcode() ---------------------------------------------------------------------------- -putbits() ¤â¸«¤è¤¦¡£ÀèÄø putcode() ¤Î¸Æ¤Ó½Ð¤·¤Ë½ñ¤­´¹¤¨¤¿¥³¡¼¥É¤ò¸«¤ë¤È -¤¹¤°¤ï¤«¤ë¤¬¡¢ +putbits() も見よう。先程 putcode() の呼び出しに書き換えたコードを見ると +すぐわかるが、 x <<= USHRT_BIT - n; putcode(n, x); -ºÇ½é¤Î¼°¤Ç¡¢x ¤Î²¼°Ì n ¥Ó¥Ã¥È¤ò x ¤Î¾å°Ì n ¥Ó¥Ã¥È¤Ë¤·¤Æ¤¤¤ë¡£ -¤½¤¦¤·¤Æ¡¢putcode() ¤ò¸Æ¤Ó½Ð¤·¤Æ¤¤¤ë¤Î¤Ç¡¢putbits(n, x) ¤Ï¡¢x -¤Î²¼°Ì n ¥Ó¥Ã¥È¤ò½ÐÎϤ¹¤ë½èÍý¤À¡£ +最初の式で、x の下位 n ビットを x の上位 n ビットにしている。 +そうして、putcode() を呼び出しているので、putbits(n, x) は、x +の下位 n ビットを出力する処理だ。 -°Ê¾å¤Ç¥Ó¥Ã¥ÈÆþ½ÐÎϥ롼¥Á¥ó¤Ï½ª¤ê¤À¡£½ÐÎϤ˴ؤ·¤Æ°ì¤ÄÊ᪤·¤Æ¤ª¤¯¤È -putcode(), putbits() ¤Ç¤ÏºÇ¸å¤ÎºÇ¸å¤Ç subbitbuf ¤Ë¾ðÊ󤬻Ĥ俤ޤޥե¡ -¥¤¥ë¤Ë½ñ¤­½Ð¤µ¤ì¤Ê¤¤¾õÂ֤ˤʤ롣¤³¤ì¤òÅǤ­½Ð¤¹¤¿¤á¤ËÍøÍÑ¼Ô¤Ï +以上でビット入出力ルーチンは終りだ。出力に関して一つ捕捉しておくと +putcode(), putbits() では最後の最後で subbitbuf に情報が残ったままファ +イルに書き出されない状態になる。これを吐き出すために利用者は putcode(7, 0) -¤ò¹Ô¤¦É¬Íפ¬¤¢¤ë¡£ +を行う必要がある。 -¤Þ¤È¤á¤è¤¦ +まとめよう ---------------------------------------------------------------------------- fillbuf(n) - bitbuf ¤«¤é¾å°Ì n ¥Ó¥Ã¥È¤ò¼Î¤Æ¤Æ¡¢²¼°Ì n ¥Ó¥Ã¥È¤ò¥Õ¥¡¥¤¥ë¤«¤éÆɤ߹þ - ¤ßËä¤á¤ë¡£ + bitbuf から上位 n ビットを捨てて、下位 n ビットをファイルから読み込 + み埋める。 getbits(n) - bitbuf ¤Î¾å°Ì n ¥Ó¥Ã¥È¤ò²¼°Ì n ¥Ó¥Ã¥È¤È¤·¤ÆÊÖ¤¹¡£bitbuf ¤Ï n ¥Ó¥Ã¥È - Êä½¼¤µ¤ì¤ë¡£ + bitbuf の上位 n ビットを下位 n ビットとして返す。bitbuf は n ビット + 補充される。 putcode(n, x) - x ¤Î¾å°Ì n ¥Ó¥Ã¥È¤ò¥Õ¥¡¥¤¥ë¤Ë½ÐÎϤ¹¤ë¡£ºÇ¸å¤Î½ÐÎÏ»þ¤Ï putcode(7, 0) - ¤¹¤ëɬÍפ¬¤¢¤ë¡£ + x の上位 n ビットをファイルに出力する。最後の出力時は putcode(7, 0) + する必要がある。 putbits(n, x) - x ¤Î²¼°Ì n ¥Ó¥Ã¥È¤ò¥Õ¥¡¥¤¥ë¤Ë½ÐÎϤ¹¤ë¡£ºÇ¸å¤Î½ÐÎÏ»þ¤Ï putcode(7, 0) - ¤¹¤ëɬÍפ¬¤¢¤ë¡£ + x の下位 n ビットをファイルに出力する。最後の出力時は putcode(7, 0) + する必要がある。 init_getbits() - ÆþÎϽèÍý¤Î½é´ü²½ + 入力処理の初期化 init_putbits() - ½ÐÎϽèÍý¤Î½é´ü²½ + 出力処理の初期化 ---------------------------------------------------------------------------- -Æɤ߹þ¤ß¤Ë´Ø¤·¤Æ¡¢bitbuf ¤Î¥µ¥¤¥º¤¬ 16 ¥Ó¥Ã¥È¤Ç¾ï¤Ë¤½¤Î¾õÂÖ¤¬ÊÝ»ý¤µ¤ì -¤Æ¤¤¤ë¤Î¤Ï LHa ¤Ë¤È¤Ã¤Æ½ÅÍפʻö¤À¡£decode ½èÍý¤Ç¤ÏľÀÜ bitbuf ¤ò»²¾È¤¹ -¤ë²Õ½ê¤¬¤¢¤ë¡£ +読み込みに関して、bitbuf のサイズが 16 ビットで常にその状態が保持され +ているのは LHa にとって重要な事だ。decode 処理では直接 bitbuf を参照す +る箇所がある。 -Huffman Ë¡ (huf.c) +Huffman 法 (huf.c) ------------------ -LHa for UNIX ¤Ç¤Ï¡¢ÀÅŪ Huffman Ë¡¤È¤·¤Æ¡¢shuf.c¡¢Æ°Åª Huffman Ë¡¤È¤· -¤Æ dhuf.c ¤¬¤¢¤ë¤é¤·¤¤¤¬¡¢¤³¤ì¤é¤Ë´Ø¤·¤Æ¤Ï¿¨¤ì¤Ê¤¤¡£LHa ¤Ç¤Ï¡¢¤³¤ì¤é¤Ï -²áµî¤ÎÈǤΥ¢¡¼¥«¥¤¥Ö¤ò²òÅà¤Ç¤­¤ë¤è¤¦¤Ë decode ¤Î¤ß¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤ë¤è¤¦ -¤À¡£¤½¤³¤Ç¡¢¤Þ¤º¤Ï -lh4-, -lh5-, -lh6-, -lh7- ¤ÇÍøÍѤµ¤ì¤Æ¤¤¤ë huf.c -¤Î²òÀϤòÃæ¿´¤Ë¹Ô¤¦¤³¤È¤È¤·¤¿¤¤¡£ +LHa for UNIX では、静的 Huffman 法として、shuf.c、動的 Huffman 法とし +て dhuf.c があるらしいが、これらに関しては触れない。LHa では、これらは +過去の版のアーカイブを解凍できるように decode のみサポートしているよう +だ。そこで、まずは -lh4-, -lh5-, -lh6-, -lh7- で利用されている huf.c +の解析を中心に行うこととしたい。 -¤È¤³¤í¤Ç¡¢Ëܽñ¤Ç¤Ï Huffman Ë¡¤¬¤É¤¦¤¤¤Ã¤¿¤â¤Î¤«¤ÏͽÈ÷Ãμ±¤È¤·¤Æ´û¤ËÃΤà -¤Æ¤¤¤ë¤â¤Î¤È¤¹¤ë¤¬¡¢¤¤¤Á¤ª¤¦³µÍפò´Êñ¤ËÀâÌÀ¤·¤Æ¤ª¤³¤¦¡£ +ところで、本書では Huffman 法がどういったものかは予備知識として既に知っ +ているものとするが、いちおう概要を簡単に説明しておこう。 -°Ê²¼¤ÎÆâÍƤΥƥ­¥¹¥È¥Õ¥¡¥¤¥ë¤¬¤¢¤Ã¤¿¤È¤¹¤ë¡£ +以下の内容のテキストファイルがあったとする。 abcabcaba -¤³¤Î¥Æ¥­¥¹¥È¤Ï 9 ¥Ð¥¤¥È¤¢¤ë¤ï¤±¤À¤¬¡¢¤³¤Î¥Õ¥¡¥¤¥ë¤Ç»È¤ï¤ì¤Æ¤¤¤ëʸ»ú¤Ï3 -¼ïÎष¤«¤Ê¤¤¡¢a, b, c ¤À¡£¤À¤«¤é¤³¤Î¥Õ¥¡¥¤¥ë¤À¤±¤Ë´Ø¤·¤Æ¸À¤¨¤Ð 1 ʸ»ú -¤Ï 2 ¥Ó¥Ã¥È¤¢¤ì¤Ðɽ¸½²Äǽ¤Ç¤¢¤ë¡£Î㤨¤Ð³Æʸ»ú¤ËÂФ·¤Æ°Ê²¼¤Î¥Ó¥Ã¥È¤ò -³ä¤êÅö¤Æ¤¿¤È¤¹¤ë¤È +このテキストは 9 バイトあるわけだが、このファイルで使われている文字は3 +種類しかない、a, b, c だ。だからこのファイルだけに関して言えば 1 文字 +は 2 ビットあれば表現可能である。例えば各文字に対して以下のビットを +割り当てたとすると - ʸ»ú ¥Ó¥Ã¥Èɽ¸½ + 文字 ビット表現 a 00 b 01 c 10 -Àè¤Î¥Æ¥­¥¹¥È¥Õ¥¡¥¤¥ë¤ÎÆâÍÆ abcabcaba ¤Ï¡¢18¥Ó¥Ã¥È¤¢¤ì¤Ðɽ¸½²Äǽ¤È¤Ê¤ë¡£ +先のテキストファイルの内容 abcabcaba は、18ビットあれば表現可能となる。 -¤µ¤é¤Ë¡¢½Ð¸½ÉÑÅ٤ι⤤ʸ»ú¤ò¾¯¤Ê¤¤¥Ó¥Ã¥È¿ô¤Çɽ¸½¤·¡¢¤Þ¤ì¤Ë¤·¤«¸½¤ì¤Ê¤¤ -ʸ»ú¤òŤ¤¥Ó¥Ã¥È¿ô¤Çɽ¤¹¤è¤¦¤Ë¤¹¤ì¤Ð¤è¤ê¥Ó¥Ã¥È¿ô¤ò¾¯¤Ê¤¯¤Ç¤­¤ë¡£Î㤨¤Ð +さらに、出現頻度の高い文字を少ないビット数で表現し、まれにしか現れない +文字を長いビット数で表すようにすればよりビット数を少なくできる。例えば - ʸ»ú ¥Ó¥Ã¥Èɽ¸½ + 文字 ビット表現 a 0 b 10 c 11 -¤Ç¤¢¤ë¤È¤¹¤ë¤È a ¤Ï 4²ó¡¢b¤Ï3²ó¡¢c¤Ï2²ó¸½¤ì¤ë¤Î¤Ç¡¢Á´ÂÎ¤Ï 4 + 2*3 + -2*2 = 14 ¥Ó¥Ã¥È¤Çɽ¸½¤Ç¤­¤ë¤³¤È¤Ë¤Ê¤ë¡£¤³¤ì¤¬ Huffman Ë¡¤Î°µ½Ì¸¶Íý¤Ç¤¢ -¤ë¡£¤³¤Î¤è¤¦¤Ë Huffman Ë¡¤Ç¤Ïʸ»ú¤ò¥Ó¥Ã¥Èñ°Ì¤Ç°·¤¦¤¿¤á¥Ó¥Ã¥ÈÆþ½ÐÎϥ롼 -¥Á¥ó¤òÀè¤Ë²òÆɤ·¤¿¤ï¤±¤À¡£¤Þ¤¿¡¢Éä¹æ²½¤ÎºÝ¤Ï¤¢¤é¤«¤¸¤á³Æʸ»ú¤Î½Ð¸½ÉÑÅÙ -¤òµá¤á¤Æ¤ª¤¯É¬Íפ¬¤¢¤ê¡¢Éü¹æ²½¤ÎºÝ¤Ï¤É¤Î¥Ó¥Ã¥ÈÎ󤬤ɤÎʸ»ú¤ËÂбþ¤¹¤ë¤« -¤ò¤¢¤é¤«¤¸¤áÃΤëɬÍפ¬¤¢¤ë¡£ +であるとすると a は 4回、bは3回、cは2回現れるので、全体は 4 + 2*3 + +2*2 = 14 ビットで表現できることになる。これが Huffman 法の圧縮原理であ +る。このように Huffman 法では文字をビット単位で扱うためビット入出力ルー +チンを先に解読したわけだ。また、符号化の際はあらかじめ各文字の出現頻度 +を求めておく必要があり、復号化の際はどのビット列がどの文字に対応するか +をあらかじめ知る必要がある。 -ʸ»úËè¤Ë¥Ó¥Ã¥ÈĹ¤Î¤Ð¤é¤Ä¤­¤¬¤¢¤ë¤è¤¦¤Ê²ÄÊÑĹÉä¹æ¤Ë¤Ï°Ê²¼¤Î¾ò·ï¤¬¤¢¤ë¡£ +文字毎にビット長のばらつきがあるような可変長符号には以下の条件がある。 - ¤¢¤ëÉä¹æ¤Î¥Ó¥Ã¥È¥Ñ¥¿¡¼¥ó¤Ï¡¢Â¾¤ÎÉä¹æ¤Î¥Ó¥Ã¥È¥Ñ¥¿¡¼¥ó¤Î³«»Ï¤Ë¤Ï¤Ê¤é - ¤Ê¤¤¡£ + ある符号のビットパターンは、他の符号のビットパターンの開始にはなら + ない。 -¤È¤¤¤¦¤â¤Î¤À¡£¤³¤ì¤ò¡Ö¸ìƬ¾ò·ï¡×¤È¸À¤¦¤é¤·¤¤¡£Î㤨¤Ð¡¢Àè¤ÎÎã¤Ç¤Ï a ¤Ë -0 ¤ò³ä¤êÅö¤Æ¤¿¤Î¤Ç¾¤Îʸ»ú¤Ïɬ¤º 1 ¤«¤é»Ï¤Þ¤ë¤è¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤ë¡£¤³¤Î¾ò -·ï¤òËþ¤¿¤µ¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤Íýͳ¤Ï¤Á¤ç¤Ã¤È¹Í¤¨¤ì¤Ð¤ï¤«¤ë¡£²¾¤Ë°Ê²¼¤Î´Ö°ã¤Ã -¤¿³äÅö¤ò¹Ô¤Ã¤¿¤È¤¹¤ë¡£ +というものだ。これを「語頭条件」と言うらしい。例えば、先の例では a に +0 を割り当てたので他の文字は必ず 1 から始まるようになっている。この条 +件を満たさなければならない理由はちょっと考えればわかる。仮に以下の間違っ +た割当を行ったとする。 - ʸ»ú ¥Ó¥Ã¥Èɽ¸½ + 文字 ビット表現 a 0 b 10 c 01 -¤³¤ì¤À¤È¡¢¥Ó¥Ã¥È¥Ñ¥¿¡¼¥ó 010 ¤¬ ab ¤Ê¤Î¤« ca ¤Ê¤Î¤«Û£Ëæ¤Ë¤Ê¤ë¤Î¤¬¤ï¤« -¤ë¤À¤í¤¦¡£ +これだと、ビットパターン 010 が ab なのか ca なのか曖昧になるのがわか +るだろう。 -ʸ»ú¤ËÂбþ¤¹¤ë¸ìƬ¾ò·ï¤òËþ¤¿¤¹(ºÇŬ¤Ê)¥Ó¥Ã¥ÈÎó¤òµá¤á¤ëÊýË¡¡¢¤½¤ì¤¬¥Ï¥Õ -¥Þ¥óË¡¤À¡£¥Ï¥Õ¥Þ¥óË¡¤Ç¤Ï¥Ï¥Õ¥Þ¥óÌڤȤ¤¤¦ÌÚ¹½Â¤¤ò¹½ÃÛ¤¹¤ë¤Î¤À¤¬¡¢¤½¤Î¥¢ -¥ë¥´¥ê¥º¥à¤Ï°Ê²¼¤Î¤È¤ª¤ê¤À¡£ +文字に対応する語頭条件を満たす(最適な)ビット列を求める方法、それがハフ +マン法だ。ハフマン法ではハフマン木という木構造を構築するのだが、そのア +ルゴリズムは以下のとおりだ。 -¤Þ¤º¡¢°µ½ÌÂоݤǤ¢¤ë¥Æ¥­¥¹¥È¤Ë´Ø¤·¤Æ³Æʸ»ú¤Î½Ð¸½²ó¿ô¤ò¿ô¤¨¤ë¡£Î㤨¤Ð -abcabcaba ¤È¤¤¤¦¥Æ¥­¥¹¥È¤Ç¤Ï¡¢a ¤Ï 4²ó¡¢b¤Ï3²ó¡¢c¤Ï2²ó¤Ê¤Î¤Ç¡¢ +まず、圧縮対象であるテキストに関して各文字の出現回数を数える。例えば +abcabcaba というテキストでは、a は 4回、bは3回、cは2回なので、 4 3 2 | | | a b c -¤È¤Ê¤ë¡£¼¡¤Ë¡¢½Ð¸½²ó¿ô¤ÎÄ㤤¤â¤ÎƱ»Î¤ò°ì¤Ä¤ÎÀá¤Ë«¤Í¤ë¡£¤³¤ÎÀá¤Ï 3+2=5 -¤È¤¤¤¦½Ð¸½²ó¿ô¤ò»ý¤Ä¤â¤Î¤È¹Í¤¨¤ë¡£ +となる。次に、出現回数の低いもの同士を一つの節に束ねる。この節は 3+2=5 +という出現回数を持つものと考える。 4 5 | / \ a b c -°Ê¹ß¤µ¤é¤Ë½Ð¸½²ó¿ô¤ÎÄ㤤¤â¤ÎƱ»Î¤ò°ì¤Ä¤ÎÀá¤Ë«¤Í¤ëÁàºî¤ò·«¤êÊÖ¤¹¡£¤³¤Î -Îã¤Ç¤Ï¡¢¤â¤¦°ìÅÙ«¤Í¤ì¤Ð½ª¤ê¤À¡£ +以降さらに出現回数の低いもの同士を一つの節に束ねる操作を繰り返す。この +例では、もう一度束ねれば終りだ。 9 /\ @@ -2659,28 +2659,28 @@ abcabcaba / / \ a b c -¤³¤³¤Ç¡¢Ìڤκ¸Â¦¤¬ 0 ±¦Â¦¤¬ 1 ¤Ç¤¢¤ë¤È¤¹¤ë¤È¡£a ¤Ïº¬¤«¤éº¸¤Ë1¤Ä¿Ê¤à¤À -¤±¤Ê¤Î¤Ç 0¡£b ¤Ï¡¢±¦(1)¢ªº¸(0) ¤Ê¤Î¤Ç¡¢10¡£c ¤Ï±¦(1)¢ª±¦(1) ¤Ê¤Î¤Ç¡¢11 -¤È¤Ê¤ë¡£¼ÂºÝ¤ÎÉä¹æ²½¤ÎºÝ¤Ïʸ»ú¤«¤é¥Ó¥Ã¥ÈÎó¤òµá¤á¤ë¤Î¤ÇÍÕ¤«¤éº¬¤Ë¤à¤«¤Ã -¤ÆµÕ½ç¤Ëé¤ë¤³¤È¤Ë¤Ê¤ë¡£¤Þ¤¿¡¢Éü¹æ¤ÎºÝ¤ÏÆþÎϤΥӥåÈÎó¤Ë±è¤Ã¤Æ¤³¤ÎÌÚ¤ò -º¬¤«¤éé¤ë¤³¤È¤ÇÂбþ¤¹¤ëʸ»ú¤òµá¤á¤ë(¤Ê¤Î¤Ç°µ½Ìʸ¤Ë¤Ï¤³¤ÎÌÚ¹½Â¤¤¬°ì½ï -¤Ë¾ðÊó¤È¤·¤Æ³ÊǼ¤µ¤ì¤ë¤³¤È¤Ë¤Ê¤ë)¡£ +ここで、木の左側が 0 右側が 1 であるとすると。a は根から左に1つ進むだ +けなので 0。b は、右(1)→左(0) なので、10。c は右(1)→右(1) なので、11 +となる。実際の符号化の際は文字からビット列を求めるので葉から根にむかっ +て逆順に辿ることになる。また、復号の際は入力のビット列に沿ってこの木を +根から辿ることで対応する文字を求める(なので圧縮文にはこの木構造が一緒 +に情報として格納されることになる)。 -¤³¤Î¤è¤¦¤Ê¥Ï¥Õ¥Þ¥óÌÚ¤òºîÀ®¤¹¤ë²Õ½ê¤¬¤¢¤ë¤«¤É¤¦¤«¤òõ¤·¤Æ¤ß¤¿¤È¤³¤í -maketree.c:make_tree() ¤¬¸«¤Ä¤«¤Ã¤¿¡£¤³¤ì¤Ï¡ÖC¸À¸ì¤Ë¤è¤ëºÇ¿·¥¢¥ë¥´¥ê¥º -¥à¼­Åµ¡×(±ü¼À²É§Ãø¡¢µ»½ÑɾÏÀ¼Ò)¤ËºÜ¤Ã¤Æ¤¤¤ë¤â¤Î¤È¤Û¤È¤ó¤ÉƱ¤¸¤À¡£¤Ç¤Ï¡¢ -¤³¤Î´Ø¿ô¤Î²òÆɤ«¤é»Ï¤á¤è¤¦(º£²ó¤Î²òÀϤϥܥȥॢ¥Ã¥×¼°¤Ë¹Ô¤¦¤³¤È¤Ë¤·¤è -¤¦¤È»×¤¦¡£¤È¤¤¤¦¤Î¤â¥Ç¡¼¥¿¹½Â¤¤«¤é¹¶¤á¤è¤¦¤Ë¤â¥°¥í¡¼¥Ð¥ëÊÑ¿ô¤¬¤¿¤¯¤µ¤ó -½Ð¤ÆÍè¤ë¤·¡¢½èÍý½ç¤ËÄɤäƤâÀµÄ¾¤è¤¯¤ï¤«¤é¤Ê¤«¤Ã¤¿¤«¤é¤À) +このようなハフマン木を作成する箇所があるかどうかを探してみたところ +maketree.c:make_tree() が見つかった。これは「C言語による最新アルゴリズ +ム辞典」(奥村晴彦著、技術評論社)に載っているものとほとんど同じだ。では、 +この関数の解読から始めよう(今回の解析はボトムアップ式に行うことにしよ +うと思う。というのもデータ構造から攻めようにもグローバル変数がたくさん +出て来るし、処理順に追っても正直よくわからなかったからだ) -¤³¤Î´Ø¿ô¤Î¤¢¤ë¥Õ¥¡¥¤¥ë maketree.c ¤Ç»ÈÍѤ·¤Æ¤¤¤ë¥Ç¡¼¥¿¹½Â¤¤Ï°Ê²¼¤À¡£ +この関数のあるファイル maketree.c で使用しているデータ構造は以下だ。 static short n, heapsize, heap[NC + 1]; static unsigned short *freq, *sort; static unsigned char *len; static unsigned short len_cnt[17]; -make_tree() ¤Ï°Ê²¼¡£ +make_tree() は以下。 short make_tree(nparm, freqparm, lenparm, codeparm) @@ -2738,13 +2738,13 @@ make_tree(nparm, freqparm, lenparm, codeparm) return k; /* return root */ } -¤³¤Î´Ø¿ô¤Î°ú¿ô¤Ë¡¢nparm, freqparm, lenparm, codeparm ¤È¤¤¤¦¤Î¤¬¤¢¤ë¡£ -¤³¤ì¤¬¤Ê¤ó¤Ê¤Î¤«¤¤¤­¤Ê¤ê¤Ç¤Ï¤ï¤«¤é¤Ê¤¤¤À¤í¤¦¡£¼Â¤Ï»ä¤Ë¤â¤ï¤«¤é¤Ê¤¤¡£º£ -²ó¤Î²òÀϤÇÆüì¤Ê¤Î¤Ï¡¢½èÍýÆâÍƤˤĤ¤¤Æ¤Ï»ä¤Ï(¥¢¥ë¥´¥ê¥º¥à¼­Åµ¤Ç)ÃÎ¤Ã¤Æ -¤¤¤ë¤³¤È¤À¡£¶¯°ú¤Ë̵»ë¤·¤Æ¤â½èÍýÆâÍƤ«¤éÁÛÁü¤¬¤Ä¤¯¤À¤í¤¦¤³¤È¤ò´üÂÔ¤·¤Æ -¤¤¤ë¡£ +この関数の引数に、nparm, freqparm, lenparm, codeparm というのがある。 +これがなんなのかいきなりではわからないだろう。実は私にもわからない。今 +回の解析で特殊なのは、処理内容については私は(アルゴリズム辞典で)知って +いることだ。強引に無視しても処理内容から想像がつくだろうことを期待して +いる。 -¤È¤ê¤¢¤¨¤º¡¢ËÁƬ¤Î½é´ü²½Éôʬ (A) ¤Ç +とりあえず、冒頭の初期化部分 (A) で /* (A) */ n = nparm; @@ -2752,8 +2752,8 @@ make_tree(nparm, freqparm, lenparm, codeparm) len = lenparm; avail = n; -¤È¤·¤Æ¤¤¤ë¡£°ú¿ô¤Ç¼õ¤±¤¿ÆþÎϤò¤³¤Î¥Õ¥¡¥¤¥ë¤Î static ÊÑ¿ô¤Ë¥»¥Ã¥È¤·¡¢Â¾ -¤Î¥ë¡¼¥Á¥ó¤È¥Ç¡¼¥¿¶¦Í­¤·¤Æ¤¤¤ë¤è¤¦¤À¡£avail ¤Ï¸å¤ÇÀâÌÀ¤·¤è¤¦¡£ +としている。引数で受けた入力をこのファイルの static 変数にセットし、他 +のルーチンとデータ共有しているようだ。avail は後で説明しよう。 /* (B) */ heapsize = 0; @@ -2764,21 +2764,21 @@ make_tree(nparm, freqparm, lenparm, codeparm) heap[++heapsize] = i; } -¤³¤³¤Ç¡¢heap[] ¤ò½é´ü²½¤·¤Æ¤¤¤ë¡£heapsize ¤Ï¡¢heap ¤ÎÍ×ÁÇ¿ô¤È¤Ê¤ë¡£¤³ -¤Î½èÍý¤ÏÍ¥ÀèÂÔ¤Á¹ÔÎó heap[] ¤òºî¤ëÉôʬ¤Ê¤Î¤À¤¬¡¢¤Ê¤¼Í¥ÀèÂÔ¤Á¹ÔÎó¤¬É¬Í× -¤Ê¤Î¤«¤È¤¤¤¦¤ÈÀè¤ÎÀâÌÀ¤Ç Huffman Ë¡¤Î¥¢¥ë¥´¥ê¥º¥à¤Ë½Ð¸½²ó¿ô¤Î¾¯¤Ê¤¤½ç -¤ËÍÕ¤òÀá¤Ë«¤Í¤ë¤È¤¤¤¦Éôʬ¤¬¤¢¤Ã¤¿¡£Í¥ÀèÂÔ¤Á¹ÔÎó¤Ï¤³¤Î¤¿¤á¤Î¤â¤Î¤À¡£¤È -¤ê¤¢¤¨¤º¡¢heap[] ¤ÎÍ×ÁǤϰµ½Ì¤¹¤ëʸ»ú¤Ç¤¢¤ë¤È¤¤¤¦¤³¤È¤À¤±¤ò½ñ¤¤¤Æ¤ª¤¯¡£ -¾ÜºÙ¤Ï¤¹¤°¸å¤Ç½Ð¤ë¤À¤í¤¦¡£freq[i] (¤¹¤Ê¤ï¤Á°ú¿ô freqparm) ¤Ï¡¢Ê¸»ú i -¤Î½Ð¸½²ó¿ô¤òɽ¤·¤Æ¤¤¤ë¡£¤À¤«¤é¡¢n (nparm)¤Ï¡¢Éä¹æ²½¤¹¤ë¥â¥Ç¥ë¾å¤Îʸ»ú -¤Î¼ïÎà¤Î¿ô¤òɽ¤·¤Æ¤¤¤ë¤³¤È¤Ë¤Ê¤ë¡£¤¿¤È¤¨¤ÐÄ̾ï¤Î¥Õ¥¡¥¤¥ë¤Ê¤é nparm ¤Ï -256 ¤À¤í¤¦¡£¤Þ¤¢¡¢·ë¶É¤Ï freq[] ¤ÎÍ×ÁÇ¿ô¤À¡£ +ここで、heap[] を初期化している。heapsize は、heap の要素数となる。こ +の処理は優先待ち行列 heap[] を作る部分なのだが、なぜ優先待ち行列が必要 +なのかというと先の説明で Huffman 法のアルゴリズムに出現回数の少ない順 +に葉を節に束ねるという部分があった。優先待ち行列はこのためのものだ。と +りあえず、heap[] の要素は圧縮する文字であるということだけを書いておく。 +詳細はすぐ後で出るだろう。freq[i] (すなわち引数 freqparm) は、文字 i +の出現回数を表している。だから、n (nparm)は、符号化するモデル上の文字 +の種類の数を表していることになる。たとえば通常のファイルなら nparm は +256 だろう。まあ、結局は freq[] の要素数だ。 - nparm ºÇÂçÍ×ÁÇ¿ô - freqparm[0:nparm] ź»ú¤¬Ê¸»ú¤Ç¡¢¤½¤ÎÍ×ÁǤ¬½Ð¸½²ó¿ô + nparm 最大要素数 + freqparm[0:nparm] 添字が文字で、その要素が出現回数 -Ãí°Õ¤¹¤Ù¤­¤Ê¤Î¤Ï heap[] ¤ÎÍ×ÁÇ¤Ï 1 °Ê¹ß¤ò»ÈÍѤ·¤Æ¤¤¤ë¤³¤È¤À¤í¤¦¡£ -heap[0] ¤Ï»È¤ï¤ì¤Ê¤¤¡£ +注意すべきなのは heap[] の要素は 1 以降を使用していることだろう。 +heap[0] は使われない。 /* (C) */ if (heapsize < 2) { @@ -2786,36 +2786,36 @@ heap[0] return heap[1]; } -¤³¤ì¤Ï¡¢heapsize ¤¬ 0 ¤« 1 ¤Î¾ì¹ç¤òɽ¤·¤Æ¤¤¤ë¡£Éä¹æ²½¤¹¤ëʸ»ú¤Î¼ïÎब -0 ¤Þ¤¿¤Ï 1 ¤Ä¤·¤«¤Ê¤¤¾ì¹ç¤À¡£heap[1] ¤Ï¡¢(B) ¤Ç 0 ¤Ë½é´ü²½¤·¤Æ¤¤¤¿¤Î¤Ç¡¢ -codeparm[0] = 0 ¤È¤·¤Æ¡¢0 ¤òÊÖ¤·¤Æ¤¤¤ë¡£¤³¤ì¤ÏÆüì¤Ê¾ò·ï¤ò¼¨¤·¤Æ¤¤¤ë¡£ -´Êñ¤ËÁÛÁü¤¬¤Ä¤¯¤³¤È¤À¤¬¡¢½Ð¸½¤¹¤ëʸ»ú¤Î¼ïÎब1¼ïÎष¤«¤Ê¤¤¾ì¹ç¡¢¥Ï¥Õ -¥Þ¥óÌÚ¤òºî¤ëɬÍפ¬¤Ê¤¤¡£LHa ¤Ç¤Ï¤³¤Î¤è¤¦¤Ê¾ì¹ç¤ËÆüì¤Ê¹½Â¤¤¢¤ë¤¤¤ÏÊýË¡ -¤òÍѤ¤¤Æ¤¤¤ë¤³¤È¤¬ÁÛÁü¤Ç¤­¤ë¡£ +これは、heapsize が 0 か 1 の場合を表している。符号化する文字の種類が +0 または 1 つしかない場合だ。heap[1] は、(B) で 0 に初期化していたので、 +codeparm[0] = 0 として、0 を返している。これは特殊な条件を示している。 +簡単に想像がつくことだが、出現する文字の種類が1種類しかない場合、ハフ +マン木を作る必要がない。LHa ではこのような場合に特殊な構造あるいは方法 +を用いていることが想像できる。 /* (D) */ for (i = heapsize / 2; i >= 1; i--) downheap(i); /* make priority queue */ -Í¥ÀèÂÔ¤Á¹ÔÎó heap[] ¤ò¹½ÃÛ¤¹¤ë¡£downheap() ¤¬¤Ê¤ó¤Ê¤Î¤«¡¢¤³¤ì¤¬¤É¤¦¤¤¤Ã -¤¿½èÍý¤Ê¤Î¤«¤Î¾ÜºÙ¤Ï¾Êά¤·¤è¤¦¡£¥¢¥ë¥´¥ê¥º¥à¼­Åµ¤Î¡Ö¥Ò¡¼¥×¥½¡¼¥È¡×¤Î¹à -¤Ë¾Ü¤·¤¤¤¬¡¢heap[] ¤ÏÌÚ¹½Â¤¤ò¼¨¤·¤Æ¤ª¤ê¡¢¤³¤ÎÌÚ¹½Â¤(2ʬÌÚ)¤Ë¤Ï¡Ö¿Æ¤Ï»Ò -¤è¤êÍ¥Àè½ç°Ì¤¬Æ±¤¸¤«¹â¤¤¡×¤È¤¤¤¦µ¬Â§¤À¤±¤¬¤¢¤ë¡£¤³¤ÎÌÚ¹½Â¤¤Ï¡¢ +優先待ち行列 heap[] を構築する。downheap() がなんなのか、これがどういっ +た処理なのかの詳細は省略しよう。アルゴリズム辞典の「ヒープソート」の項 +に詳しいが、heap[] は木構造を示しており、この木構造(2分木)には「親は子 +より優先順位が同じか高い」という規則だけがある。この木構造は、 - 1. heap[n] ¤Îº¸¤Î»Ò¤Ï heap[2*n]¡¢±¦¤Î»Ò¤Ï heap[2*n + 1] + 1. heap[n] の左の子は heap[2*n]、右の子は heap[2*n + 1] -¤Ç¡¢É½¸½¤µ¤ì¤Æ¤ª¤ê¡¢¤³¤Î¤è¤¦¤ÊȾ½ç½øÌÚ (partial ordered tree) ¤Ë¤Ï¡¢°Ê -²¼¤ÎÆÃħ¤¬¤¢¤ë +で、表現されており、このような半順序木 (partial ordered tree) には、以 +下の特徴がある - 2. heap[n] ¤Î¿Æ¤Ï heap[n/2] - 3. heap[1.. heapsize/2] ¤ÏÀá¤Ç¡¢heap[heapsize/2 .. heapsize] ¤ÏÍÕ + 2. heap[n] の親は heap[n/2] + 3. heap[1.. heapsize/2] は節で、heap[heapsize/2 .. heapsize] は葉 -¤³¤Î heap[] ¤¬ºÇ½é¤Ð¤é¤Ð¤é¤ËÍ×ÁǤ¬³ÊǼ¤µ¤ì¤Æ¤¤¤ë¤È¤­¡¢Íդ˶ᤤÀᤫ¤é½ç -¤Ë downheap() ¤È¤¤¤¦Áàºî¤ò¹Ô¤¦((D)¤Î½èÍý)¤È¡¢¥Ò¡¼¥×¤ò¹½ÃۤǤ­¤ë¤è¤¦¤Ë -¤Ê¤Ã¤Æ¤¤¤ë¡£downheap(i) ¤Ï¡¢Àá heap[i] ¤È¤½¤Î»Ò heap[2*i], heap[2*i+1] -¤ÇÍ×ÁǤòÈæ³Ó¤·¡¢»Ò¤ÎÍ¥Àè½ç°Ì¤ÎÊý¤¬¹â¤±¤ì¤Ð°ÌÃÖ¤ò¸ò´¹¤¹¤ë¡¢¤È¤¤¤¦½èÍý¤ò -Íդ˸þ¤«¤Ã¤Æ·«¤êÊÖ¤¹´Ø¿ô¤À¡£°Ê²¼¡¢»²¹Í¤Þ¤Ç¤Ë maketree.c:downheap() ¤Î -ÆâÍƤò¼¨¤¹¡£ +この heap[] が最初ばらばらに要素が格納されているとき、葉に近い節から順 +に downheap() という操作を行う((D)の処理)と、ヒープを構築できるように +なっている。downheap(i) は、節 heap[i] とその子 heap[2*i], heap[2*i+1] +で要素を比較し、子の優先順位の方が高ければ位置を交換する、という処理を +葉に向かって繰り返す関数だ。以下、参考までに maketree.c:downheap() の +内容を示す。 static void downheap(i) @@ -2836,11 +2836,11 @@ downheap(i) heap[i] = k; } -¤È¤Ë¤«¤¯ (D) ¤Ë¤è¤ê¡¢ºÇ¤âÍ¥Àè½ç°Ì¤Î¹â¤¤(½Ð¸½²ó¿ô¤Î¾¯¤Ê¤¤)Í×ÁǤ¬ -heap[1] ¤ËÍè¤ë¤è¤¦¤Ë¤Ê¤ë¡£¤³¤ÎÍ¥ÀèÂÔ¤Á¹ÔÎó¤Ï¤Ê¤«¤Ê¤«ÌÌÇò¤¤(¤È»ä¤Ï»×¤Ã -¤¿)¤Î¤Ç¤¤¤í¤¤¤í¤ÈÄ´¤Ù¤Æ¤ß¤ë¤Î¤â¤è¤¤¤À¤í¤¦¡£ +とにかく (D) により、最も優先順位の高い(出現回数の少ない)要素が +heap[1] に来るようになる。この優先待ち行列はなかなか面白い(と私は思っ +た)のでいろいろと調べてみるのもよいだろう。 -¤µ¤Æ¡¢Â³¤±¤è¤¦ (E) ¤À¡£ +さて、続けよう (E) だ。 /* (E) */ sort = codeparm; @@ -2861,38 +2861,38 @@ heap[1] right[k] = j; } while (heapsize > 1); -ºÇ½é¤Ë¡¢ +最初に、 i = heap[1]; /* take out least-freq entry */ if (i < n) *sort++ = i; -¤Ç¡¢ºÇ¤â½Ð¸½²ó¿ô¤Î¾¯¤Ê¤¤Ê¸»ú¤ò¼è¤Ã¤ÆÍè¤ë¡£if ¤ÎÉôʬ¤Ï¤Ò¤È¤Þ¤ºÌµ»ë¤·¤è¤¦¡£ +で、最も出現回数の少ない文字を取って来る。if の部分はひとまず無視しよう。 heap[1] = heap[heapsize--]; downheap(1); -¤Ç¡¢heap[] ¤ÎºÇ¸åÈø¤ÎÍ×ÁǤòÀèƬ¤Ë»ý¤Ã¤ÆÍè¤Æ downheap(1) ¤ò¹Ô¤Ã¤Æ¤¤¤ë¡£ -¤³¤¦¤¹¤ë¤È¡¢¥Ò¡¼¥×¤¬ºÆ¹½ÃÛ¤µ¤ì¡Ö¿Æ¤Ï»Ò¤è¤êÍ¥Àè½ç°Ì¤¬Æ±¤¸¤«¹â¤¤¡×¤È¤¤¤¦ -¾ò·ï¤ò¤Þ¤¿Ëþ¤¿¤¹¤è¤¦¤Ë¤Ê¤ë¡£heap[] ¤ÎÍ×ÁǤÏ1¤Ä¸º¤Ã¤Æ¤¤¤ë¡£·ë¶É¡¢¤³¤³¤Þ -¤Ç¤Î½èÍý¤ÏÏÀÍýŪ¤Ë¤Ï¡ÖÍ¥ÀèÂÔ¤Á¹ÔÎ󤫤éÍ¥ÀèÅ٤ι⤤Í×ÁǤò1¤Ä¼è¤ê½Ð¤¹¡× -¤È¸À¤¦½èÍý¤À¡£ +で、heap[] の最後尾の要素を先頭に持って来て downheap(1) を行っている。 +こうすると、ヒープが再構築され「親は子より優先順位が同じか高い」という +条件をまた満たすようになる。heap[] の要素は1つ減っている。結局、ここま +での処理は論理的には「優先待ち行列から優先度の高い要素を1つ取り出す」 +と言う処理だ。 j = heap[1]; /* next least-freq entry */ if (j < n) *sort++ = j; -³¤±¤Æ¡¢2ÈÖÌܤËÍ¥ÀèÅ٤ι⤤Í×ÁǤò¼è¤ê½Ð¤¹¡£¤Þ¤¿¡¢if ¤Ï̵»ë¤·¤Æ¤ª¤³¤¦¡£ +続けて、2番目に優先度の高い要素を取り出す。また、if は無視しておこう。 k = avail++; /* generate new node */ freq[k] = freq[i] + freq[j]; heap[1] = k; downheap(1); /* put into queue */ -avail ¤ÏºÇ½é n (nparm)¤À¤Ã¤¿¡£freq[] ¤Ï¡¢Ê¸»ú¤Î½Ð¸½²ó¿ô¤Ê¤Î¤ÇºÇ½éʸ»ú -¤Î¼ïÎà¿ôʬ(nparm)¤ÎÍ×ÁǤ·¤«¤Ê¤¤¤¬¥Ï¥Õ¥Þ¥óÌÚ¤ÎÀá¤Î½Ð¸½²ó¿ô(¤È¤¤¤¦¤«Í¥Àè -½ç°Ì)¤ò³ÊǼ¤¹¤ë¤¿¤á¤Ë freq[] ¤Ï¡¢nparm * 2 - 1 ¤Î³ÊǼ°è¤¬É¬ÍפȤʤ뤳 -¤È¤¬¤ï¤«¤ë¡£(ÍÕ¤¬ n ¸Ä¤¢¤ë 2 ʬÌڤˤϡ¢À᤬ n - 1 ¸Ä¤¢¤ë) +avail は最初 n (nparm)だった。freq[] は、文字の出現回数なので最初文字 +の種類数分(nparm)の要素しかないがハフマン木の節の出現回数(というか優先 +順位)を格納するために freq[] は、nparm * 2 - 1 の格納域が必要となるこ +とがわかる。(葉が n 個ある 2 分木には、節が n - 1 個ある) ---------------------------------------------------------------------------- @@ -2902,11 +2902,11 @@ freq | | | 0 nparm nparm * 2 - 1 |-----------------------|-----------------------| - ʸ»ú(¥Ï¥Õ¥Þ¥óÌÚ¤ÎÍÕ) ¥Ï¥Õ¥Þ¥óÌÚ¤ÎÀá¤ÎÍ¥Àè½ç°Ì - ¤ÎÍ¥Àè½ç°Ì + 文字(ハフマン木の葉) ハフマン木の節の優先順位 + の優先順位 - Îã: + 例: . ... freq[4] / \ . \ ... freq[3] @@ -2915,31 +2915,31 @@ freq | | | ---------------------------------------------------------------------------- -¤³¤³¤Þ¤Ç¤Ç¡¢½Ð¸½²ó¿ô¤ÎÄ㤤2¤Ä¤ÎÍ×ÁǤò¼è¤ê½Ð¤·¤½¤Î½Ð¸½²ó¿ô¤ÎϤò -freq[k] ¤ËÀßÄꤹ¤ë¤³¤È¤Ë¤Ê¤ë¡£½Ð¸½²ó¿ô¤ÎÏÂ¤Ï heap[] ¤ËºÆÀßÄꤵ¤ì¡¢ -downheap(1) ¤Ç¡¢Í¥ÀèÂÔ¤Á¹ÔÎó¤ò¤Þ¤¿ºÆ¹½ÃÛ¤¹¤ë¡£¤³¤ì¤Ï¡ÖÍÕ¤ò«¤ÍÀá¤òºî¤ë¡× -¤È¤¤¤¦¥Ï¥Õ¥Þ¥óÌڤι½ÃÛ¥¢¥ë¥´¥ê¥º¥à¤Î¼ÂÁõ¤À¡£¿·¤·¤¯ºîÀ®¤·¤¿À᤬ k ¤Ç¤½ -¤ÎºÇÂçÃͤ¬ºÇ½ªÅª¤Ë avail-1 ¤Ç¤¢¤ë¡£ +ここまでで、出現回数の低い2つの要素を取り出しその出現回数の和を +freq[k] に設定することになる。出現回数の和は heap[] に再設定され、 +downheap(1) で、優先待ち行列をまた再構築する。これは「葉を束ね節を作る」 +というハフマン木の構築アルゴリズムの実装だ。新しく作成した節が k でそ +の最大値が最終的に avail-1 である。 -ºÇ¸å¤Ë +最後に left[k] = i; right[k] = j; -¤Ç¡¢¥Ï¥Õ¥Þ¥óÌÚ¤ò¹½Â¤ left[]¡¢right[] ¤ÇºîÀ®¤¹¤ë¡£ +で、ハフマン木を構造 left[]、right[] で作成する。 -¤³¤Î (E) ¤òÃê¾ÝÅ٤ι⤤¥³¡¼¥É¤Ç¼¨¤·¤Æ¤ß¤è¤¦¡£¥Ï¥Õ¥Þ¥óÌÚ¤Ï +この (E) を抽象度の高いコードで示してみよう。ハフマン木は struct huffman { ... } huff; -¤Çɽ¤·¡¢¥Ï¥Õ¥Þ¥óÌÚ¤Î1¤Ä¤ÎÀá¤Ï¡¢ +で表し、ハフマン木の1つの節は、 make_huff(huff, node, left, right) -¤ÇºîÀ®¤Ç¤­¤ë¤È¤¹¤ë¡£¤Þ¤¿¡¢Í¥Àè½ç°Ì¤Ä¤­ÂÔ¤Á¹ÔÎó¤ò heap ¤È¤·¡¢heap ¤«¤é -Í×ÁǤò¼è¤ê½Ð¤¹½èÍý¡¢Í×ÁǤò³ÊǼ¤¹¤ë½èÍý¤ò¤½¤ì¤¾¤ì²¾¤Ë +で作成できるとする。また、優先順位つき待ち行列を heap とし、heap から +要素を取り出す処理、要素を格納する処理をそれぞれ仮に n = delete_pqueue(heap) insert_pqueue(heap, n) -¤È¤¹¤ë¤È¡¢ +とすると、 /* (E) */ do { @@ -2954,19 +2954,19 @@ downheap(1) make_huff(&huff, node, left, right); } while (heapsize > 1); -¤³¤ó¤Ê¤È¤³¤í¤À¤í¤¦¤«¡£¸µ¤Î½èÍý¤Ç¤Ï¥Ò¡¼¥×¤«¤é¤ÎÍ×ÁǤμè¤ê½Ð¤·¤ÈÁÞÆþ¤Ç̵ -Â̤ʽèÍý¤ò̵¤¯¤¹¤¿¤á¤Ë¾¯¤·Ê£»¨¤Ë¤Ê¤Ã¤Æ¤¤¤ë¡£(¤½¤·¤Æ¥Ç¡¼¥¿¹½Â¤¤Ë°Í¸¤· -¤¿½èÍý¤Ë¤Ê¤Ã¤Æ¤¤¤ë)¡£¤É¤Á¤é¤¬¤è¤ê¤¹¤°¤ì¤Æ¤¤¤ë¤«¤ÏÈù̯¤Ê½ê¤À¡£»ä¤Ï¿¾¯ -¤Î½èÍý¤Î̵Â̤âÌܤò¤Ä¤Ö¤Ã¤Æ¤è¤ê¤ï¤«¤ê¤ä¤¹¤¤Êý¤òÍ¥À褹¤ë¤Î¤¬¹¥¤­¤Ê¤Î¤À¤¬¡£ -¤³¤ì¤Ï¤Á¤ç¤Ã¤È¹Í¤¨¤µ¤»¤é¤ì¤ë¤È¤³¤í¤À¡£ +こんなところだろうか。元の処理ではヒープからの要素の取り出しと挿入で無 +駄な処理を無くすために少し複雑になっている。(そしてデータ構造に依存し +た処理になっている)。どちらがよりすぐれているかは微妙な所だ。私は多少 +の処理の無駄も目をつぶってよりわかりやすい方を優先するのが好きなのだが。 +これはちょっと考えさせられるところだ。 -¥ë¡¼¥×¤òÈ´¤±¤¿½ê¤Ç k (avail - 1) ¤Ï¡¢¥Ï¥Õ¥Þ¥óÌڤꬤòɽ¤·¤Æ¤¤¤ë¡£ -left[0:avail], right[0:avail] ¤Ç¥Ï¥Õ¥Þ¥óÌÚ¤òɽ¤·¡¢¤½¤Î¤¦¤Á -left[nparm...avail], right[nparm...avail] ¤¬Àá¤Î»Ò¤ò¼¨¤·¤Æ¤¤¤ë¡£ -left[0...nparm], right[0...nparm] ¤Ï»È¤ï¤ì¤Ê¤¤¤è¤¦¤À¡£ +ループを抜けた所で k (avail - 1) は、ハフマン木の根を表している。 +left[0:avail], right[0:avail] でハフマン木を表し、そのうち +left[nparm...avail], right[nparm...avail] が節の子を示している。 +left[0...nparm], right[0...nparm] は使われないようだ。 ---------------------------------------------------------------------------- - Îã: + 例: . -- k (= avail-1) / \ left[k] -- . \ @@ -2978,10 +2978,10 @@ left[0...nparm], right[0...nparm] ---------------------------------------------------------------------------- -¤³¤ì¤Ç¡¢¥Ï¥Õ¥Þ¥óÌڤι½ÃۤϽª¤ê¤Ê¤Î¤À¤¬¡¢¥Ï¥Õ¥Þ¥óË¡¤ÎÉä¹æ²½¤Ç¤Ï¥Ï¥Õ¥Þ¥ó -ÌÚ¤ÎÍÕ¤«¤éº¬¤Ë¸þ¤«¤Ã¤ÆÌÚ¤òé¤ëɬÍפ¬¤¢¤ë¤Ï¤º¤Ê¤Î¤Ë¡¢left[]¡¢right[] ¤Î -¹½Â¤¤Ç¤Ïº¬¤«¤éÍդ˸þ¤«¤Ã¤Æ¤·¤«ÌÚ¤òé¤ë¤³¤È¤¬¤Ç¤­¤Ê¤¤¤Ï¤º¤À¡£¤³¤ì¤Ï¤É¤¦ -¤¤¤¦¤³¤È¤À¤í¤¦¡©make_tree() ¤Ç¤Ï¤Þ¤À½èÍý¤¬Â³¤¤¤Æ¤¤¤ë¡£ +これで、ハフマン木の構築は終りなのだが、ハフマン法の符号化ではハフマン +木の葉から根に向かって木を辿る必要があるはずなのに、left[]、right[] の +構造では根から葉に向かってしか木を辿ることができないはずだ。これはどう +いうことだろう?make_tree() ではまだ処理が続いている。 /* (F) */ sort = codeparm; @@ -2989,13 +2989,13 @@ left[0...nparm], right[0...nparm] make_code(nparm, lenparm, codeparm); return k; /* return root */ -¤É¤¦¤ä¤é¡¢ÌÚ¹½Â¤¤Î¾¤Ë¤â¤Ê¤Ë¤ä¤é¹½Â¤¤òºîÀ®¤·¤Æ¤¤¤ë¤è¤¦¤À¡£¤³¤ì¤ÏÀèÄø̵ -»ë¤·¤¿ if ʸ¤Ë¤â´ØÏ¢¤¹¤ë¡£¤½¤·¤Æ¤³¤ì¤Ï¡Ö¥¢¥ë¥´¥ê¥º¥à¼­Åµ¡×¤Ë¤ÏºÜ¤Ã¤Æ¤¤ -¤Ê¤¤Éôʬ¤À¡£¤É¤¦¤ä¤é LHa ¤Ê¤ê¤Î¹©Éפ¬¤¢¤ë¤è¤¦¤À¡£ +どうやら、木構造の他にもなにやら構造を作成しているようだ。これは先程無 +視した if 文にも関連する。そしてこれは「アルゴリズム辞典」には載ってい +ない部分だ。どうやら LHa なりの工夫があるようだ。 -¤Þ¤º¡¢maketree.c:make_len(root) ¤«¤é¸«¤Æ¤ß¤è¤¦¤È»×¤¦¤¬¡¢¤½¤ÎÁ°¤Ë¤³¤Î´Ø -¿ô¤Ï maketree.c:count_len(root) ¤È¤¤¤¦´Ø¿ô¤ò¸Æ¤Ó½Ð¤·¤Æ¤¤¤ë¡£¤³¤Á¤é¤«¤é -Àè¤Ë¸«¤ë¤³¤È¤Ë¤·¤¿¡£ +まず、maketree.c:make_len(root) から見てみようと思うが、その前にこの関 +数は maketree.c:count_len(root) という関数を呼び出している。こちらから +先に見ることにした。 static void count_len(i) /* call with i = root */ @@ -3013,22 +3013,22 @@ count_len(i) /* call with i = root */ } } -¤³¤Î´Ø¿ô¤ËÅϤµ¤ì¤ë i ¤Ï¡¢ºÇ½é¥Ï¥Õ¥Þ¥óÌڤꬤò»Ø¤¹ÃͤÀ¡£¤³¤Î´Ø¿ô¤ÎÁ´ÂÎ -¤ò¸«¤ì¤Ð¡¢i ¤¬Àá¤äÍÕ¤ò¼¨¤¹¤³¤È¤Ï¤¹¤°¤ï¤«¤ë¡£ºÇ½é¤Î if ʸ¤Ë½Ð¤Æ¤¯¤ë n -¤Ï²¿¤«¤È¤¤¤¦¤È¤Ê¤ó¤È¤³¤Î¥Õ¥¡¥¤¥ëÆâ¤Î static ÊÑ¿ô¤Ç¡¢make_tree() ¤ÎËÁƬ -¤Ç nparm ¤Ç½é´ü²½¤·¤Æ¤¤¤¿¡£ÀèÄø¤Ïµ¤¤Ë¤â¤È¤á¤Ê¤«¤Ã¤¿¤Î¤À¤¬¡¢ÊÑ¿ô̾¤ÎÁª -¤ÓÊý¤¬¤É¤¦¤Ë¤â¤è¤í¤·¤¯¤Ê¤¤¡£¤È¤Ë¤«¤¯ n ¤Ï¡¢nparm ¤Ç¡¢freqparm ¤ÎºÇ½é¤Î -Í×ÁÇ¿ô¤Ç¡¢Ê¸»ú¤Î¼ïÎà¤Î¿ô¤òɽ¤·¤Æ¤¤¤¿¤â¤Î¤À¡£¤³¤³¤Ç¤Ï¥Ï¥Õ¥Þ¥óÌÚ¤ÎÀá¤äÍÕ -¤È¤Ê¤ë i ¤ÈÈæ³Ó¤·¤Æ¤¤¤ë¤³¤È¤«¤é¡¢i ¤¬¥Ï¥Õ¥Þ¥óÌÚ¤ÎÀá¤ò¼¨¤¹¤«ÍÕ¤ò¼¨¤¹¤« -¤ÎȽÃǤ˻ÈÍѤ·¤Æ¤¤¤ë¤é¤·¤¤¡£if ʸ¤Î¾ò·ï¤¬¿¿¤Î¾ì¹ç(i < n)¡¢i ¤ÏÍդǤ¢¤ë¡£ -µ¶¤Î¾ì¹ç i ¤ÏÀá¤Ç¤¢¤ë¡£µ¶¤Î¾ì¹ç¤Ï¡¢depth ¤ò­¤·¤ÆÆó¤Ä¤Î»Ò¤ËÂФ·¤ÆºÆµ¢ -Ū¤Ë¤³¤Î´Ø¿ô¤ò¸Æ¤Ó½Ð¤·¤Æ¤¤¤ë¡£¤Ç¡¢·ë¶É¤³¤Î´Ø¿ô¤¬²¿¤ò¤·¤Æ¤¤¤ë¤«¤È¤¤¤¦¤È¡¢ -Àè¤Û¤É¹½ÃÛ¤·¤¿¥Ï¥Õ¥Þ¥óÌڤ˴ؤ·¤Æ¡¢¤¢¤ë¿¼¤µ¤ÎÍդοô¤ò¿ô¤¨¤Æ¤¤¤ë¤è¤¦¤À¡£ - -len_cnt[1] ¤Ï¡¢¿¼¤µ 1 (º¬¤Î»Ò)¤ÎÍդοô¤Ç 0 ¡Á 2 ¤ÎÃͤˤʤ롣len_cnt[2] -¤Ï¡¢¿¼¤µ 2 (º¬¤Î¹)¤ÎÍդοô¤Ç 0 ¡Á 4 ¤ÎÃͤò»ý¤Ä¤À¤í¤¦¡£¤½¤·¤Æ¡¢¿¼¤µ 16 -°Ê¾å¤ÎÁؤ˴ؤ·¤Æ¤Ï len_cnt[16] ¤Ë¤¹¤Ù¤Æ·×¾å¤µ¤ì¤ë¤è¤¦¤À¡£¤È¤Ë¤«¤¯¤½¤Î -¤è¤¦¤Ê½èÍý¤À¤È¤¤¤¦¤³¤È¤Ç¤³¤Î´Ø¿ô¤ò½ª¤¨¡¢make_len() ¤ò¸«¤è¤¦¡£ +この関数に渡される i は、最初ハフマン木の根を指す値だ。この関数の全体 +を見れば、i が節や葉を示すことはすぐわかる。最初の if 文に出てくる n +は何かというとなんとこのファイル内の static 変数で、make_tree() の冒頭 +で nparm で初期化していた。先程は気にもとめなかったのだが、変数名の選 +び方がどうにもよろしくない。とにかく n は、nparm で、freqparm の最初の +要素数で、文字の種類の数を表していたものだ。ここではハフマン木の節や葉 +となる i と比較していることから、i がハフマン木の節を示すか葉を示すか +の判断に使用しているらしい。if 文の条件が真の場合(i < n)、i は葉である。 +偽の場合 i は節である。偽の場合は、depth を足して二つの子に対して再帰 +的にこの関数を呼び出している。で、結局この関数が何をしているかというと、 +先ほど構築したハフマン木に関して、ある深さの葉の数を数えているようだ。 + +len_cnt[1] は、深さ 1 (根の子)の葉の数で 0 〜 2 の値になる。len_cnt[2] +は、深さ 2 (根の孫)の葉の数で 0 〜 4 の値を持つだろう。そして、深さ 16 +以上の層に関しては len_cnt[16] にすべて計上されるようだ。とにかくその +ような処理だということでこの関数を終え、make_len() を見よう。 static void make_len(root) @@ -3075,15 +3075,15 @@ make_len(root) } } -¤Á¤ç¤Ã¤ÈÊ£»¨¤À¤¬¤æ¤Ã¤¯¤ê¸«¤Æ¤¤¤³¤¦¡£¤Þ¤º (A) ¤Î½é´ü²½Éôʬ¤ÏÀè¤Û¤É¤Î -count_len() ¤ò¸Æ¤Ó½Ð¤¹¤À¤±¤Î¤â¤Î¤Ê¤Î¤Ç¤â¤¦¤è¤¤¤À¤í¤¦¡£ +ちょっと複雑だがゆっくり見ていこう。まず (A) の初期化部分は先ほどの +count_len() を呼び出すだけのものなのでもうよいだろう。 /* (A) */ for (i = 0; i <= 16; i++) len_cnt[i] = 0; count_len(root); -¤³¤ì¤Ç¡¢len_cnt[1..16] ¤Ë¤Ï¥Ï¥Õ¥Þ¥óÌڤγÆÁؤÎÍդοô¤¬·×¾å¤µ¤ì¤ë¡£Â³¤¤¤Æ (B) +これで、len_cnt[1..16] にはハフマン木の各層の葉の数が計上される。続いて (B) /* (B) */ cum = 0; @@ -3094,42 +3094,42 @@ count_len() cum &= 0xffff; #endif -¤³¤ì¤Ï¡¢¤É¤¦¤¤¤¦¤³¤È¤À¤í¤¦¡©len_cnt[] ¤Ï short ¤ÎÇÛÎó¤À¤¬¡¢¤È¤Ë¤«¤¯°Ê -²¼¤Î¤è¤¦¤Ê·×»»(len_cnt[] ¤ÎÍ×ÁǤò 1 ¥Ó¥Ã¥È¤º¤é¤·¤Ê¤¬¤é­¤¹)¤ò¤·¤Æ¤¤¤ë¡£ -ºÇ¸å¤Ë int ·¿¤Î¥µ¥¤¥º¤¬ 2 ¤Ç¤Ê¤¤¾ì¹ç 0xffff ¤ÇÏÀÍýÀѤò¤·¤Æ¤¤¤ë¤Î¤Ç 2¥Ð -¥¤¥È¤ÎÉä¹æ¤Ê¤·À°¿ô¤ò·ë²Ì¤È¤·¤ÆÍߤ·¤¤¤é¤·¤¤¡£ +これは、どういうことだろう?len_cnt[] は short の配列だが、とにかく以 +下のような計算(len_cnt[] の要素を 1 ビットずらしながら足す)をしている。 +最後に int 型のサイズが 2 でない場合 0xffff で論理積をしているので 2バ +イトの符号なし整数を結果として欲しいらしい。 ---------------------------------------------------------------------------- f e d c b a 9 8 7 6 5 4 3 2 1 0 bit - len_cnt[16] |x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x| (²¼°Ì 16¥Ó¥Ã¥È) -+ len_cnt[15] |x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|0| (²¼°Ì 15¥Ó¥Ã¥È) -+ len_cnt[14] |x|x|x|x|x|x|x|x|x|x|x|x|x|x|0|0| (²¼°Ì 14¥Ó¥Ã¥È) + len_cnt[16] |x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x| (下位 16ビット) ++ len_cnt[15] |x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|0| (下位 15ビット) ++ len_cnt[14] |x|x|x|x|x|x|x|x|x|x|x|x|x|x|0|0| (下位 14ビット) + : : -+ len_cnt[ 2] |x|x|0|0|0|0|0|0|0|0|0|0|0|0|0|0| (²¼°Ì 2 ¥Ó¥Ã¥È) -+ len_cnt[ 1] |x|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| (²¼°Ì 1 ¥Ó¥Ã¥È) ++ len_cnt[ 2] |x|x|0|0|0|0|0|0|0|0|0|0|0|0|0|0| (下位 2 ビット) ++ len_cnt[ 1] |x|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| (下位 1 ビット) & 0xffff |1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1| ------------------------------------------------ = cum x x x x x x x x x x x x x x x x ---------------------------------------------------------------------------- -¤³¤³¤Ç¡¢len_cnt[] ¤Î³ÆÍ×ÁǤÎÃͤ¬³ÆÁؤÎÍդοô¤Ç¤¢¤ë¤³¤È¤ò¹Í¤¨¤ë¤È¡¢³ÆÍ× -ÁǤǻÈÍѤ¹¤ë¥Ó¥Ã¥È¿ô¤È¤Î´ØÏ¢¤¬¸«¤¨¤ë¡£ +ここで、len_cnt[] の各要素の値が各層の葉の数であることを考えると、各要 +素で使用するビット数との関連が見える。 - ¼è¤êÆÀ¤ë - ÃͤÎÈÏ°Ï »ÈÍѥӥåȿô + 取り得る + 値の範囲 使用ビット数 ----------------------------------------- - len_cnt[16] 0.. 2^16°Ê¾å 17¥Ó¥Ã¥È°Ê¾å - len_cnt[15] 0.. 2^15 16¥Ó¥Ã¥È - len_cnt[14] 0.. 2^14 15¥Ó¥Ã¥È + len_cnt[16] 0.. 2^16以上 17ビット以上 + len_cnt[15] 0.. 2^15 16ビット + len_cnt[14] 0.. 2^14 15ビット : - len_cnt[ 3] 0.. 2^3 4 ¥Ó¥Ã¥È - len_cnt[ 2] 0.. 2^2 3 ¥Ó¥Ã¥È - len_cnt[ 1] 0.. 2^1 2 ¥Ó¥Ã¥È + len_cnt[ 3] 0.. 2^3 4 ビット + len_cnt[ 2] 0.. 2^2 3 ビット + len_cnt[ 1] 0.. 2^1 2 ビット -Àè¤Î·×»»¼°¤Ç¤Ï len_cnt[] ¤Î³ÆÍ×ÁǤǻÈÍѤ·ÆÀ¤ë¥Ó¥Ã¥È¿ô¤«¤é 1 °ú¤¤¤¿¥Ó¥Ã -¥È¿ô¤¬·×»»¤Ë»ÈÍѤµ¤ì¤Æ¤¤¤ë¡£Î㤨¤Ðº¬¤Î»Ò¤¬¤¹¤Ù¤ÆÍդʤé len_cnt[1] ¤Ï¡¢ -2 ¤Ë¤Ê¤ê¡¢2¿Ê¤Ç¡¢00000000 00000010 ¤À¤¬¡¢cum ¤Î·×»»¤Ë¤Ï¤³¤Î²¼°Ì 1 ¥Ó¥Ã -¥È¤·¤«»ÈÍѤµ¤ì¤Ê¤¤¡£ +先の計算式では len_cnt[] の各要素で使用し得るビット数から 1 引いたビッ +ト数が計算に使用されている。例えば根の子がすべて葉なら len_cnt[1] は、 +2 になり、2進で、00000000 00000010 だが、cum の計算にはこの下位 1 ビッ +トしか使用されない。 /\ a b .. len_cnt[1] = 00000000 00000010 @@ -3137,8 +3137,8 @@ count_len() v cum = x0000000 00000000 -º¬¤Î¹¤¬¤¹¤Ù¤ÆÍդʤé len_cnt[2] ¤Ï¡¢4 ¤Ë¤Ê¤ê¡¢2¿Ê¤Ç¡¢00000000 00000100 -¤À¤¬¡¢cum ¤Î·×»»¤Ë¤Ï¤³¤Î²¼°Ì 2 ¥Ó¥Ã¥È¤·¤«»ÈÍѤµ¤ì¤Ê¤¤¡£ +根の孫がすべて葉なら len_cnt[2] は、4 になり、2進で、00000000 00000100 +だが、cum の計算にはこの下位 2 ビットしか使用されない。 / \ .. len_cnt[1] = 00000000 00000000 /\ /\ @@ -3147,10 +3147,10 @@ count_len() vv cum = xx000000 00000000 -¤³¤Î¤è¤¦¤Ë¤¢¤ëÁؤΤ¹¤Ù¤Æ¤¬ÍդǤ¢¤ë¤è¤¦¤Ê¥Ð¥é¥ó¥¹¤Î¤è¤¤¥Ï¥Õ¥Þ¥óÌÚ¤Ë -ÂФ·¤Æ¤ÏÀè¤Î·×»»·ë²Ì cum ¤Ï 0 ¤Ë¤Ê¤ë¤é¤·¤¤¡£ +このようにある層のすべてが葉であるようなバランスのよいハフマン木に +対しては先の計算結果 cum は 0 になるらしい。 -¤Þ¤¿¡¢ +また、 /\ a /\ .. len_cnt[1] = 00000000 00000001 b c .. len_cnt[2] = 00000000 00000010 @@ -3158,7 +3158,7 @@ count_len() vv cum = xx000000 00000000 -¤Î¤è¤¦¤ÊÌÚ¤ËÂФ·¤Æ¤â·×»»·ë²Ì¤Ï¥ª¡¼¥Ð¡¼¥Õ¥í¡¼¤µ¤ì cum ¤Ï 0 ¤Ë¤Ê¤ë¡£ +のような木に対しても計算結果はオーバーフローされ cum は 0 になる。 /\ a /\ .. len_cnt[1] = 00000000 00000001 @@ -3168,8 +3168,8 @@ count_len() vvv cum = xxx00000 00000000 -¤âƱÍÍ¤Ë cum ¤Ï 0 ¤À¡£·ë¶É cum ¤¬ 0 ¤Ë¤Ê¤é¤Ê¤¤ÌڤȤϤ¢¤ëÀ᤬ 1 ¤Ä¤ÎÍÕ -¤·¤«¤â¤¿¤Ê¤¤¤è¤¦¤Ê¾ì¹ç¤Ç¤¢¤ë¤é¤·¤¤¡£ +も同様に cum は 0 だ。結局 cum が 0 にならない木とはある節が 1 つの葉 +しかもたないような場合であるらしい。 /\ a /\ .. len_cnt[1] = 00000000 00000001 @@ -3179,12 +3179,12 @@ count_len() vvv cum = 11100000 00000000 -¤½¤·¤Æ¡¢¥Ï¥Õ¥Þ¥óÌڤκî¤êÊý¤«¤é¤³¤Î¤è¤¦¤Ê¤³¤È¤Ïµ¯¤³¤ê¤¨¤Ê¤¤¤Î¤Ç¤Ï¤Ê¤¤¤« -¤È»×¤¨¤ë¡£ +そして、ハフマン木の作り方からこのようなことは起こりえないのではないか +と思える。 -(C) ¤Ç¤Ï¡¢if (cum) ¤Ç¡¢¤³¤Îµ¯¤³¤ê¤¨¤Ê¤¤¥Ï¥Õ¥Þ¥óÌڤξì¹ç¤Ë¤Ê¤Ë¤«½èÍý¤ò -¹Ô¤Ã¤Æ¤¤¤ë¡£¤Þ¤Ã¤¿¤¯Ææ¤Ç¤¢¤ë¤¬¡¢¤Þ¤º¡¢¤³¤Î (C) ¤òÆüì¾ò·ï¤È¤ß¤Ê¤·¤ÆÀè -¤Ë (D) ¤ÎÊý¤ò¸«¤ë¤³¤È¤Ë¤·¤è¤¦¡£ +(C) では、if (cum) で、この起こりえないハフマン木の場合になにか処理を +行っている。まったく謎であるが、まず、この (C) を特殊条件とみなして先 +に (D) の方を見ることにしよう。 /* (D) */ /* make len */ @@ -3196,31 +3196,31 @@ count_len() } } -sort ¤Ï²¿¤«¤È¤¤¤¦¤È¡¢make_tree() ¤Î°ú¿ô¤ËÅϤµ¤ì¤¿ codeparm ¤ò»Ø¤·¤Æ¤¤ -¤ë¡£¤³¤ÎÇÛÎó¤ÎÃæ¤Ë¤Ï(¥Ï¥Õ¥Þ¥óÌÚ¤ò¹½ÃÛ¤¹¤ëºÝ¤ËÀßÄꤵ¤ì¤Æ¤¤¤¿¤Î¤À¤¬)¡¢½Ð -¸½ÉÑÅÙ¤ÎÄ㤤½ç¤Çʿʸ¤Îʸ»ú¥³¡¼¥É¤¬Æþ¤Ã¤Æ¤¤¤ë¡£make_tree() ¤Ç¡¢sort ¤Ë -ÃͤòÀßÄꤹ¤ëºÝ¡¢ +sort は何かというと、make_tree() の引数に渡された codeparm を指してい +る。この配列の中には(ハフマン木を構築する際に設定されていたのだが)、出 +現頻度の低い順で平文の文字コードが入っている。make_tree() で、sort に +値を設定する際、 if (j < n) *sort++ = j; -¤Î¤è¤¦¤Ë¾ò·ïȽÃǤ¬¤¢¤Ã¤¿¤Î¤Ç¡¢sort[] ¤Ë¤Ï¥Ï¥Õ¥Þ¥óÌÚ¤ÎÀá¤ÏÆþ¤Ã¤Æ¤¤¤Ê¤¤¡£ -¤½¤·¤Æ¥Ï¥Õ¥Þ¥óÌڤϤ½¤Î¹½ÃۤλÅÊý¤«¤é½Ð¸½ÉÑÅÙ¤ÎÄ㤤ʸ»ú¤ÏÌڤΤè¤ê¿¼¤¤¾ì -½ê¤Ë°ÌÃ֤Ť±¤é¤ì¤Æ¤¤¤ë¡£¤³¤ì¤é¤Î¤³¤È¤«¤é make_len()¤Çµá¤á¤è¤¦¤È¤·¤Æ¤¤ -¤ë¤â¤Î¤¬²¿¤Ê¤Î¤«¤¬¤ï¤«¤ë¡£make_len() ¤Ï¡¢ - len[ʸ»ú] = ¥Ï¥Õ¥Þ¥óÌڤο¼¤µ -¤È¤¤¤Ã¤¿Âбþɽ¤òºîÀ®¤¹¤ë½èÍý¤À¡£¤µ¤é¤Ë¸À¤¦¤È¥Ï¥Õ¥Þ¥óÌڤο¼¤µ¤Ïʸ»ú¤òÉä -¹æ²½¤·¤¿·ë²Ì¤Î¥Ó¥Ã¥È¿ô¤òɽ¤¹¤³¤È¤«¤é - lenparm[ʸ»ú] = Éä¹æ¸ì¤Î¥Ó¥Ã¥È¿ô -¤È¤¤¤Ã¤¿Âбþɽ¤òºîÀ®¤¹¤ë½èÍý¤Ç¤¢¤ë¤È¸À¤Ã¤¿Êý¤¬Àµ²ò¤À¤í¤¦¡£ -len[] ¤Ï¡¢make_tree() ¤ÎËÁƬ¤Ç¡¢lenparm ¤ò»Ø¤¹¤è¤¦¤ËÀßÄꤵ¤ì¤¿ÊÑ¿ô¤Ê¤Î -¤Ç¡¢¤½¤Î¤è¤¦¤ËÃÖ¤­´¹¤¨¤Æ¤ª¤¤¤¿¡£ - -¤Ç¤Ï¡¢Ææ¤Î (C) ¤ò¸«¤è¤¦¡£¤½¤ÎÁ°¤Ë cum != 0 ¤Ïµ¯¤³¤ê¤¨¤Ê¤¤¤È½ñ¤¤¤¿¤¬¤è -¤¯¹Í¤¨¤¿¤é len_cnt[16] ¤À¤±¤Ï¿¼¤µ16°Ê¾å¤ÎÍÕ¤¹¤Ù¤Æ¤Î¿ô¤ò·×¾å¤·¤Æ¤¤¤ë¤¿ -¤á¡¢¤É¤Î¤è¤¦¤ÊÃͤ⤢¤êÆÀ¤ë¡£¤Ä¤Þ¤ê¡¢¤³¤Î (C) ¤Î½èÍý¤Ï¥Ï¥Õ¥Þ¥óÌÚ¤¬¿¼¤µ -17 °Ê¾å¤Ë¤Ê¤Ã¤¿¤È¤­¤Ë½èÍý¤µ¤ì¤ë¤â¤Î¤À¤È¸À¤¨¤½¤¦¤À¡£»×¤¤Àڤäƿ޼¨¤·¤è -¤¦Î㤨¤Ð¤³¤ó¤ÊÌڤϡ¢(C)¤Î½èÍýÂоݤȤʤ롣 +のように条件判断があったので、sort[] にはハフマン木の節は入っていない。 +そしてハフマン木はその構築の仕方から出現頻度の低い文字は木のより深い場 +所に位置づけられている。これらのことから make_len()で求めようとしてい +るものが何なのかがわかる。make_len() は、 + len[文字] = ハフマン木の深さ +といった対応表を作成する処理だ。さらに言うとハフマン木の深さは文字を符 +号化した結果のビット数を表すことから + lenparm[文字] = 符号語のビット数 +といった対応表を作成する処理であると言った方が正解だろう。 +len[] は、make_tree() の冒頭で、lenparm を指すように設定された変数なの +で、そのように置き換えておいた。 + +では、謎の (C) を見よう。その前に cum != 0 は起こりえないと書いたがよ +く考えたら len_cnt[16] だけは深さ16以上の葉すべての数を計上しているた +め、どのような値もあり得る。つまり、この (C) の処理はハフマン木が深さ +17 以上になったときに処理されるものだと言えそうだ。思い切って図示しよ +う例えばこんな木は、(C)の処理対象となる。 /\ a /\ .. len_cnt[ 1] = 0000000000000001 @@ -3243,11 +3243,11 @@ len[] vvvvvvvvvvvvvvvv cum = 0000000000000001 -¤³¤Î¤è¤¦¤ÊÌÚ¤ÏÎ㤨¤Ð³Æʸ»ú¤¬°Ê²¼¤Î½Ð¸½ÉÑÅ٤Ȥʤë¥Õ¥¡¥¤¥ë¤òºîÀ®¤¹¤ë¤Èµ¯ -¤³¤ë(¼ÂºÝ¤Ë¤Ï¡¢LHA ¤Î¾ì¹ç¡¢slide ¼­½ñË¡¤Î½èÍý¤â¤¢¤ë¤Î¤Ç¤³¤ì¤Û¤Éñ½ã¤Ç -¤Ï¤Ê¤¤)¡£ +このような木は例えば各文字が以下の出現頻度となるファイルを作成すると起 +こる(実際には、LHA の場合、slide 辞書法の処理もあるのでこれほど単純で +はない)。 - ʸ»ú ÉÑÅ٠ʸ»ú ÉÑÅÙ + 文字 頻度 文字 頻度 ------------ ------------ r 1 i 256 q 1 h 512 @@ -3259,7 +3259,7 @@ len[] k 64 b 32768 j 128 a 65536 -¤È¤³¤í¤Ç¡¢cum ¤ÎÃͤϲ¿¤Ê¤Î¤«¤È¤¤¤¦¤È¡¢ +ところで、cum の値は何なのかというと、 : .. len_cnt[15] = 0000000000000001 @@ -3268,7 +3268,7 @@ len[] r s vvvvvvvvvvvvvvvv cum = 0000000000000010 -¤³¤Î¾ì¹ç¤Ï cum = 2 ¤À¡£ +この場合は cum = 2 だ。 : .. len_cnt[15] = 0000000000000001 p /\ .. len_cnt[16] = 0000000000000101 @@ -3276,10 +3276,10 @@ len[] r /\ vvvvvvvvvvvvvvvv s t cum = 0000000000000011 -¤³¤Î¾ì¹ç¤Ï cum = 3 ¤À¡£¾¯¤Ê¤¯¤È¤â¤³¤ÎÎã¤Ç¤Ï¿¼¤µ 16 °Ê¾å¤ÎÍդοô - 2¤Ë -¤Ê¤ë¤é¤·¤¤(¤½¤¦¤«¡¢11111111 11111110 = -2 ¤ò­¤·¤Æ¤¤¤ë¤Î¤À¤«¤éÅöÁ³¤À)¡£ +この場合は cum = 3 だ。少なくともこの例では深さ 16 以上の葉の数 - 2に +なるらしい(そうか、11111111 11111110 = -2 を足しているのだから当然だ)。 -¤Ç¤Ï¡¢º£ÅÙ¤³¤½ (C) ¤ò¸«¤ë¡£ +では、今度こそ (C) を見る。 /* (C) */ /* adjust len */ @@ -3297,24 +3297,24 @@ len[] } while (--cum); } -¤Ç¤¢¤ë¡£¤¤¤­¤Ê¤ê fprintf() ¤·¤Æ¤¤¤ë¤È¤³¤í¤¬¤¹¤´¤¤¡£¥Ç¥Ð¥Ã¥°ÍѤνÐÎϤ¬ -»Ä¤Ã¤Æ¤¤¤ë¤Î¤À¤í¤¦¡£LHa for UNIX ¤Ç 17 ¤È¤¤¤¦½ÐÎϤò¸«¤¿¤³¤È¤¬¤¢¤ë¿Í¤Ï -¶µ¤¨¤ÆÍߤ·¤¤¡£ +である。いきなり fprintf() しているところがすごい。デバッグ用の出力が +残っているのだろう。LHa for UNIX で 17 という出力を見たことがある人は +教えて欲しい。 -¤Ç¡¦¡¦¡¦¡¢·ë¶É¤³¤Î (C) ¤ÎÉôʬ¤Ï¸«¤Æ¤â¤è¤¯¤ï¤«¤é¤Ê¤«¤Ã¤¿¡£¤³¤³¤Þ¤Ç½ñ¤¤ -¤Æ¤ª¤¤¤Æ¤Ê¤ó¤À¤¬¡¢µ¤»ý¤Á¤è¤¯Ìµ»ë¤¹¤ë¤³¤È¤Ë¤·¤¿¡£ +で・・・、結局この (C) の部分は見てもよくわからなかった。ここまで書い +ておいてなんだが、気持ちよく無視することにした。 -¤Ç¤Ï¡¢make_tree() ¤«¤é¸Æ¤Ó½Ð¤µ¤ì¤ëºÇ¸å¤Î´Ø¿ô¡¢maketree.c:make_code() -¤ò¸«¤è¤¦¡£make_code() ¤Ï¡¢make_tree() ¤Î(F) ¤ÎÉôʬ¤Ç°Ê²¼¤Î¤è¤¦¤Ë¸Æ¤Ð¤ì -¤Æ¤¤¤¿¡£ +では、make_tree() から呼び出される最後の関数、maketree.c:make_code() +を見よう。make_code() は、make_tree() の(F) の部分で以下のように呼ばれ +ていた。 make_code(nparm, lenparm, codeparm); -¤³¤Î°ú¿ô¤Î¤¦¤Á¡¢lenparm[] ¤ÏÀè¤Û¤É¤Î make_len[] ¤ÇÃͤ¬ºî¤é¤ì¤¿¤â¤Î¤À¤¬¡¢ - lenparm[ʸ»ú] = Éä¹æ¸ì¤Î¥Ó¥Ã¥È¿ô -¤È¤¤¤¦Âбþɽ¤À¤Ã¤¿¡£codeparm[] ¤Ï¡¢Àè¤Û¤É¤â½Ð¤¿¤¬ make_tree() ¤ÎÃæ¤ÇÀß -Äꤵ¤ì¤Æ¤¤¤ë¤â¤Î¤À¤Ã¤¿¡£½Ð¸½ÉÑÅÙ¤ÎÄ㤤½ç¤Çʿʸ¤Îʸ»ú¥³¡¼¥É¤¬Æþ¤Ã¤¿ÇÛÎó -¤À¡£ +この引数のうち、lenparm[] は先ほどの make_len[] で値が作られたものだが、 + lenparm[文字] = 符号語のビット数 +という対応表だった。codeparm[] は、先ほども出たが make_tree() の中で設 +定されているものだった。出現頻度の低い順で平文の文字コードが入った配列 +だ。 void make_code(n, len, code) @@ -3341,23 +3341,23 @@ make_code(n, len, code) } } -# ¸å¤Çµ¤¤¬¤Ä¤¤¤¿¤³¤È¤À¤¬¡¢¤¢¤é¤«¤¸¤áÀßÄꤷ¤Æ¤¤¤¿ codeparm[] ¤ÎÆâÍƤϤ³ -# ¤³¤Ç¤Ï»ÈÍѤµ¤ì¤Ê¤¤¡£¤Ä¤Þ¤ê¡¢codeparm[] ¤ÏÀèÄø¤Ï¥ï¡¼¥¯ÍѤΥХåե¡¤È -# ¤·¤ÆÍøÍѤµ¤ì¤Æ¤¤¤¿¤¬¡¢¤³¤³¤Ç¤Î codeparm[] ¤Ï½èÍý·ë²Ì¤òɽ¤¹¤È¤¤¤¦Æó¤Ä -# ¤ÎÌò³ä¤¬¤¢¤ë¡£¤³¤ì¤Ï¡¢Îΰè¤òÀáÌ󤹤뤿¤á¤ÎÊÑ¿ô¤Î»È¤¤²ó¤·¤À¡£ +# 後で気がついたことだが、あらかじめ設定していた codeparm[] の内容はこ +# こでは使用されない。つまり、codeparm[] は先程はワーク用のバッファと +# して利用されていたが、ここでの codeparm[] は処理結果を表すという二つ +# の役割がある。これは、領域を節約するための変数の使い回しだ。 -ºÇ½é¤Î for ʸ¤Ç¤Ï¡¢ÊÑ¿ô i ¤ËÂФ·¤Æ¡¢weight[i] ¤¬²¼¤Î¤è¤¦¤ËÀßÄꤵ¤ì¤ë +最初の for 文では、変数 i に対して、weight[i] が下のように設定される weight[i=1..16] = 2^(16-i) -¤½¤·¤Æ¡¢start[i] ¤Ï¡¢ +そして、start[i] は、 start[1] = 0 start[n] = start[n-1] + weight[n-1] * len_cnt[n-1] (n > 1) -¤È¤¤¤¦Á²²½¼°¤À¡£start[] ¤Îź»ú i ¤Ï¡¢len_cnt[i](¿¼¤µ i ¤ÎÍդοô)¤Îź»ú -¤Ç¤â¤¢¤ë¤³¤È¤«¤é¡¢¥Ï¥Õ¥Þ¥óÌڤο¼¤µ¤òɽ¤·¤Æ¤¤¤ë¡£start ¤¬¼ÂºÝ¤Ë¤É¤Î¤è¤¦ -¤ÊÃͤò¼è¤ë¤«¤È¸À¤¦¤È¡¢Î㤨¤Ð len_cnt[i] ¤Î³ÆÍ×ÁǤ¬ Li ¤Ç¤¢¤Ã¤¿¾ì¹ç¡¢ +という漸化式だ。start[] の添字 i は、len_cnt[i](深さ i の葉の数)の添字 +でもあることから、ハフマン木の深さを表している。start が実際にどのよう +な値を取るかと言うと、例えば len_cnt[i] の各要素が Li であった場合、 i len_cnt[i] weight[i] start[i] -------------------------------------------- @@ -3366,7 +3366,7 @@ make_code(n, len, code) 3 L3 2^13 2^15 * L1 + 2^14 * L2 4 L4 2^12 2^15 * L1 + 2^14 * L2 + 2^13 * L3 -¤³¤ó¤Ê´¶¤¸¤À¡£¤³¤ì¤Ï¤¤¤Ã¤¿¤¤²¿¤À¤í¤¦¤«¡©Â³¤¯ for ʸ¤ò¸«¤Æ¤ß¤è¤¦¡£ +こんな感じだ。これはいったい何だろうか?続く for 文を見てみよう。 for (i = 0; i < n; i++) { j = len[i]; @@ -3374,9 +3374,9 @@ make_code(n, len, code) start[j] += weight[j]; } -¤³¤³¤Ç¤Î i ¤Ï¡¢0...n ¤ÎÈϰϤǤ¢¤ë¤³¤È¤«¤éʿʸ¤Îʸ»ú¤ò¼¨¤¹¡£Ê¶¤é¤ï¤·¤¤ -¤Î¤Ç¡¢i ¤Ï¡¢c ¤Ë½ñ¤­´¹¤¨¡¢j ¤ò i ¤Ë¤·¤è¤¦¡£(¤³¤ì¤Ç¡¢i ¤ÏÁ°¤Î for ʸ¤Î -i ¤ÈƱ¤¸°ÕÌ£¤Ë¤Ê¤ë) +ここでの i は、0...n の範囲であることから平文の文字を示す。紛らわしい +ので、i は、c に書き換え、j を i にしよう。(これで、i は前の for 文の +i と同じ意味になる) int c; @@ -3386,10 +3386,10 @@ i start[i] += weight[i]; } -i = len[c] ¤Ïʸ»ú c ¤Î¥Ó¥Ã¥ÈĹ¤Ç¡¢¥Ï¥Õ¥Þ¥óÌڤο¼¤µ¤ò¼¨¤¹¡£ -code[c] ¤Ë¤Ï¡¢start[i] ¤¬ÀßÄꤵ¤ì¤ë¤¬¡¢°ìÅÙ start[i] ¤¬»²¾È¤µ¤ì¤ë¤È -start[i] ¤Ï¡¢weight[i] ¤ò­¤·¤¿Ãͤ¬¼¡²ó»È¤ï¤ì¤ë¡£Î㤨¤Ð¡¢¤¢¤ëʸ»ú -a, b, c ¤¬¤½¤ì¤¾¤ì°Ê²¼¤Î¥Ï¥Õ¥Þ¥óÌÚ¤Çɽ¸½¤µ¤ì¤¿¤È¤¹¤ë¡£ +i = len[c] は文字 c のビット長で、ハフマン木の深さを示す。 +code[c] には、start[i] が設定されるが、一度 start[i] が参照されると +start[i] は、weight[i] を足した値が次回使われる。例えば、ある文字 +a, b, c がそれぞれ以下のハフマン木で表現されたとする。 /\ a: 0 a /\ b: 10 @@ -3407,7 +3407,7 @@ a, b, c b 2 2 2 2^14 2^15 c 2 2 2 2^14 2^15 + 2^14 -¤³¤ó¤Ê´¶¤¸¤Ë¤Ê¤ë¡£Ê̤ΥϥեޥóÌڤξì¹ç¤â¸«¤Æ¤ß¤è¤¦¡£ +こんな感じになる。別のハフマン木の場合も見てみよう。 /\ a: 00 /\ /\ b: 01 @@ -3427,8 +3427,8 @@ a, b, c c 2 2 4 2^14 2^14 * 2 d 2 2 4 2^14 2^14 * 3 -¤³¤ì¤Ç¡¢¥Ô¥ó¥ÈÍè¤ë¤ÈÀ¨¤¤¤Î¤À¤¬ code[c] ¤Ë¤Ïʸ»ú c ¤ËÂбþ¤¹¤ëÉä¹æ²½¤·¤¿ -¥Ó¥Ã¥ÈÎó¤¬ÀßÄꤵ¤ì¤ë¤è¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤ë¤³¤È¤Ëµ¤¤Å¤¯¤À¤í¤¦¤«¡©¤Ä¤Þ¤ê¤Ï¡¢ +これで、ピント来ると凄いのだが code[c] には文字 c に対応する符号化した +ビット列が設定されるようになっていることに気づくだろうか?つまりは、 c len[c] i len_cnt[i] weight[i] code[c] ----------------------------------------------------------- @@ -3436,33 +3436,33 @@ a, b, c b 2 2 4 2^14 01000000 00000000 c 2 2 4 2^14 10000000 00000000 d 2 2 4 2^14 11000000 00000000 - ^^ <- ¥Ï¥Õ¥Þ¥óÉä¹æ - -¤À¡£°Ê¹ß¡¢code[] (¼ÂºÝ¤Ë¤Ï codeparm) ¤òÍøÍѤ¹¤ë¤³¤È¤Çɽ°ú¤­¤Çʸ»ú c ¤Ë -Âбþ¤¹¤ë¥Ï¥Õ¥Þ¥óÉä¹æ¤òÆÀ¤ë¤³¤È¤¬¤Ç¤­¤ë¤è¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤ë(code[c]¤Î¤¦¤Á¾å -°Ì len[c] ¥Ó¥Ã¥È¤À¤±¤ò¸«¤ë)¡£Éä¹æ²½¤ÎºÝ¤ËÌÚ¤òé¤ëɬÍפϤʤ¯¡¢¹â®¤ÊÉä¹æ -²½¤¬²Äǽ¤Ë¤Ê¤ë(¤È´üÂÔ¤µ¤ì¤ë¡£¤É¤ÎÄøÅÙ¸ú²Ì¤¬¤¢¤ë¤«¤Ï¤¤¤º¤ì¸¡¾Ú¤·¤Æ¤ß¤¿ -¤¤¡£¤½¤¦¤¤¤¨¤Ð¡¢ÍÕ¤«¤éº¬¤Ë¸þ¤«¤Ã¤ÆÌÚ¤òé¤ë¤¿¤á¤Î¾ðÊó¤¬É¬Íפʤ«¤Ã¤¿¤³¤È -¤â¤³¤ì¤Ç¤ï¤«¤Ã¤¿)¡£ - -·ë¶É make_tree(nparm, freqparm, lenparm, codeparm) ¤Ï¡¢lenparm[c] ¤È -codeparm[c] ¤òºîÀ®¤¹¤ë´Ø¿ô¤À¤Ã¤¿¤ï¤±¤À(¥Ï¥Õ¥Þ¥óɽ¤È¤Ç¤â¸À¤¦¤Î¤À¤í¤¦ -¤«¡©)¡£¼Â¤Ï¡¢¤³¤Î¤³¤È¤Ï make_tree() ¤ò¸Æ¤Ó½Ð¤·¡¢ codeparm ¤ò»ÈÍѤ·¤Æ¤¤ -¤ë²Õ½ê(huf.c)¤ò¸«¤ë¤Þ¤Ç¤Þ¤ë¤Ç¤ï¤«¤é¤Ê¤«¤Ã¤¿¡£¤·¤«¤â¡¢¤Þ¤À¤Á¤ã¤ó¤ÈÍý²ò -¤·¤¿¤ï¤±¤Ç¤Ï¤Ê¤¤¡£ - -¤Õ¤È»×¤Ã¤¿¤Î¤À¤¬¡¢¾åµ­¤ÎɽºîÀ®¤Ïʸ»ú¥³¡¼¥É½ç¤Ë°Í¸¤·¤Æ¤¤¤ë(¥³¡¼¥É¤Î¼ã -¤¤Êý¤¬º¸¤Î»Ò¤Ë¤Ê¤ë)¤¬¡¢ÌÚ¤òºîÀ®¤¹¤ë¾ì¹ç¤Ï¤½¤Î¤è¤¦¤Ê¤³¤È¤Ï¤Ê¤«¤Ã¤¿¤Ï¤º -¤À¡£¥Ï¥Õ¥Þ¥óÌÚ¤òé¤Ã¤¿¾ì¹ç¤Èɽ¤ò»²¾È¤·¤¿¾ì¹ç¤È¤Ç¤ÏÆÀ¤é¤ì¤ëÉä¹æ¸ì¤Ï°Û¤Ê -¤ë¤Î¤Ç¤Ï¤Ê¤¤¤À¤í¤¦¤«¡©¤È¤¤¤¦¤³¤È¤Ï°µ½Ìʸ¤ËËä¤á¹þ¤Þ¤ì¤ë¤Î¤ÏÌڤǤϤʤ¯¤³ -¤Îɽ¤ÎÊý¤À¤È¤¤¤¦¤³¤È¤âÁÛÁü¤¬¤Ä¤¯¡£Ìڤι½Â¤¤òɽ¤¹ left[]¡¢right[] ¤Ï¥° -¥í¡¼¥Ð¥ëÊÑ¿ô¤À¤¬¡¢¼ÂºÝ¤Ë¤Ï make_tree() Æâ¤Ç¤·¤«»È¤ï¤ì¤Ê¤¤¤Î¤«¤âÃΤì¤Ê -¤¤(¾¯¤Ê¤¯¤È¤âÉä¹æ²½¤Ë´Ø¤·¤Æ¤Ï¤½¤Î¤è¤¦¤À¡£huf.c ¤òį¤á¤ë¤È¤É¤¦¤ä¤éÉü¹æ -»þ¤Ï left[]¡¢right[]¤Ï»È¤ï¤ì¤ë¤é¤·¤¤)¡£ - -¤µ¤é¤Ë¤Õ¤È»×¤¤ÉÕ¤¤¤¿¡£Ææ¤Î (C) ¤Î¥³¡¼¥É¤À¤¬ ¿¼¤µ 17 °Ê¾å¤ÎÌÚ¤¬ºîÀ®¤µ¤ì -¤¿¾ì¹ç¤ËÌÚ¤òºÆ¹½ÃÛ¤¹¤ë½èÍý¤À¤È¤¤¤¦¤³¤È¤¬¤ï¤«¤Ã¤¿¡£ºÇ½é¡¢len_cnt[] (¤¢ -¤ë¿¼¤µ¤ÎÍդοô) ¤À¤±¤¬¡¢Áàºî¤µ¤ì¤Æ¤¤¤¿¤«¤é¤è¤¯¤ï¤«¤é¤Ê¤«¤Ã¤¿¤Î¤À¡¢ + ^^ <- ハフマン符号 + +だ。以降、code[] (実際には codeparm) を利用することで表引きで文字 c に +対応するハフマン符号を得ることができるようになっている(code[c]のうち上 +位 len[c] ビットだけを見る)。符号化の際に木を辿る必要はなく、高速な符号 +化が可能になる(と期待される。どの程度効果があるかはいずれ検証してみた +い。そういえば、葉から根に向かって木を辿るための情報が必要なかったこと +もこれでわかった)。 + +結局 make_tree(nparm, freqparm, lenparm, codeparm) は、lenparm[c] と +codeparm[c] を作成する関数だったわけだ(ハフマン表とでも言うのだろう +か?)。実は、このことは make_tree() を呼び出し、 codeparm を使用してい +る箇所(huf.c)を見るまでまるでわからなかった。しかも、まだちゃんと理解 +したわけではない。 + +ふと思ったのだが、上記の表作成は文字コード順に依存している(コードの若 +い方が左の子になる)が、木を作成する場合はそのようなことはなかったはず +だ。ハフマン木を辿った場合と表を参照した場合とでは得られる符号語は異な +るのではないだろうか?ということは圧縮文に埋め込まれるのは木ではなくこ +の表の方だということも想像がつく。木の構造を表す left[]、right[] はグ +ローバル変数だが、実際には make_tree() 内でしか使われないのかも知れな +い(少なくとも符号化に関してはそのようだ。huf.c を眺めるとどうやら復号 +時は left[]、right[]は使われるらしい)。 + +さらにふと思い付いた。謎の (C) のコードだが 深さ 17 以上の木が作成され +た場合に木を再構築する処理だということがわかった。最初、len_cnt[] (あ +る深さの葉の数) だけが、操作されていたからよくわからなかったのだ、 /* (C) */ /* adjust len */ @@ -3480,8 +3480,8 @@ codeparm[c] } while (--cum); } -¿¼¤µ i ¤ÎÍդοô¤ò 1 ¤Ä¸º¤é¤·¤Æ¡¢¤½¤Î²¼¤ÎÍդοô¤ò 2 ­¤·¤Æ¤¤¤ë¡£ -¤³¤ì¤¬¡¢cum ¤Î¿ô¤À¤±·«¤êÊÖ¤µ¤ì¤ë¡£Î㤨¤Ð¡¢Á°¤Ë¤â½Ð¤¿ +深さ i の葉の数を 1 つ減らして、その下の葉の数を 2 足している。 +これが、cum の数だけ繰り返される。例えば、前にも出た : n /\ .. len_cnt[14] = 0000000000000001 @@ -3491,7 +3491,7 @@ codeparm[c] vvvvvvvvvvvvvvvv cum = 0000000000000001 -¤ÎÎã¤Ç¤Ï¡¢ºÇ½é¤Ë len_cnt[16] ¤«¤é cum {1} ¤¬°ú¤«¤ì¡¢ +の例では、最初に len_cnt[16] から cum {1} が引かれ、 : n /\ .. len_cnt[14] = 0000000000000001 @@ -3499,7 +3499,7 @@ codeparm[c] p / .. len_cnt[16] = 0000000000000010 q -³¤¤¤Æ¡¢¿¼¤µ 15 ¤è¤ê¾å¤ÎÍդΤ¢¤ëÀᤫ¤é 1 ¤Ä»Ò¤ò¼è¤ê¡¢ +続いて、深さ 15 より上の葉のある節から 1 つ子を取り、 : n /\ .. len_cnt[14] = 0000000000000001 @@ -3507,7 +3507,7 @@ codeparm[c] p / .. len_cnt[16] = 0000000000000010 q -²¼¤ÎÍդοô(¤³¤ÎÎã¤Ç¤Ï¡¢len_cnt[16])¤ò 2 ­¤·¤Æ¤¤¤ë¡£ +下の葉の数(この例では、len_cnt[16])を 2 足している。 / \ n / \ .. len_cnt[14] = 0000000000000001 @@ -3515,27 +3515,27 @@ codeparm[c] o r p / .. len_cnt[16] = 0000000000000100 q -cum ¤Ï¡¢¤³¤ÎÎã¤Ç¤Ï 0 ¤Ë¤Ê¤ë¤Î¤Ç¡¢¤³¤ì¤ÇÌÚ¤ÎÊ¿³ê²½¤Ï½ª¤ë¡£¥Æ¥­¥¹¥È¤À¤È -¤Á¤ç¤Ã¤È¸«¤Ë¤¯¤¤¤¬¡¢¤½¤¦¤¤¤¦½èÍý¤È¤¤¤¦¤³¤È¤Ç´Ö°ã¤¤¤Ê¤¤¤À¤í¤¦¡£ -lenparm[] ¤ÎÃͤϤ³¤Î¸å¤Î (D) ¤Ç¡¢¤³¤ÎÌÚ¤ò¸µ¤Ë·×»»¤µ¤ì¤Æ¤¤¤ë¡£ +cum は、この例では 0 になるので、これで木の平滑化は終る。テキストだと +ちょっと見にくいが、そういう処理ということで間違いないだろう。 +lenparm[] の値はこの後の (D) で、この木を元に計算されている。 -¤È¤³¤í¤Ç¡¢ËÜÅö¤Î½ê¤Ï°Ê²¼¤Î¤è¤¦¤Êʸ»ú¤ÎÂбþ¤Ë¤Ê¤ë(ɽ¤òºîÀ®¤¹¤ë¤È¤­¤Ëʸ -»ú¥³¡¼¥É½ç¤Ë¤Ê¤Ã¤Æ¤¤¤ë¤¿¤á)¤Î¤À¤¬¡¢·ë²ÌŪ¤Ë¸µ¤ÎÌÚ¤«¤é p ¤ò´Þ¤àÀá¤ò¼è¤ê -½ü¤­ o ¤Î°ÌÃÖ¤ËÁÞÆþ¤¹¤ë½èÍý¤Ë¤Ê¤Ã¤Æ¤¤¤ë¡£¤Ê¤ó¤À¤«ÌÌÇò¤¤¡£ +ところで、本当の所は以下のような文字の対応になる(表を作成するときに文 +字コード順になっているため)のだが、結果的に元の木から p を含む節を取り +除き o の位置に挿入する処理になっている。なんだか面白い。 / \ n / \ .. len_cnt[14] = 0000000000000001 /\ /\ .. len_cnt[15] = 0000000000000000 o p q r .. len_cnt[16] = 0000000000000100 -ʸ»ú¤«¤é Huffman Éä¹æ¤¬ÆÀ¤é¤ì¤ë¤è¤¦¤Ë¤Ê¤Ã¤¿¤Î¤Ç¡¢°µ½Ì½èÍý¤ò¹Ô¤¦Æ»¶ñ¤Ï -·¤Ã¤¿¡£¤¤¤è¤¤¤è Huffman Ë¡¤Ë¤è¤ë°µ½Ì½èÍýÁ´ÈÌ (huf.c) ¤ò¸«¤ë¤³¤È¤Ë¤·¤è -¤¦¡£ +文字から Huffman 符号が得られるようになったので、圧縮処理を行う道具は +揃った。いよいよ Huffman 法による圧縮処理全般 (huf.c) を見ることにしよ +う。 -¤Þ¤º huf.c ¤ÇÄêµÁ¤µ¤ì¤Æ¤¤¤ë¥Ç¡¼¥¿¹½Â¤¤«¤é³Îǧ¤·¤è¤¦¡£¥Ç¡¼¥¿¹½Â¤¤¬¤ï¤«¤Ã -¤Æ¤·¤Þ¤¨¤Ð¥¢¥ë¥´¥ê¥º¥à¤Î 90% ¤Ï¤ï¤«¤Ã¤¿¤âƱÁ³¤À(¸ØÄ¥)¡£ +まず huf.c で定義されているデータ構造から確認しよう。データ構造がわかっ +てしまえばアルゴリズムの 90% はわかったも同然だ(誇張)。 -huf.c ¤Ë¤Ï°Ê²¼¤ÎÊÑ¿ô¤¬ÄêµÁ¤µ¤ì¤Æ¤¤¤ë¡£ +huf.c には以下の変数が定義されている。 unsigned short left[2 * NC - 1], right[2 * NC - 1]; unsigned char c_len[NC], pt_len[NPT]; @@ -3549,7 +3549,7 @@ static unsigned short output_pos, output_mask; static int pbit; static int np; -»ÈÍѤµ¤ì¤Æ¤¤¤ëÄê¿ô¤â³Îǧ¤¹¤ë lha_macro.h ¤À¡£ +使用されている定数も確認する lha_macro.h だ。 #define NP (MAX_DICBIT + 1) #define NT (USHRT_BIT + 3) @@ -3560,41 +3560,41 @@ static int np; #define NPT 0x80 #define CBIT 9 /* $\lfloor \log_2 NC \rfloor + 1$ */ -¤¿¤¯¤µ¤ó¤¢¤ë¡£¤¿¤¯¤µ¤ó¤¢¤ê¤¹¤®¤Æ¤¯¤¸¤±¤½¤¦¤À¤¬(»ö¼Â¡¢¤¯¤¸¤±¤¿)¡¢¸½»þÅÀ -¤Ç¤ï¤«¤ëÊÑ¿ô¤â¤¢¤ë¡£left[] ¤ä right[] ¤Ï Huffman ÌÚ¤ò¹½ÃÛ¤¹¤ë¤Î¤Ë»ÈÍÑ -¤·¤¿ÊÑ¿ô¤À¤Ã¤¿¡£¤½¤³¤«¤é NC ¤Ïʸ»ú¼ï¤ÎºÇÂç¿ô¤Ç¤¢¤ë¤³¤È¤¬¤ï¤«¤ë¡£NC ¤Ë -MAXMATCH{256} Åù¤ÎÃͤ¬»ÈÍѤµ¤ì¤Æ¤¤¤ë¤Î¤ÏÆæ¤À¤¬¡¢¸½»þÅÀ¤Ç¤Ï̵»ë¤·¤Æ¤ª¤³ -¤¦¡£ +たくさんある。たくさんありすぎてくじけそうだが(事実、くじけた)、現時点 +でわかる変数もある。left[] や right[] は Huffman 木を構築するのに使用 +した変数だった。そこから NC は文字種の最大数であることがわかる。NC に +MAXMATCH{256} 等の値が使用されているのは謎だが、現時点では無視しておこ +う。 -c_freq[] ¤ä c_len[], p_freq[], pt_len[] ¤â make_tree() ¤Ç½Ð¤ÆÍ褿ÊÑ¿ô -̾¤Ë»÷¤Æ¤¤¤ë¡£¤ª¤½¤é¤¯ make_tree() ¤ËÅϤ¹ÊÑ¿ô¤À¤í¤¦¡£³Îǧ¤·¤Æ¤ß¤¿¤È¤³ -¤í huf.c ¤«¤é make_tree() ¤Î¸Æ¤Ó½Ð¤·¤ò¹Ô¤Ã¤Æ¤¤¤ëÉôʬ¤òÈ´¤­½Ð¤¹¤È¡¢ +c_freq[] や c_len[], p_freq[], pt_len[] も make_tree() で出て来た変数 +名に似ている。おそらく make_tree() に渡す変数だろう。確認してみたとこ +ろ huf.c から make_tree() の呼び出しを行っている部分を抜き出すと、 root = make_tree(NC, c_freq, c_len, c_code); root = make_tree(NT, t_freq, pt_len, pt_code); root = make_tree(np, p_freq, pt_len, pt_code); -¤Î 3 ²Õ½ê¤¬½Ð¤ÆÍ褿¡£¤Ä¤Þ¤ê¡¢ +の 3 箇所が出て来た。つまり、 - ʸ»ú¼ï¤Î¿ô ʸ»ú¤Î½Ð¸½²ó¿ô Éä¹æ²½¤·¤¿Ê¸»ú ʸ»ú¤ËÂбþ¤¹¤ë - ¤Î bit Ĺ Huffman Éä¹æɽ + 文字種の数 文字の出現回数 符号化した文字 文字に対応する + の bit 長 Huffman 符号表 ----------------------------------------------------------- NC c_freq c_len c_code NT t_freq pt_len pt_code np p_freq pt_len pt_code -¤È¤¤¤¦´Ø·¸¤Î¤è¤¦¤À¡£¤É¤¦¤ä¤é c_code¡¢pt_code ¤È¤¤¤¦ 2 ¼ïÎà¤Î -Huffman Éä¹æɽ¤ò»ÈÍѤ¹¤ë¤é¤·¤¤¡£ +という関係のようだ。どうやら c_code、pt_code という 2 種類の +Huffman 符号表を使用するらしい。 -# ¤¢¤È¤Ç¤ï¤«¤ë¤³¤È¤À¤¬¼ÂºÝ¤Ï 3 ¼ïÎà¤Î Huffman Éä¹æɽ¤òºî¤Ã¤Æ¤ª¤ê -# pt_code ¤ÏÊÑ¿ô¤¬»È¤¤²ó¤·¤µ¤ì¤Æ¤¤¤ë¡£ÊÑ¿ô¤Î»ÈÍÑÎΰè¤ò¸º¤é¤· -# ¤¿¤«¤Ã¤¿¤Î¤À¤í¤¦¡£ +# あとでわかることだが実際は 3 種類の Huffman 符号表を作っており +# pt_code は変数が使い回しされている。変数の使用領域を減らし +# たかったのだろう。 -¤½¤Î¾¤ÎÊÑ¿ô¤Ë´Ø¤·¤Æ¤âͽÁÛ¤òΩ¤Æ¤¿¤¤½ê¤À¤¬¡¢¤â¤¦¤¯¤¸¤±¤¿¤Î¤Ç¡¢½èÍýÆâÍÆ -¤«¤é¹¶¤á¤ë¤³¤È¤Ë¤·¤¿¡£ +その他の変数に関しても予想を立てたい所だが、もうくじけたので、処理内容 +から攻めることにした。 -slide ¼­½ñË¡¤Î²òÆÉ¤Ç Huffman Ë¡¤Ë´ØÏ¢¤·¤¿½èÍý¤Î¸Æ¤Ó½Ð¤·¤¬¤¤¤¯¤Ä¤«¤¢¤Ã -¤¿¡£ +slide 辞書法の解読で Huffman 法に関連した処理の呼び出しがいくつかあっ +た。 /* initialize */ alloc_buf() @@ -3609,8 +3609,8 @@ slide decode_set.decode_c() decode_set.decode_p() -°Ê¾å¤À¡£lh4, 5, 6, 7 ¤Ç¤Ï¡¢¾åµ­¤Î¤½¤ì¤¾¤ì¤Ï¡¢huf.c ¤Î°Ê²¼¤Î´Ø¿ô¤Î¸Æ¤Ó -½Ð¤·¤ËÂбþ¤·¤Æ¤¤¤ë¡£¤³¤ì¤Ï¡¢slide.c ¤ÎËÁƬÉôʬ¤ÇÄêµÁ¤µ¤ì¤Æ¤¤¤ë¡£ +以上だ。lh4, 5, 6, 7 では、上記のそれぞれは、huf.c の以下の関数の呼び +出しに対応している。これは、slide.c の冒頭部分で定義されている。 /* encoder */ encode_start() -> encode_start_st1() @@ -3622,9 +3622,9 @@ slide decode_c() -> decode_c_st1() decode_p() -> decode_p_st1() -¤³¤Î¤¦¤Á¤Î°µ½Ì½èÍý¤Ë¤¢¤¿¤ëÉôʬ encode_start_st1(), output_st1(), -encode_end_st1() ¤ò¸«¤Æ¤¤¤³¤¦¡£¤Þ¤º¤Ï¡¢½é´ü²½½èÍý¤Ç¤¢¤ë -encode_start_st1() ¤«¤é¡¢ +このうちの圧縮処理にあたる部分 encode_start_st1(), output_st1(), +encode_end_st1() を見ていこう。まずは、初期化処理である +encode_start_st1() から、 void encode_start_st1( /* void */ ) @@ -3651,9 +3651,9 @@ encode_start_st1( /* void */ ) buf[0] = 0; } -dicbit (¤³¤ì¤Ï¼­½ñ¤Î bit ¥µ¥¤¥º¤À¤Ã¤¿)¤Ë¤è¤Ã¤Æ¡¢np, pbit ¤ÎÃͤ¬ÊѤï¤ë¡£ -dicbit ¤Î°ã¤¤¤È¤¤¤¦¤Î¤Ï LHa ¤Î encoding ¥á¥½¥Ã¥É¤Î°ã¤¤¤À¡£¤½¤ì¤¾¤ì°Ê²¼ -¤ÎÂбþ¤Ë¤Ê¤ë¡£ +dicbit (これは辞書の bit サイズだった)によって、np, pbit の値が変わる。 +dicbit の違いというのは LHa の encoding メソッドの違いだ。それぞれ以下 +の対応になる。 method dicbit np pbit -------------------------- @@ -3662,27 +3662,27 @@ dicbit -lh6- 15 16 5 -lh7- 16 17 5 -np ¤È¤¤¤¦¤Î¤Ï¡¢ÀèÄø make_tree() ¤ò¸Æ¤Ó½Ð¤·¤Æ¤¤¤ë²Õ½ê¤ÎÀö¤¤½Ð¤·¤Ç¸«¤«¤± -¤¿ÊÑ¿ô¤À¤Ã¤¿¡£¤Þ¤À¡¢¤³¤Î´ØÏ¢¤Ï¤è¤¯¤ï¤«¤é¤Ê¤¤¡£ +np というのは、先程 make_tree() を呼び出している箇所の洗い出しで見かけ +た変数だった。まだ、この関連はよくわからない。 -½èÍý¤Î¸åȾ¤Ç¤Ï¡¢Ê¸»ú¤Î½Ð¸½ÉÑÅÙ¤òɽ¤¹ c_freq[]¡¢p_freq[] ¤Î½é´ü²½¤ò -¹Ô¤Ã¤Æ¤¤¤ë¡£¤µ¤é¤Ë +処理の後半では、文字の出現頻度を表す c_freq[]、p_freq[] の初期化を +行っている。さらに output_pos output_mask buf[] -¤È¤¤¤¦½é½Ð¤ÎÊÑ¿ô¤â 0 ¤Ë½é´ü²½¤·¤Æ¤¤¤ë¡£(buf ¤Ï¡¢buf[0] ¤Î¤ß½é´ü²½¤·¤Æ -¤¤¤ë) init_putbits() ¤Î¸Æ¤Ó½Ð¤·¤Ï bit ½ÐÎϥ롼¥Á¥ó¤Î½é´ü²½½èÍý¤À¤Ã¤¿¡£ -°Ê¹ß¡¢putbits(), putcode() ¤ò»ÈÍѤǤ­¤ë¡£ +という初出の変数も 0 に初期化している。(buf は、buf[0] のみ初期化して +いる) init_putbits() の呼び出しは bit 出力ルーチンの初期化処理だった。 +以降、putbits(), putcode() を使用できる。 -¼¡¤Ë output_st1(c, p) ¤ò¸«¤ë¡£slide.c ¤Ç¤³¤Î´Ø¿ô¤Ï°Ê²¼¤Î¤è¤¦¤Ë»ÈÍѤµ¤ì -¤Æ¤¤¤¿¡£ +次に output_st1(c, p) を見る。slide.c でこの関数は以下のように使用され +ていた。 - output_st1(c, 0) ʸ»ú c ¤ò½ÐÎÏ - output_st1(len, off) ¤Î¥Ú¥¢¤ò½ÐÎÏ + output_st1(c, 0) 文字 c を出力 + output_st1(len, off) のペアを出力 -¤³¤Î¤³¤È¤òƧ¤Þ¤¨¤¿¾å¤Ç¡¢½èÍýÆâÍƤò¸«¤Æ¤ß¤è¤¦¡£ +このことを踏まえた上で、処理内容を見てみよう。 void output_st1(c, p) @@ -3721,14 +3721,14 @@ output_st1(c, p) } } -(A) ¤Ï¡¢output_mask ¤ÎÃͤ˱þ¤¸¤Æ½èÍý¤ò¹Ô¤¦¤è¤¦¤À¡£½é´ü²½¤Ç output_mask -¤Ï 0 ¤À¤«¤é (A) ¤Î½èÍý¤ÏºÇ½é¤«¤é¼Â¹Ô¤µ¤ì¤ë¤¬¡¢¤Ò¤È¤Þ¤ºÌµ»ë¤·¤è¤¦¡£ +(A) は、output_mask の値に応じて処理を行うようだ。初期化で output_mask +は 0 だから (A) の処理は最初から実行されるが、ひとまず無視しよう。 -(B) ¤Ï¡¢buf ¤Ë°ú¿ô¤ÇÅϤµ¤ì¤¿Ê¸»ú c ¤ò³ÊǼ¤·¡¢c_freq[c] ¤ÎÃÍ(ʸ»ú¤Î½Ð¸½ -ÉÑÅÙ)¤ò­¤·¤Æ¤¤¤ë¡£¤É¤¦¤ä¤é´ðËܤÏÅϤµ¤ì¤¿Ê¸»ú c ¤ò±ä¡¹¤È buf ¤Ë³ÊǼ¤·¡¢ -¸å¤Ç°µ½Ì¤ò¹Ô¤¦(¤ª¤½¤é¤¯ (A) ¤À¤í¤¦)¤è¤¦¤À¡£ +(B) は、buf に引数で渡された文字 c を格納し、c_freq[c] の値(文字の出現 +頻度)を足している。どうやら基本は渡された文字 c を延々と buf に格納し、 +後で圧縮を行う(おそらく (A) だろう)ようだ。 -¤³¤Î buf ¤Î¥µ¥¤¥º¤Ï¤È¸À¤¦¤È¡¢¤³¤ì¤Ï alloc_buf() ¤Ç³ä¤êÅö¤Æ¤é¤ì¤Æ¤¤¤¿¡£ +この buf のサイズはと言うと、これは alloc_buf() で割り当てられていた。 unsigned char * alloc_buf( /* void */ ) @@ -3742,15 +3742,15 @@ alloc_buf( /* void */ ) return buf; } -bufsiz ¤¬ buf ¤Î¥µ¥¤¥º¤é¤·¤¤¡£¤³¤ì¤Ï¤Ç¤­¤ë¤À¤±Â礭¤¯¼è¤ë¤è¤¦¤Ë¤·¤Æ¤¤¤ë -¤¬¡¢Â礭¤¯¼è¤ì¤Ê¤±¤ì¤Ð¤½¤ì¤Ï¤½¤ì¤ÇÎɤ¤¤è¤¦¤À¡£ +bufsiz が buf のサイズらしい。これはできるだけ大きく取るようにしている +が、大きく取れなければそれはそれで良いようだ。 -¤µ¤é¤Ë¡¢(C) ¤Î½èÍý¤ò¹Ô¤¦¤«¤É¤¦¤«¤Ï¡¢c >= (1 << CHAR_BIT) ¤È¤¤¤¦¾ò·ï¤Ç -ȽÃǤµ¤ì¤Æ¤¤¤ë¡£¤³¤Î¾ò·ï¤¬¿¿¤È¤Ê¤ë¾ì¹ç¤Ï²¿¤«¤È¸À¤¦¤È c ¤¬¡ÖŤµ¡×¤òɽ -¤¹¾ì¹ç¤À¡£¤³¤Î¤È¤­°ú¿ô p ¤Ç¡Ö°ÌÃ֡פ¬ÅϤµ¤ì¤Æ¤¤¤ë¤Î¤Ç¤³¤ì¤â buf ¤Ë¥»¥Ã -¥È¤·¤Æ¤¤¤ë¡£¤½¤Î¶ñÂÎŪÆâÍƤϤȤ¤¤¦¤È¡¢²¿¤ä¤é cpos ¤È¤¤¤¦¤³¤Î´Ø¿ôÆâ¤Ç -static ¤ÊÊÑ¿ô¤¬»ÈÍѤµ¤ì¤Æ¤¤¤ë¡£¤è¤¯¤ï¤«¤é¤Ê¤¤¤¬Ê¸»ú c ¤ä ¤Î -¥Ú¥¢¤Ï¡¢buf ¾å¤Ç°Ê²¼¤Î¤è¤¦¤Ëɽ¤µ¤ì¤ë¤é¤·¤¤¡£ +さらに、(C) の処理を行うかどうかは、c >= (1 << CHAR_BIT) という条件で +判断されている。この条件が真となる場合は何かと言うと c が「長さ」を表 +す場合だ。このとき引数 p で「位置」が渡されているのでこれも buf にセッ +トしている。その具体的内容はというと、何やら cpos というこの関数内で +static な変数が使用されている。よくわからないが文字 c や の +ペアは、buf 上で以下のように表されるらしい。 ---------------------------------------------------------------------------- @@ -3758,7 +3758,7 @@ output_st1(c1, 0) output_st1(c2, 0) output_st1(len, off) -¤È¸Æ¤Ó½Ð¤·¤¿¾ì¹ç¤Î buf ¤Î¾õÂÖ +と呼び出した場合の buf の状態 +-----+-----+-----+-----+-----+ buf | c1 | c2 | len | off | @@ -3766,7 +3766,7 @@ buf | c1 | c2 | len | off | ---------------------------------------------------------------------------- -(C) ¤Î½èÍý¤ÎºÇ¸å¤ÎÉôʬ +(C) の処理の最後の部分 c = 0; while (p) { @@ -3775,25 +3775,25 @@ buf | c1 | c2 | len | off | } p_freq[c]++; -¤Ï¡¢½Ð¸½ÉÑÅÙ p_freq[] ¤òµá¤á¤ë½èÍý¤À¤¬¡¢p_freq ¤Ï¡¢off Ãͤνи½ÉÑÅÙ¤ò -ɽ¤·¤Æ¤¤¤ë¤é¤·¤¤¡£¤³¤³¤Ç¤Î c ¤Ï¡¢p (off) ¤Î bit Ĺ¤Ë¤Ê¤Ã¤Æ¤¤¤ë¡£off ¤Î -ÃͤÏÂ礭¤¤(¼­½ñ¥µ¥¤¥º¤À¤«¤éºÇÂç(lh7)¤Ç¡¢64KB)¤Î¤Ç¡¢Âå¤ï¤ê¤Ë bit ŤòÍø -ÍѤ·¤Æ¤¤¤ë¤È¤¤¤Ã¤¿¤È¤³¤í¤«¡£¤½¤¦¤¤¤¨¤Ð¡¢np ¤È¤¤¤¦ÊÑ¿ô¤¬¤¢¤ê¡¢ -make_tree() ¤ÎÂè°ì°ú¿ô¤ËÅϤµ¤ì¤ë¤³¤È¤«¤é¡¢¤³¤ì¤Ï¡¢p_freq[] ¤ÎÍ×ÁÇ¿ô¤ò -ɽ¤¹¡£p_freq[] ¤ÎÍ×ÁÇ¿ô¤È¤Ï¡¢ ¤Î bit Ĺ¤ÎºÇÂçÃÍ+1¤Ê¤Î¤Ç¡¢lh7 ¤Ç¡¢ -64KB¡£¤Ä¤Þ¤ê 16 bit + 1 ¤¬ np ¤Ë¤Ê¤ë¡£ +は、出現頻度 p_freq[] を求める処理だが、p_freq は、off 値の出現頻度を +表しているらしい。ここでの c は、p (off) の bit 長になっている。off の +値は大きい(辞書サイズだから最大(lh7)で、64KB)ので、代わりに bit 長を利 +用しているといったところか。そういえば、np という変数があり、 +make_tree() の第一引数に渡されることから、これは、p_freq[] の要素数を +表す。p_freq[] の要素数とは、 の bit 長の最大値+1なので、lh7 で、 +64KB。つまり 16 bit + 1 が np になる。 -¤Ä¤¤¤Ç¤Ë¸À¤¦¤È¡¢¡ÖŤµ¡×¤Ï¤½¤Î¤Þ¤Þ c_freq[] ¤ÇÉÑÅÙ¤¬·×¾å¤µ¤ì¤Æ¤¤¤¿¡£Æ± -¤¸¤¯ make_tree() ¤ÎÂè°ì°ú¿ô¤ËÅϤµ¤ì¤ë NC ¤ÎÃͤ¬ +ついでに言うと、「長さ」はそのまま c_freq[] で頻度が計上されていた。同 +じく make_tree() の第一引数に渡される NC の値が #define NC (UCHAR_MAX + MAXMATCH + 2 - THRESHOLD) -¤Ê¤Î¤Ï¡¢¤½¤¦¤¤¤¦¤³¤È¤À(Ťµ¤ò¹Í¤¨¤Ê¤±¤ì¤Ðʸ»ú¤ÎºÇÂçÃÍ{255}+1¤È¤Ê¤ë¤È¤³ -¤í¤À¤¬¡¢Ä¹¤µ¤ÎºÇÂçÃͤ¬¡¢256 + MAXMATCH - THRESHOLD ¤À¤«¤é¾å¤Î¼°¤Ë¤Ê¤Ã -¤Æ¤¤¤ë¤Î¤À¤í¤¦¤È»×¤¦¡£¤Á¤ç¤Ã¤È¤ï¤«¤ê¤Ë¤¯¤¤¤¬) +なのは、そういうことだ(長さを考えなければ文字の最大値{255}+1となるとこ +ろだが、長さの最大値が、256 + MAXMATCH - THRESHOLD だから上の式になっ +ているのだろうと思う。ちょっとわかりにくいが) -¤³¤³¤Þ¤Ç¤Ç¡¢°µ½Ì¤ò¹Ô¤¦½èÍý¤Ï¸½¤ï¤ì¤Ê¤«¤Ã¤¿¡£¤ä¤Ï¤ê (A) ¤ÎÉôʬ¤¬°µ½Ì½è -Íý¤À¡£ +ここまでで、圧縮を行う処理は現われなかった。やはり (A) の部分が圧縮処 +理だ。 /* (A) */ output_mask >>= 1; @@ -3809,20 +3809,20 @@ make_tree() buf[cpos] = 0; } -ºÇ½é¡¢output_mask ¤Ï¡¢0 ¤Ê¤Î¤Ç if ¾ò·ï¤òËþ¤¿¤¹¡£¤½¤·¤Æ¡¢output_mask ¤Ï¡¢ -(1 << (CHAR_BIT - 1)) ¤Ä¤Þ¤ê¡¢128 ¤Ë¤Ê¤ë¡£(A) ¤ÎËÁƬ¤Ç¡¢>>= 1 ¤·¤Æ¤¤¤ë -¤Î¤Ç¡¢output_mask ¤Ï¡¢128, 64, 32, ..., 1, 128 ¤È¤¤¤¦Ãͤò¼è¤ë¤é¤·¤¤¡£¤½ -¤·¤Æ¡¢ËÜÅö¤Î½é´üÃÍ¤Ï 128 ¤À¡£ +最初、output_mask は、0 なので if 条件を満たす。そして、output_mask は、 +(1 << (CHAR_BIT - 1)) つまり、128 になる。(A) の冒頭で、>>= 1 している +ので、output_mask は、128, 64, 32, ..., 1, 128 という値を取るらしい。そ +して、本当の初期値は 128 だ。 -¼¡¤Î¾ò·ï +次の条件 output_pos >= bufsiz - 3 * CHAR_BIT -¤È¤¤¤¦¤Î¤Ï¡¢buf ¤¬ bufsiz - 24 ¤è¤ê¤âÂ礭¤¯¤Ê¤Ã¤¿¤È¤­¤ÎÃͤÀ¡£¤Ò¤È¤Þ¤º -̵»ë¤·¤è¤¦¡£¤½¤·¤Æ¡¢cpos = output_pos++ ¤È¤·¤Æ¡¢buf[cpos] = 0 ¤Ë¥»¥Ã¥È -¤µ¤ì¤Æ¤¤¤ë¡£¤É¤¦¤ä¤é¡¢Àè¤Ë¤³¤Á¤é¤ò¸«¤ë¤Ù¤­¤À¤Ã¤¿¤è¤¦¤À¡£cpos ¤ÎÃÍ -¤È output_pos++ ¤¬ (A) ¤Ç¹Ô¤ï¤ì¤Æ¤¤¤ë¤³¤È¤òƧ¤Þ¤¨¤Æ¤â¤¦°ìÅÙ (B)¡¢(C) -¤Î½èÍý¤ò¸«¤ë¤È¡¢buf ¤Ï°Ê²¼¤Î¤è¤¦¤Ë»ÈÍѤµ¤ì¤Æ¤¤¤ë¤é¤·¤¤¡£ +というのは、buf が bufsiz - 24 よりも大きくなったときの値だ。ひとまず +無視しよう。そして、cpos = output_pos++ として、buf[cpos] = 0 にセット +されている。どうやら、先にこちらを見るべきだったようだ。cpos の値 +と output_pos++ が (A) で行われていることを踏まえてもう一度 (B)、(C) +の処理を見ると、buf は以下のように使用されているらしい。 ---------------------------------------------------------------------------- @@ -3830,7 +3830,7 @@ output_st1(c1, 0) output_st1(c2, 0) output_st1(len, off) -¤È¸Æ¤Ó½Ð¤·¤¿¾ì¹ç¤Î buf ¤Î¾õÂÖ +と呼び出した場合の buf の状態 +-----+-----+-----+-----+-----+-----+-- @@ -3840,18 +3840,18 @@ buf | 32 | c1 | c2 | len | off | ... ---------------------------------------------------------------------------- - ¤Î¥Ú¥¢¤ò½ÐÎϤ¹¤ë¤È¤­ buf[cpos] ¤Ë¤Ï°Ê²¼¤Î¤è¤¦¤ÊÃͤ¬ÀßÄꤵ¤ì -¤Æ¤¤¤¿¤³¤È¤â¿Þ¤Ë½ñ¤¤¤Æ¤¢¤ë¡£ + のペアを出力するとき buf[cpos] には以下のような値が設定され +ていたことも図に書いてある。 buf[cpos] |= output_mask; -¤â¤¦¾¯¤·Ãí°Õ¿¼¤¯¤³¤Î¤¢¤¿¤ê¤ò¹Í¤¨¤è¤¦¡£output_mask ¤Ï¡¢¤³¤Î´Ø¿ô¤¬¸Æ¤Ð¤ì -¤ë¤¿¤Ó¤Ë 128, 64, 32, ..., 1, 128, 64, ... ¤È¤¤¤¦Ãͤˤʤ롣¤½¤·¤Æ¡¢buf -¤Ï¡¢¸Æ¤Ð¤ì¤ë¤¿¤Ó¤Ë c (1¥Ð¥¤¥È)¡¢¤¢¤ë¤¤¤Ï (3¥Ð¥¤¥È)¤ÎÃͤ¬Àß -Äꤵ¤ì¤ë¤¬¡¢output_mask ¤¬ 128 ¤Ë¤Ê¤Ã¤¿¤È¤­¤Ï¡¢Í¾Ê¬¤Ë 1 ¥Ð¥¤¥È¶õ¤­¤¬¤Ç -¤­¤ë(¤³¤ì¤Ï¡¢buf[cpos]¤Ç¼¨¤µ¤ì¤ë)¡£¤³¤Î¶õ¤­¤Ë¤Ï ¤¬ÀßÄꤵ¤ì¤ë -¤¿¤Ó¤Ë¤½¤Î»þÅÀ¤Î output_mask Ãͤ¬ÀßÄꤵ¤ì¤ë¤è¤¦¤À¡£(A) ¤¬¸Æ¤Ð¤ì¤ë¤È¤­ -¤È¸À¤¦¤Î¤Ï¡¢°ìÈֺǽé¤Î output_mask = 0 ¤Î¾ì¹ç¤ò½ü¤±¤Ð¡¢ +もう少し注意深くこのあたりを考えよう。output_mask は、この関数が呼ばれ +るたびに 128, 64, 32, ..., 1, 128, 64, ... という値になる。そして、buf +は、呼ばれるたびに c (1バイト)、あるいは (3バイト)の値が設 +定されるが、output_mask が 128 になったときは、余分に 1 バイト空きがで +きる(これは、buf[cpos]で示される)。この空きには が設定される +たびにその時点の output_mask 値が設定されるようだ。(A) が呼ばれるとき +と言うのは、一番最初の output_mask = 0 の場合を除けば、 ---------------------------------------------------------------------------- @@ -3866,11 +3866,11 @@ buf | 40 | c1 | c2 |len | off | c4 |len | off | c6 | c7 | c8 | ---------------------------------------------------------------------------- -¤³¤Î¤è¤¦¤Ê¾õÂ֤ˤʤ俤Ȥ­¤È¤¤¤¦¤³¤È¤À¡£¤µ¤é¤Ë¡¢buf[cpos] ¤Ë¤Ï¡¢ - ¤¬³ÊǼ¤µ¤ì¤Æ¤¤¤ë°ÌÃÖ¤òɽ¤·¤Æ¤¤¤ë¡£¤³¤Î¾õÂÖ¤ò 1 ¥»¥°¥á¥ó¥È¤È¸Æ -¤Ö¤³¤È¤Ë¤·¤è¤¦¡£¤½¤·¤Æ¤³¤Î¥»¥°¥á¥ó¥Èñ°Ì¤Ë¾ðÊó¤¬ buf ¤Ë³ÊǼ¤µ¤ì¡¢buf ¤¬ -¤¤¤Ã¤Ñ¤¤¤Ë¤Ê¤Ã¤¿¤é¤³¤Î¥»¥°¥á¥ó¥È¤Î½¸¤Þ¤ê¤ò 1 ¥Ö¥í¥Ã¥¯¤È¤·¤Æ (A) ¤Î½èÍý -¤Î̵»ë¤·¤¿if ʸ¤ÎÃæ¿È¤Ç +このような状態になったときということだ。さらに、buf[cpos] には、 + が格納されている位置を表している。この状態を 1 セグメントと呼 +ぶことにしよう。そしてこのセグメント単位に情報が buf に格納され、buf が +いっぱいになったらこのセグメントの集まりを 1 ブロックとして (A) の処理 +の無視したif 文の中身で if (output_pos >= bufsiz - 3 * CHAR_BIT) { send_block(); @@ -3879,34 +3879,34 @@ buf | 40 | c1 | c2 |len | off | c4 |len | off | c6 | c7 | c8 | output_pos = 0; } -¤Î¤è¤¦¤Ë send_block() ¤¬¸Æ¤Ð¤ì¤ë¤è¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤ë¤è¤¦¤À¡£¤³¤Î if ¤Î¾ò·ï -¤Ç¡¢3 * CHAR_BIT ¤È¤¤¤¦¤Î¤Ï ¤Î³ÊǼ¥Ð¥¤¥È¿ô¤ò¼¨¤·¤Æ¤¤¤ë¡£ -(¤È»×¤Ã¤¿¤¬¡¢3 * CHAR_BIT ¤Ç¤Ï¥Ó¥Ã¥È¿ô¤À¡£°ìÊý¡¢bufsiz ¤Ï¥Ð¥¤¥È¿ô¤À¡£ -·×»»¤Ë»ÈÍѤ·¤Æ¤¤¤ëñ°Ì¤¬°ã¤¦¡£²¿¤ä¤é¥Ð¥°¤Ã¤Ý¤¤Ê·°Ïµ¤¤¬¤¢¤ë¤¬¡¢¥Ð¥°¤À¤È -¤·¤Æ¤â¥Ð¥Ã¥Õ¥¡¤ò¤Á¤ç¤Ã¤È¤À¤±ÌµÂ̤ˤ·¤Æ¤¤¤ë¤À¤±¤Ê¤Î¤ÇÂ礷¤¿¤³¤È¤Ï¤Ê¤¤¤Î -¤À¤í¤¦) - -# ¤É¤¦¤ä¤é¥Ð¥°¤Ç¤Ï¤Ê¤¤¤é¤·¤¤¡£3 * CHAR_BIT ¤È¤¤¤¦¤Î¤Ï¡¢1 ¥»¥°¥á¥ó¥È¤¬ -# CHAR_BIT ¤Î¥¹¥í¥Ã¥È(8 ¤Ä¤Î¥¹¥í¥Ã¥È)¤ò»ý¤Á¡¢1 ¥»¥°¥á¥ó¥ÈÆâ¤Î¥¹¥í¥Ã¥È¤¬ -# ¤¹¤Ù¤Æ (3 bytes)¤Î¾ì¹ç¡¢ºÇÂç 3 bytes * 8 ¤È¤Ê¤ë¤³¤È¤ò¼¨¤·¤Æ -# ¤¤¤ë¤è¤¦¤À¡£ -# CHAR_BIT ¤Ï¡¢buf[cpos] ¤Î¥Ó¥Ã¥È¿ô¤òɽ¤·¤Æ¤¤¤ë¡£ +のように send_block() が呼ばれるようになっているようだ。この if の条件 +で、3 * CHAR_BIT というのは の格納バイト数を示している。 +(と思ったが、3 * CHAR_BIT ではビット数だ。一方、bufsiz はバイト数だ。 +計算に使用している単位が違う。何やらバグっぽい雰囲気があるが、バグだと +してもバッファをちょっとだけ無駄にしているだけなので大したことはないの +だろう) + +# どうやらバグではないらしい。3 * CHAR_BIT というのは、1 セグメントが +# CHAR_BIT のスロット(8 つのスロット)を持ち、1 セグメント内のスロットが +# すべて (3 bytes)の場合、最大 3 bytes * 8 となることを示して +# いるようだ。 +# CHAR_BIT は、buf[cpos] のビット数を表している。 # -# ¼ÂºÝ¤Î¤È¤³¤í 1 ¥»¥°¥á¥ó¥È¤Ï¡¢buf[cpos] ¤ÎÎΰè 1 byte ¤¬ÀèƬ¤ËɬÍפʤΠ-# ¤ÇºÇÂ祵¥¤¥º¤Ï +# 実際のところ 1 セグメントは、buf[cpos] の領域 1 byte が先頭に必要なの +# で最大サイズは # 3 * CHAR_BIT + 1 -# ¤È¤Ê¤ë¡£¤½¤¦¤¤¤¦°ÕÌ£¤Ç¤Ï¡¢ -# if (buf ¤Î»Ä¤ê¥µ¥¤¥º < ºÇÂ祵¥¤¥º) { -# ¤È¤¤¤¦·Á¼°¡£¤Ä¤Þ¤ê¡¢ +# となる。そういう意味では、 +# if (buf の残りサイズ < 最大サイズ) { +# という形式。つまり、 # if (bufsiz - output_pos < 3 * CHAR_BIT + 1) { -# ¤ÎÊý¤¬¤ï¤«¤ê¤ä¤¹¤¤¤è¤¦¤Ë»×¤¦¡£ +# の方がわかりやすいように思う。 -output_pos = 0 ¤È¤·¤Æ¤¤¤ë¤³¤È¤«¤é¤³¤Î»þÅÀ¤Î buf ¤ÎÃæ¿È(¥»¥°¥á¥ó¥È¤Î½¸¤Þ -¤ê=1 ¥Ö¥í¥Ã¥¯)¤¬¤¹¤Ù¤Æ send_block() ¤Ç°µ½Ì¤µ¤ì¥Õ¥¡¥¤¥ë¤Ë½ÐÎϤµ¤ì¤ë¤À¤í -¤¦¤³¤È¤¬ÁÛÁü¤Ç¤­¤ë¡£ +output_pos = 0 としていることからこの時点の buf の中身(セグメントの集ま +り=1 ブロック)がすべて send_block() で圧縮されファイルに出力されるだろ +うことが想像できる。 -¤³¤Î 1 ¥Ö¥í¥Ã¥¯¤ËËþ¤¿¤Ê¤¤¾õÂ֤ǥե¡¥¤¥ë¤Î½ª¤ê¤¬Í褿¾ì¹ç¡¢¸å½èÍý -encode_end_st1() ¤Ç send_block() ¤¬¸Æ¤Ð¤ì¤ë¤Ç¤¢¤í¤¦¤³¤È¤âÁÛÁü¤Ç¤­¤ë¡£ +この 1 ブロックに満たない状態でファイルの終りが来た場合、後処理 +encode_end_st1() で send_block() が呼ばれるであろうことも想像できる。 encode_end_st1( /* void */ ) { @@ -3916,12 +3916,12 @@ encode_end_st1( /* void */ ) } } -»×¤Ã¤¿Ä̤ê¤Ç¤¢¤ë¡£putbits(7, 0) ¤È¤¤¤¦¤Ï¡¢bitbuf ¤Ë»Ä¤Ã¤¿ bit ¤òÅǤ­½Ð¤¹ -¤¿¤á¤Ç¤¢¤ë¤³¤È¤Ï¡¢bit Æþ½ÐÎϥ롼¥Á¥ó¤Î²òÆɤdzÎǧºÑ¤ß¤À¡£ +思った通りである。putbits(7, 0) というは、bitbuf に残った bit を吐き出す +ためであることは、bit 入出力ルーチンの解読で確認済みだ。 -¤½¤¦¤¤¤¦¤ï¤±¤Ç¡¢send_block() ¤¬°µ½Ì¤Î¥á¥¤¥ó¥ë¡¼¥Á¥ó¤Ç¤¢¤ë¡£ -send_block() ¤ÎÆþÎϤȤÏÀè¤Ë¼¨¤·¤¿¿Þ¤Î¾õÂ֤Πbuf ¤À¡£huf.c:send_block() -¤ò¸«¤Æ¤ß¤è¤¦¡£ +そういうわけで、send_block() が圧縮のメインルーチンである。 +send_block() の入力とは先に示した図の状態の buf だ。huf.c:send_block() +を見てみよう。 static void send_block( /* void */ ) @@ -3983,19 +3983,19 @@ send_block( /* void */ ) p_freq[i] = 0; } -¤Ê¤«¤Ê¤«Â礭¤Ê´Ø¿ô¤Ç¤¢¤ë¤¬¡¢¤½¤ì¤Û¤ÉÆñ¤·¤¤¤³¤È¤Ï¤Ê¤¤¡£¤Þ¤º¡¢(A) +なかなか大きな関数であるが、それほど難しいことはない。まず、(A) /* (A) */ root = make_tree(NC, c_freq, c_len, c_code); size = c_freq[root]; putbits(16, size); -make_tree() ¤Ç Huffman ɽ c_len[], c_code[] ¤ò¹½ÃÛ¤¹¤ë¡£Ìá¤êÃͤΠroot -¤Ï¡¢Huffman Ìڤꬤò¼¨¤·¡¢c_freq[root] ¤Ï¡¢Ê¸»ú¤Î½Ð¸½²ó¿ô¤ÎÁíϤǤ¢¤ë -¤«¤é¡¢size ¤Ï¡¢Ê¿Ê¸¤ÎÁí¥Ð¥¤¥È¿ô¤À(size ¤Ë ¤Îʬ¤Î¥µ¥¤¥º¤Ï´Þ¤Þ¤ì¤Ê -¤¤¡£c_freq[] ¤¬¡¢ ¤Î½Ð¸½ÉÑÅÙ¤ò¿ô¤¨¤Ê¤«¤Ã¤¿¤«¤é)¡£¥Õ¥¡¥¤¥ë¤Ë¤Ï¡¢¤³ -¤Î size ¤¬¤Þ¤º½ñ¤­½Ð¤µ¤ì¤Æ¤¤¤ë(¤½¤¦¤¤¤¨¤Ð¡¢¤³¤Î bit Æþ½ÐÎϥ롼¥Á¥ó¤ò»È -ÍѤ¹¤ë¤È¥Ð¥¤¥È¥ª¡¼¥À¡¼¤Ë´Ø¤·¤Æ¹Íθ¤¹¤ëɬÍפ¬¤Ê¤¯¤Ê¤ë)¡£ +make_tree() で Huffman 表 c_len[], c_code[] を構築する。戻り値の root +は、Huffman 木の根を示し、c_freq[root] は、文字の出現回数の総和である +から、size は、平文の総バイト数だ(size に の分のサイズは含まれな +い。c_freq[] が、 の出現頻度を数えなかったから)。ファイルには、こ +の size がまず書き出されている(そういえば、この bit 入出力ルーチンを使 +用するとバイトオーダーに関して考慮する必要がなくなる)。 ---------------------------------------------------------------------------- @@ -4007,7 +4007,7 @@ make_tree() ---------------------------------------------------------------------------- -³¤¤¤Æ¡¢(B) +続いて、(B) if (root >= NC) { count_t_freq(); @@ -4026,20 +4026,20 @@ make_tree() putbits(CBIT, root); } -root ¤¬ NC ¤è¤ê¤âÂ礭¤¤¾ì¹ç¤òȽÃǤ·¤Æ¤¤¤ë¤¬¡¢¥Ï¥Õ¥Þ¥óÌڤꬤÏɬ¤º NC ¤è -¤ê¤âÂ礭¤¤(make_tree() ¤Î avail ¤Î½é´üÃͤò³Îǧ¤·¤è¤¦)¡£¤Ç¤Ï¡¢¤³¤Î -¾ò·ï¤òËþ¤¿¤µ¤Ê¤¤¾ì¹ç¤È¸À¤¦¤Î¤Ï²¿¤«¤È¸À¤¦¤È¡¢Æ±¤¸¤¯ make_tree() ¤ò³Îǧ¤¹¤ë¤È¡¢ +root が NC よりも大きい場合を判断しているが、ハフマン木の根は必ず NC よ +りも大きい(make_tree() の avail の初期値を確認しよう)。では、この +条件を満たさない場合と言うのは何かと言うと、同じく make_tree() を確認すると、 if (heapsize < 2) { codeparm[heap[1]] = 0; return heap[1]; } -¤È¤¤¤¦Îã³°¾ò·ï¤¬¤¢¤Ã¤¿¡£¤³¤ì¤Ï¡¢°µ½Ì¤¹¤ëʸ»ú¤¬¤Ê¤¤¡¢¤¢¤ë¤¤¤Ï 1 ¼ïÎष¤« -¤Ê¤¤¾ì¹ç¤Î½èÍý¤À¡£°µ½Ì¤¹¤ëʸ»ú¤¬¤Ê¤¤¾ì¹ç¤Ë send_block() ¤¬¸Æ¤Ð¤ì¤ë¤³¤È -¤Ï¤Ê¤¤¤À¤í¤¦¤«¤é¡¢(B) ¤Î½èÍý¤Î else ¤Ï 1 ¥Ö¥í¥Ã¥¯Ãæ¤Ë°µ½Ì¤¹¤ëʸ»ú¤¬ 1 -¼ïÎष¤«¤Ê¤¤¾ì¹ç¤Î½èÍý¤Ç¤¢¤ë(¤³¤Î 1 ¼ïÎà¤Îʸ»ú¤È¤Ï¡¢make_tree() ¤ÎÌá¤ê -ÃÍ root ¤À)¡£¤³¤Î¤È¤­°Ê²¼¤Î¤è¤¦¤Ê½ÐÎϤˤʤ롣(TBIT{5}, CBIT{9} ¤Ç¤¢¤ë) +という例外条件があった。これは、圧縮する文字がない、あるいは 1 種類しか +ない場合の処理だ。圧縮する文字がない場合に send_block() が呼ばれること +はないだろうから、(B) の処理の else は 1 ブロック中に圧縮する文字が 1 +種類しかない場合の処理である(この 1 種類の文字とは、make_tree() の戻り +値 root だ)。このとき以下のような出力になる。(TBIT{5}, CBIT{9} である) ---------------------------------------------------------------------------- TBIT CBIT @@ -4051,11 +4051,11 @@ root ---------------------------------------------------------------------------- -¤³¤ì¤¬¡¢1 ¥Ö¥í¥Ã¥¯¤Ë 1 ¼ïÎष¤«Ê¸»ú¤¬¤Ê¤¤¾ì¹ç¤Î½ÐÎϤÀ(off ¤Î¾ðÊó¤Ï¤Þ¤À -´Þ¤Þ¤Ê¤¤)¡£(B)¤Î if ¤¬¿¿¤Î¤È¤­¤¬¤É¤¦¤Ê¤ë¤«¤ÏÊ£»¨¤½¤¦¤Ê¤Î¤Ç¸å¤Ç¸«¤ë¤³¤È -¤Ë¤·¤è¤¦¡£ +これが、1 ブロックに 1 種類しか文字がない場合の出力だ(off の情報はまだ +含まない)。(B)の if が真のときがどうなるかは複雑そうなので後で見ること +にしよう。 -³¤¤¤Æ (C) +続いて (C) root = make_tree(np, p_freq, pt_len, pt_code); if (root >= np) { @@ -4066,9 +4066,9 @@ root putbits(pbit, root); } -p_freq[] ¤ò¸«¤Æ¤¤¤ë¤³¤È¤«¤éº£ÅÙ¤Ï ¤Î¾ðÊó¤Î Huffman ÌÚ¤ò¹½ÃÛ¤·¤Æ -¤¤¤ë¤³¤È¤¬¤ï¤«¤ë¡£ÀèÄø¤ÈƱÍͤˡ¢ ¤ÎÃͤ¬¤¹¤Ù¤ÆƱ¤¸¾ì¹ç¤Ï¡¢else ¤Î -¾ò·ï¤Ë¤Ê¤ê¡¢°Ê²¼¤Î½ÐÎϤ¬¹Ô¤ï¤ì¤ë¡£(pbit ¤ÎÃͤϡ¢-lh7- ¤Î¾ì¹ç¤Ç¡¢5 ¤À) +p_freq[] を見ていることから今度は の情報の Huffman 木を構築して +いることがわかる。先程と同様に、 の値がすべて同じ場合は、else の +条件になり、以下の出力が行われる。(pbit の値は、-lh7- の場合で、5 だ) ---------------------------------------------------------------------------- @@ -4081,13 +4081,13 @@ p_freq[] ---------------------------------------------------------------------------- -¤³¤³¤Þ¤Ç¤Ë½ÐÎϤ·¤¿¾ðÊ󤬲¿¤ò¼¨¤¹¤«¤ï¤«¤ë¤À¤í¤¦¤«¡©Huffman Ë¡¤ÎÉä¹æ²½½è -Íý¤Ïʸ»ú¤ò bit Îó¤ËÊÑ´¹¤¹¤ë¡£¤³¤ì¤òÉü¹æ¤¹¤ë¾ì¹ç¤Ï bit Îó¤ËÂбþ¤¹¤ëʸ»ú -¤òÃΤëɬÍפ¬¤¢¤ë¡£¤¹¤Ê¤ï¤Á Huffman ÌڤǤ¢¤ë(¼ÂºÝ¤Ë¤Ï Huffman ɽ)¡£¿Þ¼¨ -¤·¤¿¤Î¤Ï¡¢Huffman ÌÚ¤ò¹½ÃÛ¤¹¤ëɬÍפ¬¤Ê¤¤(¹½ÃۤǤ­¤Ê¤¤)¾ì¹ç¤Î¾ðÊó¤Ë¤Ê¤ë -¤¬¡¢¸½ºß²òÆɤòÈô¤Ð¤·¤Æ¤¤¤ë½èÍý¤Ï Huffman ɽ¤ò¥Õ¥¡¥¤¥ë¤Ë½ÐÎϤ·¤Æ¤¤¤ë²Õ -½ê¤Ç¤¢¤ë¤³¤È¤ÏÍưפËÁÛÁü¤¬¤Ä¤¯¡£¤È¤¤¤¦¤³¤È¤Ï»Ä¤ê¤Î (D) ¤¬ËÜÅö¤Î°µ½Ìʸ -¤ò½ÐÎϤ¹¤ë²Õ½ê¤À¡£ +ここまでに出力した情報が何を示すかわかるだろうか?Huffman 法の符号化処 +理は文字を bit 列に変換する。これを復号する場合は bit 列に対応する文字 +を知る必要がある。すなわち Huffman 木である(実際には Huffman 表)。図示 +したのは、Huffman 木を構築する必要がない(構築できない)場合の情報になる +が、現在解読を飛ばしている処理は Huffman 表をファイルに出力している箇 +所であることは容易に想像がつく。ということは残りの (D) が本当の圧縮文 +を出力する箇所だ。 /* (D) */ pos = 0; @@ -4107,20 +4107,20 @@ p_freq[] return; } -size ¿ôʬ¥ë¡¼¥×¤·¤Æ¤¤¤ë¡£size ¤Ï¡¢ ¤ò½ü¤¤¤¿ buf ¤Îʸ»ú¿ô¤ò¼¨¤·¤Æ -¤¤¤ë¤ÈÁ°¤Ë½ñ¤¤¤¿¤¬¡¢¤É¤¦¤ä¤é ¤ò 1 ʸ»ú¤È¿ô¤¨¤¿¤È¤­¤Î buf ¤Î -ʸ»ú¿ô¤ò¼¨¤·¤Æ¤¤¤ë¤È¹Í¤¨¤¿Êý¤¬Îɤµ¤½¤¦¤À¡£ +size 数分ループしている。size は、 を除いた buf の文字数を示して +いると前に書いたが、どうやら を 1 文字と数えたときの buf の +文字数を示していると考えた方が良さそうだ。 -ºÇ½é¤Î if ¤Ç¡¢ +最初の if で、 if (i % CHAR_BIT == 0) flags = buf[pos++]; else flags <<= 1; -¤³¤ì¤¬¿¿¤Ë¤Ê¤ë¾ò·ï¤Ï buf[pos] ¤¬ buf[cpos] ¤Ç¤¢¤ë¾ì¹ç¤À(output_mask ¤¬ -128, 64, ..., 1 ¤È 8 ¤Ä¤ÎÃͤò½ä²ó¤·¤Æ¤¤¤¿¤³¤È¤ò»×¤¤½Ð¤½¤¦)¡£ -flags ¤Ï¡¢ ¤Î buf ¾å¤Î°ÌÃÖ¤ò¼¨¤¹ bit ¥Þ¥¹¥¯¤Ë¤Ê¤ë¡£ +これが真になる条件は buf[pos] が buf[cpos] である場合だ(output_mask が +128, 64, ..., 1 と 8 つの値を巡回していたことを思い出そう)。 +flags は、 の buf 上の位置を示す bit マスクになる。 if (flags & (1 << (CHAR_BIT - 1))) { encode_c(buf[pos++] + (1 << CHAR_BIT)); @@ -4130,24 +4130,24 @@ flags } else encode_c(buf[pos++]); -flags ¤Î 7 ¥Ó¥Ã¥ÈÌÜ(128)¤¬Î©¤Ã¤Æ¤¤¤ë¤È¤­ buf[pos] ¤Ï¡¢ ¤ò»Ø¤·¡¢ +flags の 7 ビット目(128)が立っているとき buf[pos] は、 を指し、 encode_c(len + 256) encode_p(off) -¤Ç¡¢°µ½Ì¤ò¹Ô¤¦¤è¤¦¤À¡£len ¤Ë 256 ¤ò­¤·¤Æ¤¤¤ë¤Î¤Ï¡¢buf[] ¤Ë len ¤ò³ÊǼ -¤¹¤ë¤È¤­(output_st1() ¤Î (B) ¤Î½èÍý)¤Ë +で、圧縮を行うようだ。len に 256 を足しているのは、buf[] に len を格納 +するとき(output_st1() の (B) の処理)に buf[output_pos++] = (unsigned char) c; -¤Î¤è¤¦¤ËºÇ¾å°Ì bit ¤ò¼Î¤Æ¤Æ¤¤¤¿¤«¤é¤À¡£len ¤Ï¾ï¤Ë 256 °Ê¾å¤Ê¤Î¤Ç¡¢256 -¤ò­¤¹¤³¤È¤Ç¸µ¤Î len ¤ÎÃͤò°µ½Ì¥ë¡¼¥Á¥ó¤ËÅϤ·¤Æ¤¤¤ë¡£ +のように最上位 bit を捨てていたからだ。len は常に 256 以上なので、256 +を足すことで元の len の値を圧縮ルーチンに渡している。 -Ä̾ï¤Îʸ»ú¤Ï +通常の文字は encode_c(buf[pos]) -¤Ç°µ½Ì¤µ¤ì¤Æ¤¤¤ë¡£encode_c() ¤Î½èÍýÆâÍƤϴÊñ¤Ê¤Î¤Ç¸«¤Æ¤ß¤è¤¦¡£ +で圧縮されている。encode_c() の処理内容は簡単なので見てみよう。 static void encode_c(c) @@ -4156,10 +4156,10 @@ encode_c(c) putcode(c_len[c], c_code[c]); } -c_len[], c_code[] ¤¬Ê¸»ú c ¤ËÂбþ¤¹¤ë Huffman Éä¹æ¤Î bit ŤÈÉä¹æ¤ò¼¨ -¤·¤Æ¤¤¤ë¤Î¤Ç¡¢¤½¤ì¤ò¤½¤Î¤Þ¤Þ½ÐÎϤ·¤Æ¤¤¤ë¡£´Êñ¤À¡£ +c_len[], c_code[] が文字 c に対応する Huffman 符号の bit 長と符号を示 +しているので、それをそのまま出力している。簡単だ。 -encode_p() ¤Ï¤â¤¦¾¯¤·Ê£»¨¤À¡£ +encode_p() はもう少し複雑だ。 static void encode_p(p) @@ -4178,12 +4178,12 @@ encode_p(p) putbits(c - 1, p); } -ºÇ½é¤Î while ʸ¤Ç¡¢ ¤Î bit Ťòµá¤á¡¢¤½¤Î bit Ĺ¤Î¾ðÊó¤ò -Huffman Éä¹æ²½¤·¤Æ¤¤¤ë¡£¤½¤Î¸å¡¢putbits() ¤Ç¡¢É¬Í× bit ¿ô¤À¤± -½ÐÎϤ¹¤ë¡£¤Ä¤Þ¤ê¡¢ ¤Ï°Ê²¼¤Î¤è¤¦¤ËÉä¹æ²½¤µ¤ì¤ë¡£ +最初の while 文で、 の bit 長を求め、その bit 長の情報を +Huffman 符号化している。その後、putbits() で、必要 bit 数だけ +出力する。つまり、 は以下のように符号化される。 ---------------------------------------------------------------------------- -off = 64 ¤Î°µ½Ì +off = 64 の圧縮 |---- 16 bit -------| +----+----+----+----+ @@ -4191,21 +4191,21 @@ off |0000 0000 0100 0000| +----+----+----+----+ |-7 bit-| -¤³¤Î°µ½Ìʸ¤Ï°Ê²¼(Ťµ¤¬ 7 bit ¤Ç¤¢¤ë¤È¤¤¤¦¾ðÊó(HuffmanÉä¹æ²½)¤ÈÃͤΥڥ¢) +この圧縮文は以下(長さが 7 bit であるという情報(Huffman符号化)と値のペア) |-6 bit-| +-----------------+-------+ - | 7 ¤ÎHuffmanÉä¹æ |00 0000| + | 7 のHuffman符号 |00 0000| +-----------------+-------+ ---------------------------------------------------------------------------- -¤³¤³¤Ç¡¢Ãͤò 6 bit ¤·¤«½ÐÎϤ·¤Ê¤¤(putbits() ¤Ç c-1 ¤òÅϤ·¤Æ¤¤¤ë)¤Î¤Ï¡¢ -7 bit Ìܤ¬ 1 ¤Ç¤¢¤ë¤³¤È¤¬¼«ÌÀ¤À¤«¤é¤Ç¤¢¤ë¡£ºÇ½é¤Ë¥Ó¥Ã¥ÈŤò½ÐÎϤ·¤Æ¤¤¤ë -¤Î¤Ç¡¢ÃͤξðÊó¤Ï1 bit ºï¸º¤Ç¤­¤ë¤ï¤±¤À¡£¤·¤¿¤¬¤Ã¤Æ¡¢off=1 ¤Î¤È¤­¤Ï bit -Ť¬ 1 ¤È¤¤¤¦¾ðÊó¤·¤«½ñ¤­½Ð¤µ¤Ê¤¤¡£ +ここで、値を 6 bit しか出力しない(putbits() で c-1 を渡している)のは、 +7 bit 目が 1 であることが自明だからである。最初にビット長を出力している +ので、値の情報は1 bit 削減できるわけだ。したがって、off=1 のときは bit +長が 1 という情報しか書き出さない。 -ºÇ¸å¤Î (E) ¤ÏÉÑÅÙɽ¤ò¥¯¥ê¥¢¤·¤Æ¤¤¤ë¤À¤±¤À¡£ +最後の (E) は頻度表をクリアしているだけだ。 /* (E) */ for (i = 0; i < NC; i++) @@ -4213,18 +4213,18 @@ off |0000 0000 0100 0000| for (i = 0; i < np; i++) p_freq[i] = 0; -¤³¤³¤Ç¡¢ÉÑÅÙɽ¤ò¥¯¥ê¥¢¤·¤Æ¤¤¤ë¤È¤¤¤¦¤³¤È¤Ïʸ»ú¤ä°ÌÃ֤νи½²ó¿ô¤Ï 1 ¥Ö¥í¥Ã¥¯ -ñ°Ì¤Ç¤·¤«·×¾å¤·¤Ê¤¤¤³¤È¤òɽ¤¹¡£ +ここで、頻度表をクリアしているということは文字や位置の出現回数は 1 ブロック +単位でしか計上しないことを表す。 -# °ìÊý¡¢c_freq ¤ä p_freq ¤Ï unsigned short ¤Ç¤¢¤ë¤«¤é(16 bit ¤À¤È¤· -# ¤Æ)65535 ¤Þ¤Ç¤·¤«¿ô¤¨¤é¤ì¤Ê¤¤¡£¤µ¤é¤Ë¡¢{c,p}_freq ¤Ë¤Ï Huffman Ìڤι½ -# ÃۤβáÄø¤Ç½Ð¸½²ó¿ô¤ÎÁíϤ¬¥»¥Ã¥È¤µ¤ì¤ë¾ì¹ç¤¬¤¢¤ë¤³¤È¤«¤é 1 ¥Ö¥í¥Ã¥¯¤Ë -# ¤Ï 65535 ʸ»ú + 65535 ¸Ä¤Î°ÌÃ֤ޤǤ·¤«³ÊǼ¤Ç¤­¤Ê¤¤¡£ -# ¤¤¤ä¡¢°ÌÃÖ¤Ïɬ¤ºÄ¹¤µ¤È¥»¥Ã¥È¤Ç¤¢¤ë¤³¤È¤«¤é¡£°ÌÃ֤νи½²ó¿ô¤Ïʸ»ú¤Î½Ð -# ¸½²ó¿ô(¤³¤ì¤ÏŤµ¤Î½Ð¸½²ó¿ô¤ò´Þ¤à)¤Ë´Þ¤Þ¤ì¤ë¤¿¤á 65535 ¥¹¥í¥Ã¥È¤Þ¤Ç¤· -# ¤« buf ¤ò»ý¤Æ¤Ê¤¤¤³¤È¤Ë¤Ê¤ë¡£¤³¤ì¤Ï blocksize (16 bit)¤È¤â°ìÃפ¹¤ë¡£ +# 一方、c_freq や p_freq は unsigned short であるから(16 bit だとし +# て)65535 までしか数えられない。さらに、{c,p}_freq には Huffman 木の構 +# 築の過程で出現回数の総和がセットされる場合があることから 1 ブロックに +# は 65535 文字 + 65535 個の位置までしか格納できない。 +# いや、位置は必ず長さとセットであることから。位置の出現回数は文字の出 +# 現回数(これは長さの出現回数を含む)に含まれるため 65535 スロットまでし +# か buf を持てないことになる。これは blocksize (16 bit)とも一致する。 # -# buf ¤ÎÎΰè³ÎÊݤϰʲ¼¤Î¤è¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤¿¡£ +# buf の領域確保は以下のようになっていた。 # # unsigned char * # alloc_buf( /* void */ ) @@ -4238,25 +4238,25 @@ off |0000 0000 0100 0000| # return buf; # } # -# ¤³¤ì¤«¤é¡¢bufsiz ¤Ï¡¢16 * 1024 *2 = 2^4 * 2^10 * 2 = 2^15 ¤Ç¤¢¤ë¡£ -# 1 ¥»¥°¥á¥ó¥È¤ÎºÇ¾®¥µ¥¤¥º¤¬¥Ð¥¤¥È¿ô¤Ç(1*CHAR_BIT+1)¤Ç¤¢¤ë¤«¤é -# ¤³¤ÎÎΰè¤Ë³ÊǼ¤Ç¤­¤ëºÇÂ祻¥°¥á¥ó¥È¿ô¤Ï¡¢ +# これから、bufsiz は、16 * 1024 *2 = 2^4 * 2^10 * 2 = 2^15 である。 +# 1 セグメントの最小サイズがバイト数で(1*CHAR_BIT+1)であるから +# この領域に格納できる最大セグメント数は、 # 2^15 / (1*CHAR_BIT+1) -# ¤Ç¤¢¤ê¡¢1 ¥»¥°¥á¥ó¥È¤Ï 8 ¥¹¥í¥Ã¥È¤¢¤ë¤«¤é¡¢ºÇÂ祹¥í¥Ã¥È¿ô¤Ï +# であり、1 セグメントは 8 スロットあるから、最大スロット数は # 8 * 2^15 / (1*CHAR_BIT+1) # = 2^18 / 9 # = 29127.1111111111 -# ¤È¤Ê¤ë¡£¤³¤ì¤Ï¡¢ÉÑÅÙɽ¤Î¾å¸Â(¥¹¥í¥Ã¥È¿ô¤Î¾å¸Â) +# となる。これは、頻度表の上限(スロット数の上限) # 65535=2^16-1 -# ¤è¤ê¤â¾®¤µ¤¤¤Î¤ÇÌäÂê¤Ï¤Ê¤¤¤³¤È¤Ë¤Ê¤ë¡£ +# よりも小さいので問題はないことになる。 # -# ¤Ê¤ª¡¢1 ¥Ö¥í¥Ã¥¯¤Î¥µ¥¤¥º¤Ï¤³¤Î¥Ð¥Ã¥Õ¥¡¥µ¥¤¥º¤Ë¤è¤ê·è¤Þ¤ë¤ï¤±¤À¤¬ 1 ¥Ö -# ¥í¥Ã¥¯¤Î¥µ¥¤¥º¤¬Â礭¤±¤ì¤ÐÂ礭¤¤¤Û¤É¤è¤¤¤È¤¤¤¦¤ï¤±¤Ç¤Ï¤Ê¤¤¡£¤à¤·¤í¡¢ -# ʸ»ú¤Î½Ð¸½³ÎΨ¤ÎÊÑÆ°¤ËÄɿ魯¤ë¤¿¤á¤Ë¤Ï¾®¤µ¤¤Êý¤¬¤è¤¤¤Î¤À¤¬¤½¤¦¤¹¤ë¤È -# Huffman ÌڤγÊǼ²ó¿ô¤¬Áý¤¨¤ë¤Î¤Ç´Êñ¤Ë¤ÏºÇŬÃͤϷè¤Þ¤é¤Ê¤¤¡£ +# なお、1 ブロックのサイズはこのバッファサイズにより決まるわけだが 1 ブ +# ロックのサイズが大きければ大きいほどよいというわけではない。むしろ、 +# 文字の出現確率の変動に追随するためには小さい方がよいのだがそうすると +# Huffman 木の格納回数が増えるので簡単には最適値は決まらない。 -°Ê¾å¤Ç¡¢°µ½Ì½èÍýÁ´ÂΤγµÍפ¬¤ï¤«¤Ã¤¿¡£¸å¤Ï̵»ë¤·¤Æ¤¤¤¿ Huffman ɽ¤ò½Ð -ÎϤ¹¤ë²Õ½ê¤À¤±¤À¡£ +以上で、圧縮処理全体の概要がわかった。後は無視していた Huffman 表を出 +力する箇所だけだ。 /* (B) */ if (root >= NC) { @@ -4270,11 +4270,11 @@ off |0000 0000 0100 0000| } write_c_len(); -¤³¤³¤Ç¤Ï¡¢c_len[], c_code[] ¤È¤¤¤¦ Huffman ɽ¤ò½ÐÎϤ¹¤ë¤À¤±¤Î¤Ï¤º¤À¤¬¡¢ -¤µ¤é¤Ë Huffman ɽ pt_len[], pt_code[] ¤Î¹½ÃÛ¤ò¹Ô¤Ã¤Æ¤¤¤ë¡£¤³¤ì¤Ï¡¢ - ¤Î bit ŤΠHuffman Éä¹æɽ¤Ç¤â¤¢¤Ã¤¿ÊÑ¿ô¤À¤¬¡¢Ã±¤ËÊÑ¿ô¤ò»È¤¤²ó¤· -¤Æ¤¤¤ë¤À¤±¤À¡£¤³¤³¤Ç¤Î pt_len[], pt_code[] ¤¬²¿¤ÎÉä¹æɽ¤«¤Ï¡¢ -count_t_freq() ¤ò¸«¤ëɬÍפ¬¤¢¤ë¡£ +ここでは、c_len[], c_code[] という Huffman 表を出力するだけのはずだが、 +さらに Huffman 表 pt_len[], pt_code[] の構築を行っている。これは、 + の bit 長の Huffman 符号表でもあった変数だが、単に変数を使い回し +ているだけだ。ここでの pt_len[], pt_code[] が何の符号表かは、 +count_t_freq() を見る必要がある。 static void count_t_freq(/*void*/) @@ -4310,13 +4310,13 @@ count_t_freq(/*void*/) } } -ºÇ½é¤ËÉÑÅÙɽ t_freq[] ¤ò½é´ü²½¤¹¤ë¡£Â³¤¤¤Æ¡¢ +最初に頻度表 t_freq[] を初期化する。続いて、 n = NC; while (n > 0 && c_len[n - 1] == 0) n--; -¤Ç¡¢c_len[n] != 0 ¤Ç¤¢¤ëºÇÂç¤Î n ¤òµá¤á¤Æ¤¤¤ë¡£ +で、c_len[n] != 0 である最大の n を求めている。 i = 0; while (i < n) { @@ -4341,26 +4341,26 @@ count_t_freq(/*void*/) t_freq[k + 2]++; } -c_len[i] ¤Ï¡¢Ê¸»ú i ¤Î Huffman Éä¹æ¤Ç¤Î bit ŤǤ¢¤Ã¤¿¡£¤³¤Î -c_len[i] ¤ÎÃͤò°Ê²¼¤Î¾ì¹çʬ¤±¤Ç t_freq[] ¤ËÉÑÅÙ·×»»¤·¤Æ¤¤¤ë¡£ -count ¤Ï¡¢c_len[i] ¤¬Ï¢Â³¤Ç²¿²ó 0 ¤Ç¤¢¤ë¤«¤Î¿ô¤À¡£ +c_len[i] は、文字 i の Huffman 符号での bit 長であった。この +c_len[i] の値を以下の場合分けで t_freq[] に頻度計算している。 +count は、c_len[i] が連続で何回 0 であるかの数だ。 c_len[i] count t_freq[] ------------------------------------------- 0 1 .. 2 t_freq[0] += count 0 3 ..18 t_freq[1]++ 0 19 t_freq[0]++, t_freq[1]++ - 0 20°Ê¾å t_freq[2]++ - 0°Ê³° t_freq[c_len[i]+2]++; + 0 20以上 t_freq[2]++ + 0以外 t_freq[c_len[i]+2]++; -¤³¤ì¤¬¤É¤¦¤¤¤¦Íý¶þ¤Ç¤¢¤ë¤«¤Ï¤è¤¯¤ï¤«¤é¤Ê¤¤¤¬¡¢¤È¤Ë¤«¤¯ÉÑÅÙ·×»»¤ò¹Ô¤¦¾ì -¹ç¤Ë t_freq[0], t_freq[1], t_freq[2] ¤òÆÃÊÌ°·¤¤¤·¤Æ¤¤¤ë¡£¤½¤·¤Æ¡¢ÉÑÅÙ -·×»»¤ÎÂоݤ¬ c_len[] ¤Ç¤¢¤ë¤³¤È¤«¤é (B) ¤Î½èÍý¤Ï¡¢c_len[] ¤Ë´Ø¤·¤Æ -Huffman Éä¹æ²½¤ò¹Ô¤¦½èÍý¤Î¤è¤¦¤À¡£ +これがどういう理屈であるかはよくわからないが、とにかく頻度計算を行う場 +合に t_freq[0], t_freq[1], t_freq[2] を特別扱いしている。そして、頻度 +計算の対象が c_len[] であることから (B) の処理は、c_len[] に関して +Huffman 符号化を行う処理のようだ。 -¤½¤¦¤·¤Æ¡¢make_tree() ¤Ç¡¢t_freq[] ¤Ë´Ø¤·¤Æ Huffman Éä¹æɽ¤òºîÀ®¤·¡¢ -write_pt_len() ¤Ç¡¢¤³¤ÎÉä¹æɽ(ʸ»ú¤Î Huffman Éä¹æ¤Î¥Ó¥Ã¥ÈĹ c_len ¤Î -Huffman Éä¹æ¤Î¥Ó¥Ã¥ÈĹ) pt_len[] ¤ò½ÐÎϤ¹¤ë¡£ +そうして、make_tree() で、t_freq[] に関して Huffman 符号表を作成し、 +write_pt_len() で、この符号表(文字の Huffman 符号のビット長 c_len の +Huffman 符号のビット長) pt_len[] を出力する。 static void write_pt_len(n, nbit, i_special) @@ -4388,53 +4388,53 @@ write_pt_len(n, nbit, i_special) } } -ºÇ½é¤Ë pt_len[] ¤ÎÍ×ÁÇ¿ô¤ò nbit ½ÐÎϤ·¡¢Â³¤±¤ÆÉä¹æ bit Ĺ pt_len[] ¤Î -Í×ÁǤò½ÐÎϤ·¤Æ¤¤¤ë¡£nbit ¤Ï¡¢n ¤ò³ÊǼ¤¹¤ë¤Î¤ËɬÍ×¤Ê bit ¿ô¤òɽ¤·¤Æ¤¤¤ë -¤è¤¦¤À¡£¤³¤³¤Ç¤Ï¡¢n (NT{19}) ¤ò½ÐÎϤ¹¤ë¤Î¤Ë TBIT{5} bit ɬÍפǤ¢¤ë¤È¤¤ -¤¦¤³¤È¤À¡£ +最初に pt_len[] の要素数を nbit 出力し、続けて符号 bit 長 pt_len[] の +要素を出力している。nbit は、n を格納するのに必要な bit 数を表している +ようだ。ここでは、n (NT{19}) を出力するのに TBIT{5} bit 必要であるとい +うことだ。 -pt_len[] ¤ò½ÐÎϤ¹¤ë¤È¤­¤Ï¡¢¤½¤ÎÃͤ¬ 6 ¤è¤êÂ礭¤¤¤«¤É¤¦¤«¤Ç·Á¼°¤òÊѤ¨¤Æ -½ÐÎϤ·¤Æ¤¤¤ë¡£6 °Ê²¼¤Ç¤¢¤ì¤Ð¤½¤Î¤Þ¤Þ 3 bit ¤Ç½ÐÎϤ·¡¢7 bit °Ê¾å¤Ç¤¢¤ì -¤Ð¡¢bit ¿ô¤Çɽ¸½¤¹¤ë¤é¤·¤¤¡£Î㤨¤Ð pt_len[i] == 7 ¤Ê¤é¡¢1110 ¤È¤Ê¤ë¡£ -ºÇ½é¤Î 3 bit ¤Ïɬ¤º 1 ¤Ë¤Ê¤ê¡¢ºÇ½é¤Î·Á¼°¤È¶èÊ̤¬¤Ä¤¯¤è¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤ë¡£ +pt_len[] を出力するときは、その値が 6 より大きいかどうかで形式を変えて +出力している。6 以下であればそのまま 3 bit で出力し、7 bit 以上であれ +ば、bit 数で表現するらしい。例えば pt_len[i] == 7 なら、1110 となる。 +最初の 3 bit は必ず 1 になり、最初の形式と区別がつくようになっている。 -¤µ¤é¤Ë¡¢i_special ÈÖÌܤΠpt_len[i] ¤ò½ÐÎϤ·¤¿¸å¤Ï¡¢i_special ... 6 ¤ÎÈÏ -°Ï¤Ç pt_len[i] == 0 ¤¬Â³¤¯¤³¤È¤ò 2 bit ¤Ç¡¢É½¸½¤·¤Æ¤¤¤ë¡£i_special ¤Ï -write_pt_len() ¤Î 3 ÈÖÌܤΰú¿ô¤Ç¡¢º£²ó¤Î¾ì¹ç¤Ï 3 ¤À¡£Î㤨¤Ð -pt_len[3..5] ¤¬¤¹¤Ù¤Æ 0 ¤Ê¤é pt_len[3..5] ¤ò½ÐÎϤ»¤º¤Ë¡¢i - 3 (= 3) ¤ò -2 bit ½ÐÎϤ¹¤ë¡£¤Ä¤Þ¤ê¡¢11 ¤¬½ÐÎϤµ¤ì¤ë¡£¤³¤Î¤è¤¦¤Ê¤³¤È¤ò¤·¤Æ¤¤¤ë°ÕÌ£¤Ï -¤³¤ì¤Þ¤¿¤è¤¯¤ï¤«¤é¤Ê¤¤¡£¤Á¤ç¤Ã¤ÈÊ£»¨¤Ê¤Î¤Ç¿Þ¼¨¤·¤Æ¤ß¤¿¡£ +さらに、i_special 番目の pt_len[i] を出力した後は、i_special ... 6 の範 +囲で pt_len[i] == 0 が続くことを 2 bit で、表現している。i_special は +write_pt_len() の 3 番目の引数で、今回の場合は 3 だ。例えば +pt_len[3..5] がすべて 0 なら pt_len[3..5] を出力せずに、i - 3 (= 3) を +2 bit 出力する。つまり、11 が出力される。このようなことをしている意味は +これまたよくわからない。ちょっと複雑なので図示してみた。 ---------------------------------------------------------------------------- -< pt_len[] ¤Î½ÐÎÏ¥Õ¥©¡¼¥Þ¥Ã¥È > +< pt_len[] の出力フォーマット > 0 TBIT{5} +-------+-----------+-----------+-- --+-----------+ | n | pt_len[0] | pt_len[1] | ... pt_len[n-1]| +-------+-----------+-----------+-- --+-----------+ -pt_len[i] <= 6 ¤Î¾ì¹ç +pt_len[i] <= 6 の場合 0 3bit +-----+ pt_len[i] | | | | +-----+ -pt_len[i] >= 7 ¤Î¾ì¹ç +pt_len[i] >= 7 の場合 0 pt_len[i] - 3 +----------------+ pt_len[i] |1 1 1 1 ... 1 0 | +----------------+ -pt_len[i_special-1] ¤Îľ¸å¤Ï 2 bit ¤Î¾ðÊó¤¬Éղ䵤ì¤ë¡£¤³¤ÎÃͤò x ¤È¤¹¤ë -¤È¡¢pt_len[i_special .. x + 2] ¤ÎÈÏ°Ï¤Ç 0 ¤¬Â³¤¯¤³¤È¤ò°ÕÌ£¤¹¤ë¡£x ¤¬ 0 -¤Ê¤é pt_len[i_special] ¤Ï 0 ¤Ç¤Ï¤Ê¤¤¡£ +pt_len[i_special-1] の直後は 2 bit の情報が付加される。この値を x とする +と、pt_len[i_special .. x + 2] の範囲で 0 が続くことを意味する。x が 0 +なら pt_len[i_special] は 0 ではない。 ---------------------------------------------------------------------------- -ºÇ¸å¤Ë¡¢write_c_len() ¤Ç¡¢Huffman Éä¹æ¤Î¥Ó¥Ã¥ÈĹ c_len[] (¤Î Huffman Éä -¹æɽ pt_code[]) ¤ò½ÐÎϤ¹¤ë¡£ +最後に、write_c_len() で、Huffman 符号のビット長 c_len[] (の Huffman 符 +号表 pt_code[]) を出力する。 static void write_c_len(/*void*/) @@ -4477,22 +4477,22 @@ write_c_len(/*void*/) } } -Á°¤Ë¡¢ÉÑÅÙ¤ò¿ô¤¨¤¿¤È¤­¤ÈƱÍͤξò·ï¤Ç½ÐÎÏ·Á¼°¤¬ÊѤï¤Ã¤Æ¤¤¤ë¡£½èÍýÆâÍÆ¤Ï -´Êñ¤Ê¤Î¤Ç¡¢°Ê²¼¤Î¿Þ¤ò¼¨¤¹¤À¤±¤Ë¤¹¤ë(Íý¶þ¤Ï¤è¤¯¤ï¤«¤é¤Ê¤¤)¡£ +前に、頻度を数えたときと同様の条件で出力形式が変わっている。処理内容は +簡単なので、以下の図を示すだけにする(理屈はよくわからない)。 ---------------------------------------------------------------------------- -< c_len[] ¤Î½ÐÎÏ¥Õ¥©¡¼¥Þ¥Ã¥È > +< c_len[] の出力フォーマット > 0 CBIT{9} +-------+-----------+-----------+-- --+-----------+ | n | c_len[0] | c_len[1] | ... c_len[n-1]| +-------+-----------+-----------+-- --+-----------+ -c_len[i] == 0 ¤Î¾ì¹ç +c_len[i] == 0 の場合 - 0 ¤¬Â³¤¯¿ô¤ò count ¤È¤¹¤ë¤È¡¢ + 0 が続く数を count とすると、 - count == 1..2 ¤Î¾ì¹ç + count == 1..2 の場合 pt_len[0] <----------> @@ -4500,7 +4500,7 @@ c_len[i] == 0 | pt_code[0] | +------------+ - count == 3..18 ¤Î¾ì¹ç + count == 3..18 の場合 pt_len[1] 4 bit <----------> <------> @@ -4508,7 +4508,7 @@ c_len[i] == 0 | pt_code[1] |count-3| +------------+-------+ - count == 19 ¤Î¾ì¹ç + count == 19 の場合 pt_len[0] pt_len[1] 4 bit <----------> <----------> <------> @@ -4516,7 +4516,7 @@ c_len[i] == 0 | pt_code[0] | pt_code[1] |count-3| +------------+------------+-------+ - count >= 20 ¤Î¾ì¹ç + count >= 20 の場合 pt_len[2] CBIT{9} <----------> <------> @@ -4524,7 +4524,7 @@ c_len[i] == 0 | pt_code[2] |count-20| +------------+--------+ -c_len[i] != 0 ¤Î¾ì¹ç +c_len[i] != 0 の場合 pt_len[c_len[i]+2] +-------------------+ @@ -4533,33 +4533,33 @@ c_len[i] != 0 ---------------------------------------------------------------------------- -¤³¤¦¤·¤Æ¡¢Ê¸»ú¤Î Huffman Éä¹æɽ¤Ï¡¢pt_len[] ¤È pt_code[](pt_code[] ¤Ï -¤Ä¤Þ¤ê c_len[] ¤Î Huffman Éä¹æ)¤ò½ÐÎϤ¹¤ë¤³¤È¤Çɽ¸½¤µ¤ì¤ë¡£c_code[] ¤¬ -½ÐÎϤµ¤ì¤Æ¤Ê¤¤¤È»×¤¦¤«¤â¤·¤ì¤Ê¤¤¤¬¡¢¤ª¤½¤é¤¯¡¢decode ½èÍý¤¬ c_len[] ¤ÎÃÍ -¤«¤é·×»»¤·¤Æµá¤á¤Æ¤¤¤ë¤Î¤Ç¤Ï¤Ê¤¤¤«¤È»×¤ï¤ì¤ë¡£¤³¤ì¤Ï decode ½èÍý¤Î²òÆÉ -¤ÇÌÀ¤é¤«¤Ë¤Ê¤ë¤À¤í¤¦¡£ +こうして、文字の Huffman 符号表は、pt_len[] と pt_code[](pt_code[] は +つまり c_len[] の Huffman 符号)を出力することで表現される。c_code[] が +出力されてないと思うかもしれないが、おそらく、decode 処理が c_len[] の値 +から計算して求めているのではないかと思われる。これは decode 処理の解読 +で明らかになるだろう。 -# ¾¯¤·ÊѤʤ³¤È¤ò½ñ¤¤¤Æ¤¤¤ë¡£pt_code[] ¤Î½ÐÎϤϡ¢c_len[] ¤Î Huffman Éä¹æ -# ¤ò½ÐÎϤ·¤Æ¤¤¤ë¤Î¤Ç¤¢¤ê¡¢Huffman ÌڤξðÊó¤È¤·¤Æ½ÐÎϤ·¤Æ¤¤¤ë¤ï¤±¤Ç¤Ï¤Ê -# ¤¤¡£¤Ä¤Þ¤ê¡¢c_code[] ¤¬½ÐÎϤµ¤ì¤Æ¤¤¤Ê¤¤¤Î¤ÈƱÍÍ¤Ë pt_code[] ¤â½ÐÎÏ¤Ï -# ¤·¤Æ¤¤¤Ê¤¤¡£ +# 少し変なことを書いている。pt_code[] の出力は、c_len[] の Huffman 符号 +# を出力しているのであり、Huffman 木の情報として出力しているわけではな +# い。つまり、c_code[] が出力されていないのと同様に pt_code[] も出力は +# していない。 -¤³¤Î¸å¡¢send_block() ¤Ï¡¢(C) ¤Ç¡¢ ¤Î Huffman Éä¹æɽ¤ò½ÐÎϤ¹¤ë¤Î¤À -¤¬¡¢ +この後、send_block() は、(C) で、 の Huffman 符号表を出力するのだ +が、 write_pt_len(np, pbit, -1); -¤³¤ì¤Ï¡¢Àè¤Î pt_len[] ¤Î½ÐÎÏ¥Õ¥©¡¼¥Þ¥Ã¥È¤ÈƱ¤¸¤Ê¤Î¤Ç¾ÜºÙ¤Ï¤Ï¤·¤ç¤í¤¦¡£ -¤¿¤À¤·¡¢º£Å٤Πpt_len[] ¤Î½ÐÎÏ¤Ç¤Ï write_pt_len() ¤ÎÂè»°°ú¿ô i_special -¤¬ -1 ¤Ç»ØÄꤵ¤ì¤Æ¤¤¤Æ¡¢i_special ÈÖÌܤΠpt_len[i_special..5] ¤Ë´Ø¤·¤Æ -ÆÃÊÌ°·¤¤¤¬¤Ê¤¯¤Ê¤Ã¤Æ¤¤¤ë¤È¤³¤í¤¬°Û¤Ê¤ë¡£ +これは、先の pt_len[] の出力フォーマットと同じなので詳細ははしょろう。 +ただし、今度の pt_len[] の出力では write_pt_len() の第三引数 i_special +が -1 で指定されていて、i_special 番目の pt_len[i_special..5] に関して +特別扱いがなくなっているところが異なる。 -np ¤ä pbit ¤Î°ÕÌ£¤â¤³¤Î»þÅÀ¤Ç¤ï¤«¤ë¤Î¤Ç°ì±þÀâÌÀ¤·¤Æ¤ª¤¯¡£np, pbit ¤½¤· -¤Æ¡¢LHA ¤Î°µ½Ì method ¤È¤Î´Ø·¸¤Ï°Ê²¼¤Îɽ¤ÎÄ̤ê¤Ê¤Î¤À¤¬¡¢np ¤Ï¡¢ ¤Î -ºÇÂç bit Ĺ + 1 ¤À¡£ ¤ÎºÇÂç bit Ĺ¤Ï¤¹¤Ê¤ï¤Á dicbit ¤Ê¤Î¤Ç¡¢np ¤Ï¡¢ -dicbit + 1 ¤Ç¤¢¤ë¡£-lh4- ¤Î¤È¤­¤Ë dicbit + 2 ¤Ê¤Î¤¬ÉԻ׵ĤÀ¤¬¡¢¤³¤ì¤ÏÎò -»ËŪ¤ÊÍýͳ¤À¤í¤¦¤È»×¤ï¤ì¤ë¡£pbit ¤Ï¡¢ÃÍ np ¤ò½ÐÎϤ¹¤ë¤Î¤ËɬÍ×¤Ê bit ¿ô -¤Ê¤Î¤Çɽ¤ÎÄ̤ê¤Ë¤Ê¤ë¡£ +np や pbit の意味もこの時点でわかるので一応説明しておく。np, pbit そし +て、LHA の圧縮 method との関係は以下の表の通りなのだが、np は、 の +最大 bit 長 + 1 だ。 の最大 bit 長はすなわち dicbit なので、np は、 +dicbit + 1 である。-lh4- のときに dicbit + 2 なのが不思議だが、これは歴 +史的な理由だろうと思われる。pbit は、値 np を出力するのに必要な bit 数 +なので表の通りになる。 method dicbit np pbit -------------------------- @@ -4568,11 +4568,11 @@ dicbit + 1 -lh6- 15 16 5 -lh7- 16 17 5 -¤Þ¤È¤á¤ë¤È LHA ¤Ë¤ª¤±¤ë°µ½Ì¥Õ¥¡¥¤¥ë¤Î¹½Â¤¤Ï°Ê²¼¤ÎϢ³¤Ç¤¢¤ë¤È¸À¤¨¤ë¤è -¤¦¤À¡£ +まとめると LHA における圧縮ファイルの構造は以下の連続であると言えるよ +うだ。 ---------------------------------------------------------------------------- -< LHA ¥Õ¥¡¥¤¥ë¤Î¹½Â¤(1 ¥Ö¥í¥Ã¥¯Ê¬) > +< LHA ファイルの構造(1 ブロック分) > +-----------+ | blocksize | @@ -4580,48 +4580,48 @@ dicbit + 1 16bit +-----+--------------------+ - | len | pt_len | c_len¤Î¥Ï¥Õ¥Þ¥óÉä¹æɽ + | len | pt_len | c_lenのハフマン符号表 +-----+--------------------+ 5bit ?? bit TBIT +-------+------------------+ - | len | c_len | ʸ»ú¤ÈŤµ¤Î¥Ï¥Õ¥Þ¥óÉä¹æɽ + | len | c_len | 文字と長さのハフマン符号表 +-------+------------------+ 9bit ?? bit CBIT +---------+--------------------+ - | len | pt_len | °ÌÃ֤ΥϥեޥóÉä¹æɽ + | len | pt_len | 位置のハフマン符号表 +---------+--------------------+ pbit ?? bit (pbit=4bit(lh4,5) or 5bit(lh6,7)) +---------------------+ - | °µ½Ìʸ | + | 圧縮文 | +---------------------+ ---------------------------------------------------------------------------- -¤³¤³¤Þ¤Ç¤Î²òÆɤǤϺÙÉô¤ò¤«¤Ê¤ê¤Ï¤·¤ç¤Ã¤¿¤¬¡¢decode ½èÍý¤ò¸«¤ì¤Ð¤ï¤«¤ë -¤³¤È¤â¤¢¤ë¤Ç¤¢¤í¤¦¤³¤È¤ò´üÂÔ¤·¤Æ¤¤¤ë¡£°Ê¹ß¡¢decode ½èÍý¤ÎÆâÍƤò½èÍý¤Î -ή¤ì¤òÄɤ¦¤³¤È¤Ç³Îǧ¤·¤è¤¦¡£ +ここまでの解読では細部をかなりはしょったが、decode 処理を見ればわかる +こともあるであろうことを期待している。以降、decode 処理の内容を処理の +流れを追うことで確認しよう。 -¤Ç¤Ï¡¢¤¤¤è¤¤¤è decode ½èÍý¤Î²òÆɤËÆþ¤ë¡£¤³¤ì¤¬½ª¤ì¤Ð LHA ¤Î½èÍý¤ÎÁ´ËÆ -¤ò°ì±þ¤ÏÌÖÍ夷¤¿¤³¤È¤Ë¤Ê¤ë¤Î¤Ç¡¢µ¤¹ç¤¤¤òÆþ¤ì¤Æ¿Ê¤á¤è¤¦¡£ +では、いよいよ decode 処理の解読に入る。これが終れば LHA の処理の全貌 +を一応は網羅したことになるので、気合いを入れて進めよう。 -decode ½èÍý¤Ï°Ê²¼¤Î´Ø¿ô¤«¤éÀ®¤Ã¤Æ¤¤¤ë¡£¤³¤ì¤é¤Ï¡¢slide.c ¤Î decode ´Ø -¿ô¤«¤é¸Æ¤Ð¤ì¤Æ¤¤¤ë¡£ +decode 処理は以下の関数から成っている。これらは、slide.c の decode 関 +数から呼ばれている。 -huf.c:decode_c_st1() /* ʸ»ú¡¢Ä¹¤µ¤Î decode ½èÍý */ -huf.c:decode_p_st1() /* °ÌÃ֤Πdecode ½èÍý */ -huf.c:decode_start_st1() /* decode ½èÍý¤Î½é´ü²½ */ +huf.c:decode_c_st1() /* 文字、長さの decode 処理 */ +huf.c:decode_p_st1() /* 位置の decode 処理 */ +huf.c:decode_start_st1() /* decode 処理の初期化 */ - (¼ÂºÝ¤Ë¤Ï¡¢struct decode_option ¤Î decode_c, - decode_p, decode_start ¤ò²ð¤·¤Æ¸Æ¤Ð¤ì¤ë) + (実際には、struct decode_option の decode_c, + decode_p, decode_start を介して呼ばれる) -decode_start_st1() ¤Ï¡¢°Ê²¼¤ÎÄ̤ê¤À¡£¤³¤ì¤Ï encode_start_st1() ¤Î¤È¤­ -¤ÈÆÃÊÌÊѤï¤ë½ê¤Ï¤Ê¤¤¡£ÆäËÀâÌÀ¤ÎɬÍפϤʤ¤¤À¤í¤¦¡£ +decode_start_st1() は、以下の通りだ。これは encode_start_st1() のとき +と特別変わる所はない。特に説明の必要はないだろう。 void decode_start_st1( /* void */ ) @@ -4642,7 +4642,7 @@ decode_start_st1( /* void */ ) blocksize = 0; } -¤Ç¤Ï¡¢decode_c_st1() ¤ò¸«¤è¤¦¡£ +では、decode_c_st1() を見よう。 unsigned short decode_c_st1( /*void*/ ) @@ -4674,7 +4674,7 @@ decode_c_st1( /*void*/ ) return j; } -blocksize == 0 ¤Î¾ì¹ç¤Ë +blocksize == 0 の場合に if (blocksize == 0) { blocksize = getbits(16); @@ -4683,18 +4683,18 @@ blocksize == 0 read_pt_len(np, pbit, -1); } -¤È¡¢¤¤¤í¤¤¤í¤È¤ä¤Ã¤Æ¤¤¤ë¤¬¤³¤ÎÉôʬ¤Ï¤¹¤°ÁÛÁü¤¬¤Ä¤¯¡£< LHA ¥Õ¥¡¥¤¥ë¤Î¹½ -¤ > ¤Î¥Ï¥Õ¥Þ¥óÉä¹æɽ¤òÆɤ߹þ¤ó¤Ç¤¤¤ë¤Î¤À¤í¤¦¡£¤½¤¦¤·¤Æ¡¢°ìÅÙÆɤ߹þ¤ó¤À -¸å¤Ï¸å³¤Î½èÍý¤Ç blocksize ʬÆɤ߹þ¤à¤Þ¤Ç decode ¤ò¹Ô¤¦¤À¤±¤À¡£ +と、いろいろとやっているがこの部分はすぐ想像がつく。< LHA ファイルの構 +造 > のハフマン符号表を読み込んでいるのだろう。そうして、一度読み込んだ +後は後続の処理で blocksize 分読み込むまで decode を行うだけだ。 blocksize--; j = c_table[bitbuf >> 4]; -decode ½èÍý¤Ï¥Ï¥Õ¥Þ¥óÉä¹æɽ¤òɽ°ú¤­¤¹¤ë¤À¤±¤Ê¤Î¤Çñ½ã¤À¡£bitbuf >> 4 -¤Ï¡¢bitbuf >> (16 - 12) ¤ÈÆɤßÊѤ¨¤¿Êý¤¬¤ï¤«¤ê¤ä¤¹¤¤¡£¤³¤ì¤Ï°ÊÁ°²¿ÅÙ¤â -½Ð¤¿·Á¤À¤¬ bitbuf ¤Î¾å°Ì 12 bit ¤ò¼è¤ê½Ð¤·¤Æ¤¤¤ë¡£¤½¤¦¤·¤Æ¤½¤ÎÃÍ(¥Ï¥Õ -¥Þ¥óÉä¹æ)¤ò¸µ¤Ëɽ°ú¤­¤·¤¿·ë²Ì j ¤¬Éü¹æ¤·¤¿Ê¸»ú¤È¤Ê¤ë¡£¤Ê¤¼ 12 bit ¤Ê¤Î¤« -¤Ï¤è¤¯¤ï¤«¤é¤Ê¤¤¸å¤Ç¹Í¤¨¤è¤¦¡£¤³¤Î¸å¤ÎÉôʬ¤Ç¡¢ +decode 処理はハフマン符号表を表引きするだけなので単純だ。bitbuf >> 4 +は、bitbuf >> (16 - 12) と読み変えた方がわかりやすい。これは以前何度も +出た形だが bitbuf の上位 12 bit を取り出している。そうしてその値(ハフ +マン符号)を元に表引きした結果 j が復号した文字となる。なぜ 12 bit なのか +はよくわからない後で考えよう。この後の部分で、 if (j < NC) fillbuf(c_len[j]); @@ -4712,43 +4712,43 @@ decode } return j; -j < NC ¤Î¾ì¹ç¤Ï c_len[j] ¤Ç¥Ï¥Õ¥Þ¥óÉä¹æ¤Î¥Ó¥Ã¥ÈĹʬ¤ò fillbuf() ¤·¤Æ¤¤ -¤ë¡£¤Ä¤Þ¤êÀèÄøɽ°ú¤­¤·¤¿ 12 bit ¤Î¤¦¤Á c_len[j] bit ¤¬ËÜÅö¤Î¥Ï¥Õ¥Þ¥óÉä -¹æ¤Ê¤Î¤À¤¬¡¢É½°ú¤­¤ÎºÝ¤Ë¼ÂºÝ¤Î¥Ó¥Ã¥ÈŤòµ¤¤Ë¤¹¤ëɬÍפ¬¤Ê¤¤¤Î¤¬ÆÃħŪ¤À¡£ +j < NC の場合は c_len[j] でハフマン符号のビット長分を fillbuf() してい +る。つまり先程表引きした 12 bit のうち c_len[j] bit が本当のハフマン符 +号なのだが、表引きの際に実際のビット長を気にする必要がないのが特徴的だ。 -else ¤ÎÉôʬ¤Ï¡¢j ¤òµá¤áľ¤·¤Æ¤¤¤ë¤³¤È¤«¤é¡¢¤É¤¦¤ä¤éÉä¹æɽ¤«¤é¤Îɽ°ú¤­ -¤Ç¤ÏÉü¹æ¤Ç¤­¤Ê¤¤¾ì¹ç¤òɽ¤·¤Æ¤¤¤ë¤é¤·¤¤¡£¤³¤Î¾ì¹ç¡¢É½°ú¤­¤Ë»ÈÍѤ·¤¿ 12 -bit ¤ò¼Î¤Æ(fillbuf(12))¡¢¥Ï¥Õ¥Þ¥óÌÚ(left[], right[])¤òé¤ë»ö¤Ç¡¢Éü¹æ¤ò -¹Ô¤Ã¤Æ¤¤¤ë¡£¤³¤Î¸å¡¢fillbuf(c_len[j] - 12) ¤·¤Æ¤¤¤ë¤³¤È¤«¤é¡¢Éä¹æĹ¤Ï -12 bit °Ê¾å¤¢¤ë¤Î¤À¤í¤¦¡£ +else の部分は、j を求め直していることから、どうやら符号表からの表引き +では復号できない場合を表しているらしい。この場合、表引きに使用した 12 +bit を捨て(fillbuf(12))、ハフマン木(left[], right[])を辿る事で、復号を +行っている。この後、fillbuf(c_len[j] - 12) していることから、符号長は +12 bit 以上あるのだろう。 -decode_c_st1() ¤¬ decode ¤¹¤ë°µ½Ìʸ¤Î¹½Â¤¤Ï¿Þ¤Çɽ¤¹¤È°Ê²¼¤Î¤è¤¦¤Ë¤Ê¤ë +decode_c_st1() が decode する圧縮文の構造は図で表すと以下のようになる ---------------------------------------------------------------------------- -j < NC ¤Î¾ì¹ç (c_len[j] < 12 ¤Î¾ì¹ç) +j < NC の場合 (c_len[j] < 12 の場合) <- c_len[j] -> <----- 12 -------> +--------------+---------- -°µ½Ìʸ | ¥Ï¥Õ¥Þ¥óÉä¹æ | +圧縮文 | ハフマン符号 | +--------------+---------- -j >= NC ¤Î¾ì¹ç (c_len[j] > 12 ¤Î¾ì¹ç) +j >= NC の場合 (c_len[j] > 12 の場合) <------------ c_len[j] ---------> <------ 12 ------> +------------------+--------------+-------- -°µ½Ìʸ | root | ¥Ï¥Õ¥Þ¥óÉä¹æ | +圧縮文 | root | ハフマン符号 | +------------------+--------------+-------- - root: ¥Ï¥Õ¥Þ¥óÌڤκ¬ + root: ハフマン木の根 ---------------------------------------------------------------------------- -¤Ï¤¿¤·¤Æ¡¢°µ½Ì½èÍý¤Î¤È¤­¤Ë¤³¤Î¤è¤¦¤Ê¹½Â¤¤òºî¤Ã¤¿³Ð¤¨¤Ï¤Ê¤¤¤Î¤À¤¬¤É¤¦¤¤ -¤¦¤³¤È¤À¤í¤¦¡©²ÝÂê¤ò»Ä¤·¤Ä¤Äº£ÅÙ¤Ï decode_p_st1() (°ÌÃÖ¤ÎÉü¹æ½èÍý)¤ò¸« -¤ë¡£ +はたして、圧縮処理のときにこのような構造を作った覚えはないのだがどうい +うことだろう?課題を残しつつ今度は decode_p_st1() (位置の復号処理)を見 +る。 unsigned short decode_p_st1( /* void */ ) @@ -4775,53 +4775,53 @@ decode_p_st1( /* void */ ) return j; } -ÀèÄø¤ÈƱ¤¸¤À¡£º£Å٤ϡ¢bitbuf ¤Î¤¦¤Á 8 bit ¤ò»ÈÍѤ·¤Æɽ°ú¤­¤ò¹Ô¤¤¡¢ -j < np ¤Ê¤é pt_len[j] ¤òµÍ¤á¡¢¤½¤¦¤Ç¤Ê¤±¤ì¤Ð¥Ï¥Õ¥Þ¥óÌÚ¤òé¤Ã¤Æ¤¤¤ë¡£ -Éü¹æ¤·¤¿ j ¤Ï°ÌÃÖ¤òɽ¤¹ÃͤΠbit Ĺ¤Ê¤Î¤ÇºÇ¸å¤Ë +先程と同じだ。今度は、bitbuf のうち 8 bit を使用して表引きを行い、 +j < np なら pt_len[j] を詰め、そうでなければハフマン木を辿っている。 +復号した j は位置を表す値の bit 長なので最後に j = (1 << (j - 1)) + getbits(j - 1); -¤Ç¡¢ËÜÅö¤Î°ÌÃÖ¤ÎÃͤòÆɤó¤Ç¤¤¤ë(encode_p() ¤¬¤½¤¦¤¤¤¦½èÍý¤À¤Ã¤¿»ö¤ò»×¤¤ -½Ð¤½¤¦)¡£ +で、本当の位置の値を読んでいる(encode_p() がそういう処理だった事を思い +出そう)。 -decode_p_st1() ¤¬ decode ¤¹¤ë°µ½Ìʸ¤Î¹½Â¤¤Ï¿Þ¤Çɽ¤¹¤È°Ê²¼¤Î¤è¤¦¤Ë¤Ê¤ë +decode_p_st1() が decode する圧縮文の構造は図で表すと以下のようになる ---------------------------------------------------------------------------- -j < np ¤Î¾ì¹ç (pt_len[j] < 8 ¤Î¾ì¹ç) +j < np の場合 (pt_len[j] < 8 の場合) <- pt_len[j] -> <------ 8 -------> +--------------+---------- -°µ½Ìʸ | ¥Ï¥Õ¥Þ¥óÉä¹æ | +圧縮文 | ハフマン符号 | +--------------+---------- -j >= np ¤Î¾ì¹ç (pt_len[j] > 8 ¤Î¾ì¹ç) +j >= np の場合 (pt_len[j] > 8 の場合) <----------- pt_len[j] ---------> <------ 8 -------> +------------------+--------------+----------+---------- -°µ½Ìʸ | root | ¥Ï¥Õ¥Þ¥óÉä¹æ | °ÌÃÖ¤ÎÃÍ | +圧縮文 | root | ハフマン符号 | 位置の値 | +------------------+--------------+----------+---------- - root: ¥Ï¥Õ¥Þ¥óÌڤκ¬ + root: ハフマン木の根 ---------------------------------------------------------------------------- -°Ê¾å¤¬¡¢decode ½èÍý¤Î³µÍפÀ¡£¤³¤³¤Þ¤Ç¤Î½èÍý¤ÏÊ̤ˤɤ¦¤È¤¤¤¦»ö¤â¤Ê¤¤¤À -¤í¤¦¡£decode ½èÍý¤Î¥­¥â¤Ï¡¢°µ½Ìʸ¤ËËä¤á¹þ¤ó¤À¥Ï¥Õ¥Þ¥óÉä¹æɽ¤ÎÆɤ߹þ¤ß -¤Ë¤¢¤ë¡£blocksize == 0 ¤Î¤È¤­¤Ë¡¢decode_c_st1() ¤Ç¸Æ¤Ð¤ì¤ë -read_pt_len(), read_c_len() ¤À¡£¤³¤ì¤Ë¤è¤ê¡¢decode ½èÍý¤Ç»ÈÍѤ¹¤ë¥Æ¡¼¥Ö¥ë +以上が、decode 処理の概要だ。ここまでの処理は別にどうという事もないだ +ろう。decode 処理のキモは、圧縮文に埋め込んだハフマン符号表の読み込み +にある。blocksize == 0 のときに、decode_c_st1() で呼ばれる +read_pt_len(), read_c_len() だ。これにより、decode 処理で使用するテーブル -c_table[] ¥Ï¥Õ¥Þ¥óÉä¹æ -> ʸ»ú¤ÎÊÑ´¹¥Æ¡¼¥Ö¥ë -c_len[] ¥Ï¥Õ¥Þ¥óÉä¹æ -> ¥Ï¥Õ¥Þ¥óÉä¹æ¤Î¥Ó¥Ã¥ÈŤÎÂбþ -pt_table[] ¥Ï¥Õ¥Þ¥óÉä¹æ -> °ÌÃ֤ΥӥåÈŤÎÊÑ´¹¥Æ¡¼¥Ö¥ë -pt_len[] ¥Ï¥Õ¥Þ¥óÉä¹æ -> ¥Ï¥Õ¥Þ¥óÉä¹æ¤Î¥Ó¥Ã¥ÈŤÎÂбþ -left[] ¥Ï¥Õ¥Þ¥óÌÚ(º¸¤Î¥Î¡¼¥É) -right[] ¥Ï¥Õ¥Þ¥óÌÚ(±¦¤Î¥Î¡¼¥É) +c_table[] ハフマン符号 -> 文字の変換テーブル +c_len[] ハフマン符号 -> ハフマン符号のビット長の対応 +pt_table[] ハフマン符号 -> 位置のビット長の変換テーブル +pt_len[] ハフマン符号 -> ハフマン符号のビット長の対応 +left[] ハフマン木(左のノード) +right[] ハフマン木(右のノード) -¤¬¹½ÃÛ¤µ¤ì¤ë¡£¤³¤ÎÉôʬ¤ÎÊý¤¬ decode ½èÍýËÜÂΤè¤ê¤ä¤ä¤³¤·¤½¤¦¤À¡£ -¤Ç¤Ï¡¢¤³¤ì¤é¤ò¸«¤Æ¹Ô¤³¤¦¡£ +が構築される。この部分の方が decode 処理本体よりややこしそうだ。 +では、これらを見て行こう。 if (blocksize == 0) { blocksize = getbits(16); @@ -4830,7 +4830,7 @@ right[] read_pt_len(np, pbit, -1); } -ºÇ½é¤Ï¡¢read_pt_len(NT, TBIT, 3) ¤«¤é +最初は、read_pt_len(NT, TBIT, 3) から static void read_pt_len(nn, nbit, i_special) @@ -4873,8 +4873,8 @@ read_pt_len(nn, nbit, i_special) } } -¼ÂºÝ¡¢Â礷¤¿»ö¤Ï¤Ê¤¤¡£< pt_len[] ¤Î½ÐÎÏ¥Õ¥©¡¼¥Þ¥Ã¥È > ¤Ë¤·¤¿¤¬¤Ã¤Æ¡¢ -pt_len[] ¤òÆɤßľ¤·¤Æ¤¤¤ë¤À¤±¤À¡£read_c_len() ¤â¸«¤è¤¦¡£ +実際、大した事はない。< pt_len[] の出力フォーマット > にしたがって、 +pt_len[] を読み直しているだけだ。read_c_len() も見よう。 static void read_c_len( /* void */ ) @@ -4922,23 +4922,23 @@ read_c_len( /* void */ ) } } -¤³¤Á¤é¤â¡¢< c_len[] ¤Î½ÐÎÏ¥Õ¥©¡¼¥Þ¥Ã¥È > ¤Ë¤·¤¿¤¬¤Ã¤Æ¡¢c_len[] ¤òÆÉ¤ß -ľ¤·¤Æ¤¤¤ë¤À¤±¤À¡£ -# ¤³¤Î¤¢¤¿¤ê¤Ë¤Ê¤ë¤È²òÀϤ¬¤«¤Ê¤ê»¨¤Ë¤Ê¤Ã¤Æ¤¤¤ë(Åö»þÈè¤ì¤Æ¤¤¤¿¤Î¤À¤í¤¦)¡£ -# ºÇ½ªÅª¤Ë¤Ï¸å½Ò¤Î¡ÖLHA ¥Õ¥¡¥¤¥ë¤Î¹½Â¤(¤Þ¤È¤á)¡×¤Ë¤Æ¡¢ºÆÅÙ¸¡Æ¤¤·¤Ê¤ª¤· -# ¤Æ¤¤¤ë¤Î¤Ç¤½¤Á¤é¤ò¸«¤ÆÍߤ·¤¤¡£ÉÔÌÀ¤À¤Ã¤¿Éôʬ¤â¤³¤³¤Ç¤¹¤Ù¤ÆÌÀ¤é¤«¤Ë¤· -# ¤Æ¤¤¤ë¡£ -·ë¶É¥­¥â¤È¤Ê¤ë¤Î¤Ï¡¢make_table() ¤Ë¤¢¤ë¤é¤·¤¤¡£¤³¤Î´Ø¿ô¤Ë¤è¤ê¡¢Æɤ߹þ¤ó -¤À pt_len[], c_len[] ¤«¤é pt_table[], c_table[] (¤½¤·¤Æ¡¢¥Ï¥Õ¥Þ¥óÌÚ -left[], right[])¤ò¹½ÃÛ¤·¤Æ¤¤¤ë¤Î¤À¤í¤¦¡£ +こちらも、< c_len[] の出力フォーマット > にしたがって、c_len[] を読み +直しているだけだ。 +# このあたりになると解析がかなり雑になっている(当時疲れていたのだろう)。 +# 最終的には後述の「LHA ファイルの構造(まとめ)」にて、再度検討しなおし +# ているのでそちらを見て欲しい。不明だった部分もここですべて明らかにし +# ている。 +結局キモとなるのは、make_table() にあるらしい。この関数により、読み込ん +だ pt_len[], c_len[] から pt_table[], c_table[] (そして、ハフマン木 +left[], right[])を構築しているのだろう。 -·ë¶É¡¢decode ½èÍý read_c_len(), read_pt_len() ¤òÆɤó¤Ç¤â¤Ê¤¼¤³¤Î¤è¤¦¤Ê -Éä¹æ²½¤ò¹Ô¤Ã¤Æ¤¤¤ë¤Î¤«¤è¤¯¤ï¤«¤é¤Ê¤«¤Ã¤¿¡£²¿¤«Åý·×Ū¤Êº¬µò¤Ç¤â¤¢¤ë¤Î¤À -¤í¤¦¤«¡©¤½¤ì¤È¤â LHA ¤Ë¤È¤Ã¤ÆÎò»ËŪ¤Ê»ö¾ð¤Ç¤â¤¢¤ë¤Î¤«¡©¤³¤ì¤Ë´Ø¤·¤Æ¤Ï -ÊÌÅÓ¸¡¾Ú¤ÎɬÍפ¬¤¢¤ë¤À¤í¤¦¡£ +結局、decode 処理 read_c_len(), read_pt_len() を読んでもなぜこのような +符号化を行っているのかよくわからなかった。何か統計的な根拠でもあるのだ +ろうか?それとも LHA にとって歴史的な事情でもあるのか?これに関しては +別途検証の必要があるだろう。 -¤Ç¤Ï¡¢ºÇ¸å¤Î´Ø¿ô make_table() ¤ò²òÆɤ·¤è¤¦¡£¤³¤ì¤Ï¡¢maketbl.c ¤ÇÄêµÁ¤µ -¤ì¤Æ¤¤¤ë¡£ +では、最後の関数 make_table() を解読しよう。これは、maketbl.c で定義さ +れている。 void make_table(nchar, bitlen, tablebits, table) @@ -5030,7 +5030,7 @@ make_table(nchar, bitlen, tablebits, table) } } -½ç¤Ë¸«¤Æ¹Ô¤³¤¦¡£ +順に見て行こう。 /* (A) */ avail = nchar; @@ -5041,18 +5041,18 @@ make_table(nchar, bitlen, tablebits, table) weight[i] = 1 << (16 - i); } -avail ¤Ï¤ª¤½¤é¤¯ maketree.c:make_tree() ¤Ç¤½¤¦¤Ç¤¢¤Ã¤¿¤è¤¦¤Ë¡¢ÌÚ¤ÎÀá¤Î -½é´üÃͤÀ¤í¤¦¤ÈͽÁÛ¤·¤Æ¤ª¤¯¡£count[], weight[] ¤â¡¢maketree.c ¤Ç¤Î -len_cnt[] weight[] ¤ÈƱµÁ¤À¤í¤¦(¤¹¤Ê¤ï¤Á¡¢count[i] ¤Ï¡¢Ìڤο¼¤µ i ÈÖÌÜ -¤ÎÍդοô¡¢weight[i] ¤Ï½Å¤ß) +avail はおそらく maketree.c:make_tree() でそうであったように、木の節の +初期値だろうと予想しておく。count[], weight[] も、maketree.c での +len_cnt[] weight[] と同義だろう(すなわち、count[i] は、木の深さ i 番目 +の葉の数、weight[i] は重み) /* (B) */ /* count */ for (i = 0; i < nchar; i++) count[bitlen[i]]++; -count[] ¤òµá¤á¤Æ¤¤¤ë¡£bitlen[i] ¤Ï¡¢Ê¸»ú i ¤Î¥Ï¥Õ¥Þ¥óÉä¹æ¤Ç¤Î bit Ĺ¤Ç -¤¢¤Ã¤¿¡£¤ä¤Ï¤ê count[] ¤ÏͽÁÛÄ̤ê¤À¡£ +count[] を求めている。bitlen[i] は、文字 i のハフマン符号での bit 長で +あった。やはり count[] は予想通りだ。 /* (C) */ /* calculate first code */ @@ -5064,9 +5064,9 @@ count[] if ((total & 0xffff) != 0) error("make_table()", "Bad table (5)\n"); -¤³¤ì¤Ï¡¢maketree.c:make_code() ¤ÎÁ°È¾Éôʬ¤È¤Þ¤Ã¤¿¤¯Æ±¤¸¤À¡£¤³¤ì¤Ë¤è¤ê¡¢ -¿¼¤µ i ¤ËÂФ·¤Æ¡¢°Ê²¼¤ÎÂбþɽ¤¬¤Ç¤­¤ë(¤³¤ì¤ÏÁ°¤Ë¤â½ñ¤¤¤¿¡£Li ¤Ï¡¢ -count[i] ¤ÎÃͤòɽ¤·¤Æ¤¤¤ë)¡£ +これは、maketree.c:make_code() の前半部分とまったく同じだ。これにより、 +深さ i に対して、以下の対応表ができる(これは前にも書いた。Li は、 +count[i] の値を表している)。 i count[i] weight[i] start[i] -------------------------------------------- @@ -5075,9 +5075,9 @@ count[i] 3 L3 2^13 2^15 * L1 + 2^14 * L2 4 L4 2^12 2^15 * L1 + 2^14 * L2 + 2^13 * L3 -¤³¤ì¤¬²¿¤òɽ¤¹¤«¤È¸À¤¦¤È¿¼¤µ i ¤ÎÉä¹æ(¤Ä¤Þ¤ê bit Ĺ i ¤ÎÉä¹æ)¤Ï¡¢ -start[i] ¤«¤é start[i+1]-1 ¤ÎÈϰϤÎÃͤò»ý¤Ä¤È¸À¤¦»ö¤ò°ÕÌ£¤¹¤ë¡£ºÆÅÙ¡¢ -Îã¤Ç¼¨¤½¤¦¡£ +これが何を表すかと言うと深さ i の符号(つまり bit 長 i の符号)は、 +start[i] から start[i+1]-1 の範囲の値を持つと言う事を意味する。再度、 +例で示そう。 /\ a: 0 a /\ b: 10 @@ -5089,10 +5089,10 @@ start[i] 2 2 2^14 2^15 3 0 2^13 2^15 + 2^14 * 2 -¤³¤ì¤è¤ê¡¢¿¼¤µ 1 ¤ÎÍÕ a ¤Ï¡¢start[1] .. start[2]-1 ¤Ä¤Þ¤ê¡¢ -00000000 00000000 .. 01111111 11111111 ¤ÎÈϰϤÎÉä¹æ¤È¤Ê¤ë¡£ -¿¼¤µ 2 ¤ÎÍÕ b, c ¤Ï¡¢start[2] .. start[3]-1 ¤Ä¤Þ¤ê¡¢ -10000000 00000000 ... 11111111 11111111 ¤È¤Ê¤ë¡£ +これより、深さ 1 の葉 a は、start[1] .. start[2]-1 つまり、 +00000000 00000000 .. 01111111 11111111 の範囲の符号となる。 +深さ 2 の葉 b, c は、start[2] .. start[3]-1 つまり、 +10000000 00000000 ... 11111111 11111111 となる。 /* (D) */ /* shift data for make table. */ @@ -5102,165 +5102,165 @@ start[i] weight[i] >>= m; } -Íýͳ¤Ï¤ï¤«¤é¤Ê¤¤¤¬¡¢ºîÀ®¤¹¤ë¥Æ¡¼¥Ö¥ë¤ò°ú¿ô¤ÇÅϤµ¤ì¤¿ bit ¿ô¤Î¥Æ¡¼¥Ö¥ë -¤Ë¤Ê¤ë¤è¤¦¼ÌÁü¤·¤Æ¤¤¤ë¡£¤Ä¤Þ¤ê¡¢ÃͤÎÈϰϤνé´üÃÍ start[] ¤È weight[] -¤ò 16 - tablebits ¤Ç¥·¥Õ¥È¤¹¤ë¤³¤È¤Ç¡¢ +理由はわからないが、作成するテーブルを引数で渡された bit 数のテーブル +になるよう写像している。つまり、値の範囲の初期値 start[] と weight[] +を 16 - tablebits でシフトすることで、 01111111 11111111 -¤È¤¤¤¦¥Æ¡¼¥Ö¥ë¤ÎÃͤÏ(tablebits ¤¬ 12 ¤Ç¤¢¤ë¤È¤¹¤ë¤È)¡¢ +というテーブルの値は(tablebits が 12 であるとすると)、 00000111 11111111 - <--> (16 - tablebits{12}) = 4 bit ¥·¥Õ¥È + <--> (16 - tablebits{12}) = 4 bit シフト -¤ÎÃͤˤ¹¤ë¡£encode ¤¹¤ë¤È¤­¤Ï¡¢16 bit ¤Î¥Æ¡¼¥Ö¥ë¤ò¤½¤Î¤Þ¤Þ»ÈÍѤ·¤Æ¤¤¤¿ -¤Ë¤â´Ø¤ï¤é¤º decode ¤Î¤È¤­¤Ë¤Ï¥Æ¡¼¥Ö¥ë¤Î bit ¿ô¤ò¸º¤é¤·¤Æ¤¤¤ë¤Î¤À¡£¤Þ¤Ã -¤¿¤¯Íýͳ¤¬¤ï¤«¤é¤Ê¤¤¡£ +の値にする。encode するときは、16 bit のテーブルをそのまま使用していた +にも関わらず decode のときにはテーブルの bit 数を減らしているのだ。まっ +たく理由がわからない。 -ÅöÁ³¡¢encode ¤Ç»ÈÍѤ·¤Æ¤¤¤¿¤È¤­¤Î¥Æ¡¼¥Ö¥ë¤è¤ê¾ðÊóÎ̤¬¸º¤Ã¤Æ¤¤¤ë¤Î¤Ç¡¢ -¤¹¤Ù¤Æ¤ò¥Æ¡¼¥Ö¥ë»²¾È¤Ç decode ¤¹¤ë¤³¤È¤Ï¤Ç¤­¤Ê¤¤¡£¤½¤³¤Ç¡¢Â­¤ê¤Ê¤¤Éôʬ -¤Ï¸å¤Î½èÍý¤ÇÌÚ¤òºî¤ë¤³¤È¤ÇÊä¤Ã¤Æ¤¤¤ë¤è¤¦¤À¡£ +当然、encode で使用していたときのテーブルより情報量が減っているので、 +すべてをテーブル参照で decode することはできない。そこで、足りない部分 +は後の処理で木を作ることで補っているようだ。 -bit ¿ô¤ò¸º¤é¤¹Íýͳ¤ò¹Í¤¨¤Æ¤ß¤ë¡£¤Þ¤ºÉ½°ú¤­¤Î¹Í¤¨Êý¤Ï¡¢ +bit 数を減らす理由を考えてみる。まず表引きの考え方は、 /\ a: 0 a /\ b: 10 b c c: 11 -¤È¤¤¤¦ Huffman ÌڤˤĤ¤¤Æ¡¢ +という Huffman 木について、 00: c_table[00] = a 01: c_table[01] = a 10: c_table[10] = b 11: c_table[11] = c -¤È¤¤¤¦¥Æ¡¼¥Ö¥ë¡¢¤Ä¤Þ¤ê +というテーブル、つまり - c_table[HuffmanÉä¹æ]=Éü¹æ¸ì + c_table[Huffman符号]=復号語 -¤òºîÀ®¤¹¤ë¤³¤È¤Ç Huffman Éä¹æ²½¤·¤¿¥Ó¥Ã¥ÈÎ󤫤éÉü¹æ¸ì¤òÆÀ¤é¤ì¤ë¤è¤¦¤Ë¤¹ -¤ë¡£¤½¤·¤Æ¡¢Huffman Éä¹æ¤«¤éʸ»ú¤òÆÀ¤ëɬÍפ¬¤¢¤ë¤Î¤Ç¡¢c_table[] ¤Î¥¤¥ó -¥Ç¥Ã¥¯¥¹¤Ë¤Ï¡¢Huffman Éä¹æ¤¬¼è¤ê¤¦¤ëºÇÂçÃͤò»ØÄê¤Ç¤­¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£ -16 bit ¤ÎHuffman Éä¹æ¤ÎºÇÂçÃÍ¤Ï 65535 ¤Ç¤¢¤ë¤«¤éɽ°ú¤­¤ËɬÍפÊÊÑ¿ô¤Ï¡¢ +を作成することで Huffman 符号化したビット列から復号語を得られるようにす +る。そして、Huffman 符号から文字を得る必要があるので、c_table[] のイン +デックスには、Huffman 符号が取りうる最大値を指定できなければならない。 +16 bit のHuffman 符号の最大値は 65535 であるから表引きに必要な変数は、 unsigned short c_table[65535+1]; (unsigned short >= NC-1) -¤È¤Ê¤ë¡£¤ª¤½¤é¤¯É½¤Î bit ¿ô¤ò¸º¤é¤·¤Æ¤¤¤ë¤Î¤Ï¤³¤ÎÂ礭¤Ê¥Æ¡¼¥Ö¥ë¤òºî¤ê¤¿ -¤¯¤Ê¤¤¤«¤é¤Ç¤Ï¤Ê¤¤¤«¤È»×¤ï¤ì¤ë¡£c_table[] ¤ÎÍ×ÁÇ¿ô¤òºÇÄã¸ÂɬÍפʿô¤ËÍÞ -¤¨¤è¤¦¤È¤¹¤ë¤È°Ê²¼¤Î¤è¤¦¤Ë¤Ê¤ë¤¬¡¢ +となる。おそらく表の bit 数を減らしているのはこの大きなテーブルを作りた +くないからではないかと思われる。c_table[] の要素数を最低限必要な数に抑 +えようとすると以下のようになるが、 0: c_table[ 0=0] = a 10: c_table[10=2] = b 11: c_table[11=3] = c -¤³¤ÎÎã¤Î¾ì¹ç¡¢c_table[1] ¤¬¶õ¤Ç¤¢¤ë¤è¤¦¤Ê¥Æ¡¼¥Ö¥ë¤òºîÀ®¤·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê -¤¤¡£¥Ï¥Ã¥·¥åɽ¤òºî¤ì¤Ð²Äǽ¤À¤¬¤½¤¦¤Ï¤»¤º¡¢c_table[] ¤ÎÍ×ÁÇ¿ô¤ò¸º¤é¤·¡¢ -ɽ°ú¤­¤Çɽ¤»¤Ê¤¤Éôʬ¤ÏÌÚ¤ÇÊä¤Ã¤Æ¤¤¤ë¤È¤¤¤¦¤³¤È¤À¡£ +この例の場合、c_table[1] が空であるようなテーブルを作成しなければならな +い。ハッシュ表を作れば可能だがそうはせず、c_table[] の要素数を減らし、 +表引きで表せない部分は木で補っているということだ。 -¼ÂºÝ¤Î c_table[] ¤ÎÄêµÁ¤Ï¡¢ +実際の c_table[] の定義は、 unsigned short c_table[4095+1]; -¤È¤Ê¤Ã¤Æ¤ª¤ê¡¢12 ¥Ó¥Ã¥È(2^12=4096)¤Î Huffman Éä¹æ¤Ë¤Ä¤¤¤Æɽ°ú¤­¤¬²Äǽ¤È -¤Ê¤Ã¤Æ¤¤¤ë¡£¤½¤·¤Æ¡¢Éü¹æ¸ì¤Ï¡¢0...NC ¤ÎÈϰϤǤ¢¤ë¤«¤é¡¢ - c_table[¥Ó¥Ã¥ÈÎó] < NC -¤Î¾ì¹ç¤Ï¡¢¤½¤Î¤Þ¤Þɽ°ú¤­¤Ç - c_table[¥Ó¥Ã¥ÈÎó] >= NC -¤Î¾ì¹ç¤Ï¡¢ÌڤΥ롼¥È¥Î¡¼¥É¤ò¼¨¤·¡¢¤½¤ÎÃͤ«¤éÌÚ¤òé¤Ã¤ÆÉü¹æ¤Ç¤­¤ë¤è¤¦¤Ë -¤Ê¤Ã¤Æ¤¤¤ë¡£¿Þ¼¨¤¹¤ë¤È +となっており、12 ビット(2^12=4096)の Huffman 符号について表引きが可能と +なっている。そして、復号語は、0...NC の範囲であるから、 + c_table[ビット列] < NC +の場合は、そのまま表引きで + c_table[ビット列] >= NC +の場合は、木のルートノードを示し、その値から木を辿って復号できるように +なっている。図示すると ---------------------------------------------------------------------------- . / \ - a . ... ³¬ÁØ:1 `. + a . ... 階層:1 `. / \ | - b ... ³¬ÁØ:2 `> ɽ°ú¤­ + b ... 階層:2 `> 表引き : ' : | - . ... ³¬ÁØ:11 | + . ... 階層:11 | / \ | - x y ... ³¬ÁØ:12 .' <- ɽ°ú¤­¤Î·ë²Ì y ¤¬(>=NC)¤Î¾ì¹ç¡¢ - / \ Ìڤγ¤­¤¬¤¢¤ë¤³¤È¤ò¼¨¤¹ - z . ... ³¬ÁØ:13 `. + x y ... 階層:12 .' <- 表引きの結果 y が(>=NC)の場合、 + / \ 木の続きがあることを示す + z . ... 階層:13 `. | - : > left[]/right[]¤Çɽ¸½ (³¬ÁØ12¤¬root) - ... ³¬ÁØ:16 .' + : > left[]/right[]で表現 (階層12がroot) + ... 階層:16 .' ---------------------------------------------------------------------------- -³¬ÁØ 12 ¤è¤ê²¼(13 °Ê¾å¤Î bit ¿ô¤Î Huffman Éä¹æ)¤Î Huffman ÌڤˤĤ¤¤Æ -left[], right[] ¤Çɽ¤·¤Æ¤¤¤ë¤è¤¦¤À¡£ +階層 12 より下(13 以上の bit 数の Huffman 符号)の Huffman 木について +left[], right[] で表しているようだ。 -¾å¤ÎÎã¤Ç¡¢13 ¥Ó¥Ã¥È¤Î Huffman Éä¹æ¤ÇÉä¹æ²½¤µ¤ì¤ëÉü¹æ¸ì z ¤Ë¤Ä¤¤¤Æ¤Ï¡¢ -y ¤Ë¤½¤ÎÌڤΥ롼¥È¥Î¡¼¥É¤ÎÃÍ(y > NC)¤¬³ä¤êÅö¤Æ¤é¤ì¤ë¡£ +上の例で、13 ビットの Huffman 符号で符号化される復号語 z については、 +y にその木のルートノードの値(y > NC)が割り当てられる。 -¿Þ¤«¤éÌÀ¤é¤«¤À¤¬¡¢Éü¹æ¸ì¤½¤Î¤â¤Î¤ò»Ø¤¹ 12 bit ¤ÎÉä¹æ(¤¿¤È¤¨¤Ð x)¤È¡¢ÌÚ -¤Î¥ë¡¼¥È¥Î¡¼¥É¤ò»Ø¤¹ 12 bit ¤ÎÉä¹æ(¤¿¤È¤¨¤Ð y)¤¬Æ±¤¸Éä¹æ¤È¤Ê¤ë¤³¤È¤Ï¤Ê -¤¤¡£¤³¤ì¤Ï Huffman Éä¹æ¤¬¸ìƬ¾ò·ï¤òËþ¤¿¤¹¤«¤é¤Ç¤¢¤ë¡£ +図から明らかだが、復号語そのものを指す 12 bit の符号(たとえば x)と、木 +のルートノードを指す 12 bit の符号(たとえば y)が同じ符号となることはな +い。これは Huffman 符号が語頭条件を満たすからである。 -¤Ç¤Ï¡¢y ¤Ë³ä¤êÅö¤Æ¤ëÃͤϤɤΤ褦¤Ë·è¤Þ¤ë¤«¤È¤¤¤¦¤È¤ª¤½¤é¤¯ NC °Ê¾å¤Î¿ô -ÃͤòÏ¢ÈÖ¤«²¿¤«¤Ç³ä¤êÅö¤Æ¤Æ¤¤¤ë¤Î¤Ç¤Ï¤Ê¤¤¤«¤È»×¤ï¤ì¤ë¡£¤³¤ì¤Ë¤Ä¤¤¤Æ¤Ï¤³ -¤ì°Ê¹ß¤Ç³Îǧ¤¹¤ë¡£ +では、y に割り当てる値はどのように決まるかというとおそらく NC 以上の数 +値を連番か何かで割り当てているのではないかと思われる。これについてはこ +れ以降で確認する。 -¤Ê¤ª¡¢³¬Áؤ¬¿¼¤¤ Huffman ÌڤȤϡ¢bit Ť¬Ä¹¤¤Éä¹æ¤Ç¤¢¤ê¡¢bit Ť¬Ä¹¤¤¤È -¤¤¤¦¤³¤È¤Ï½Ð¸½ÉÑÅÙ¤¬Ä㤤¤Ï¤º¤Ç¤¢¤ë¤«¤é¡¢left[], right[] ¤ÇÌÚ¤òé¤ë²ó¿ô -¤Ï¾¯¤Ê¤¤¤Ï¤º¤Ç¤¢¤ë¡£¤³¤ì¤ÏÍý¤Ë¤«¤Ê¤Ã¤Æ¤¤¤ë¡£ +なお、階層が深い Huffman 木とは、bit 長が長い符号であり、bit 長が長いと +いうことは出現頻度が低いはずであるから、left[], right[] で木を辿る回数 +は少ないはずである。これは理にかなっている。 -¤¢¤È¡¢¤³¤ÎÌÚ¤ËɬÍפʥµ¥¤¥º¤Ï¤¤¤¯¤Ä¤Ç¤¢¤ë¤«¤È¤¤¤¦ÅÀ¤¬µ¤¤Ë¤Ê¤ë¡£¤³¤ì¤Ï¡¢ -³¬ÁØ 13 °Ê¾å¤Î¥Î¡¼¥É¤Î¿ô¤È¤Ê¤ë¤¬¡¢³¬ÁØ n ¤ÎÆóʬÌڤΥΡ¼¥É(¤³¤³¤Ç¤Ï¡¢ÍÕ -¤ª¤è¤ÓÀá¤ò¥Î¡¼¥É¤È¸Æ¤Ö¤³¤È¤Ë¤·¤Æ¤¤¤ë¡£¡Ö¥Î¡¼¥É¡×¤È¡ÖÀá¡×¤ÏËÜÍèƱ¤¸°ÕÌ£ -¤Ê¤Î¤À¤¬¤â¤¦½ñ¤¤¤Æ¤·¤Þ¤Ã¤¿¤Î¤Ç»ÅÊý¤Ê¤¤)¤Î¿ô¤¬ 2^(n+1) - 1 ¤Ç¤¢¤ë(³¬ÁØ -n ¤ÎÍդοô¤Ï 2^n ¤Ç¡¢Àá¤Î¿ô¤Ï 2^n-1)¤«¤é¡¢ +あと、この木に必要なサイズはいくつであるかという点が気になる。これは、 +階層 13 以上のノードの数となるが、階層 n の二分木のノード(ここでは、葉 +および節をノードと呼ぶことにしている。「ノード」と「節」は本来同じ意味 +なのだがもう書いてしまったので仕方ない)の数が 2^(n+1) - 1 である(階層 +n の葉の数は 2^n で、節の数は 2^n-1)から、 - ³¬ÁØ 12 ¤Î¥Î¡¼¥É¤Î¿ô¤Ï¡¢(2^(12+1)-1) = 8191 - ³¬ÁØ 16 ¤Î¥Î¡¼¥É¤Î¿ô¤Ï¡¢(2^(16+1)-1) = 131071 + 階層 12 のノードの数は、(2^(12+1)-1) = 8191 + 階層 16 のノードの数は、(2^(16+1)-1) = 131071 -¤È¤Ê¤ê¡¢¤½¤Îº¹¤Ï 131071 - 8191 = 122880 ¤È¤Ê¤ë¡£Ã±½ã¤Ë¹Í¤¨¤ë¤È¡¢ºÇ°­¤Î -¾ì¹ç¤òÁÛÄꤷ¤Æ left[], right[] ¹ç·×¤Ç 122880 ¸Ä¤ÎÎΰ褬ɬÍפǤ¢¤ë¤³¤È¤Ë -¤Ê¤ë¡£¤½¤¦¤¹¤ë¤ÈÎΰè¤òÀáÌ󤹤ë¤È¤¤¤¦ÌÜŪ¤«¤é¤Ï¸µ¤â»Ò¤â¤Ê¤¤¤³¤È¤È¤Ê¤ë¡£ +となり、その差は 131071 - 8191 = 122880 となる。単純に考えると、最悪の +場合を想定して left[], right[] 合計で 122880 個の領域が必要であることに +なる。そうすると領域を節約するという目的からは元も子もないこととなる。 -¤·¤«¤·¤è¤¯¤è¤¯¹Í¤¨¤ë¤È¤½¤Î¿´ÇۤϤʤ¤¤è¤¦¤À¡£¼ÂºÝ¤Î¤È¤³¤í¤Ï¥Ï¥Õ¥Þ¥óÌÚÁ´ -ÂΤÎÍդοô¤Ïʸ»ú¼ï¤Î¿ô NC ¤·¤«Â¸ºß¤·¤Ê¤¤¤¿¤á(¥Ï¥Õ¥Þ¥óÌÚ¹½ÃÛ¥¢¥ë¥´¥ê¥º¥à -µÚ¤Ó¥¨¥ó¥³¡¼¥É½èÍý¤ò³Îǧ) 2*NC-1¤¬¥Ï¥Õ¥Þ¥óÌÚÁ´ÂΤΥΡ¼¥É¿ô¤Ç¤¢¤ë¡£¤½¤· -¤Æ¡¢ +しかしよくよく考えるとその心配はないようだ。実際のところはハフマン木全 +体の葉の数は文字種の数 NC しか存在しないため(ハフマン木構築アルゴリズム +及びエンコード処理を確認) 2*NC-1がハフマン木全体のノード数である。そし +て、 - ³¬ÁØ 12 ¤ÎÍդοô¤ÎºÇ¾®ÃÍ 12 + 階層 12 の葉の数の最小値 12 -¤Ç¤¢¤ë¤³¤È¤«¤é¡¢³¬ÁØ13°Ê²¼¤Î¥Î¡¼¥É¿ô¤Ï +であることから、階層13以下のノード数は - ÌÚ¤Çɽ¸½¤¹¤ëºÇÂç¤ÎÍդοô NC-12 + 木で表現する最大の葉の数 NC-12 -¤È¤Ê¤êÌڤΥΡ¼¥É¿ô¤ÎºÇÂçÃÍ¤Ï 2*(NC-12)-1 ¤È¤Ê¤ë¡£¤µ¤é¤Ë¡¢¤³¤ÎÌÚ¤ÎÍÕ¤Ï -left right ¤Îź¤¨»ú¤Ë¤Ê¤é¤Ê¤¤¤Î¤Ç¤½¤Îʬ¤ÎÎΰè¤Ï¸·Ì©¤Ë¤ÏÉÔÍפǤ¢¤ë¡£ +となり木のノード数の最大値は 2*(NC-12)-1 となる。さらに、この木の葉は +left right の添え字にならないのでその分の領域は厳密には不要である。 -# ¸å¤Î²òÀϤǤ狼¤ë¤³¤È¤À¤¬¡¢left, right ¤Îź¤¨»ú¤Ï NC °Ê¾å¤Ç¤¢¤ë¤¿¤á -# 0...NC ¤ÎÈϰϤÎÎΰè¤Ï(c_len ¤Ë¤Ä¤¤¤Æ¤Ï)»È¤ï¤ì¤Ê¤¤¡£¤Þ¤¿¡¢ÊÑ¿ô left -# right ¤Ï¥¨¥ó¥³¡¼¥É½èÍý¤Ç¥Ï¥Õ¥Þ¥óÌÚ¤òºî¤ëºÝ¤Ë»ÈÍѤ·¤¿ÊÑ¿ô¤òήÍѤ·¤Æ¤¤ -# ¤ë¤¿¤áÎΰ褬­¤ê¤Ê¤¤¤È¤¤¤¦¤³¤È¤Ï¤Ê¤¤¡£ +# 後の解析でわかることだが、left, right の添え字は NC 以上であるため +# 0...NC の範囲の領域は(c_len については)使われない。また、変数 left +# right はエンコード処理でハフマン木を作る際に使用した変数を流用してい +# るため領域が足りないということはない。 # -# ɽ°ú¤­¤ò¹Ô¤ï¤º¤Ë Huffman ÌÚ¤ò´°Á´¤ËÆɤ߹þ¤à¤³¤È¤ò¹Í¤¨¤ë¤È¡¢left, -# right ¤Ï¡¢c_len ¤Ë¤Ä¤¤¤Æ¤Ï¡¢ +# 表引きを行わずに Huffman 木を完全に読み込むことを考えると、left, +# right は、c_len については、 # NC ... 2*NC-1 -# ¤¬»ÈÍѤµ¤ì¡¢°ÌÃÖ(p_len)¤ËÂФ¹¤ë Huffman Ìڤι½ÃÛ»þ¤Ë¤Ï +# が使用され、位置(p_len)に対する Huffman 木の構築時には # np ... 2*np-1 -# ¤ÎÈϰϤ¬»ÈÍѤµ¤ì¤ë¡£ +# の範囲が使用される。 # -# c_len, p_len ¤Î Huffman ÌÚ¤ÏƱ»þ¤Ë¸ºß¤¹¤ë¤Î¤Ç¡¢left, right ¤È¤â¤Ë -# ²¼¿Þ¤ÎÎΰ褬Ʊ»þ¤Ë»ÈÍѤµ¤ì¤ë¤³¤È¤Ë¤Ê¤ë¡£ -# (t_len ¤Ï c_len ¤ÎÆɤ߹þ¤ß¤¬´°Î»¤·¤¿»þÅÀ¤ÇÉÔÍפȤʤ뤿¤á¡¢¤³¤Î¿Þ¤Ë¤Ï -# ɽ¤·¤Æ¤¤¤Ê¤¤) +# c_len, p_len の Huffman 木は同時に存在するので、left, right ともに +# 下図の領域が同時に使用されることになる。 +# (t_len は c_len の読み込みが完了した時点で不要となるため、この図には +# 表していない) # # 0 np 2*np-1 NC 2*NC-1 # +---------+-------+----------+------------------------------+ -# |̤»ÈÍÑ°è |»ÈÍÑ°è | ̤»ÈÍÑ°è | »ÈÍÑ°è | +# |未使用域 |使用域 | 未使用域 | 使用域 | # +---------+-------+----------+------------------------------+ # -# np: 14¡Á17 +# np: 14〜17 # NC: 510 # -# ¥½¡¼¥¹¤ò¸«¤Æ¤ß¤Æ¤â¡¢¤³¤Î¤³¤È¤Ï¤Ê¤«¤Ê¤«µ¤¤¬¤Ä¤­¤Ë¤¯¤¤¡£¤ä¤Ï¤ê¡¢left, -# right ¤Ï³Æ Huffman Éä¹æÍѤËÎΰè¤ò³ä¤êÅö¤Æ¤¿Êý¤¬¤ï¤«¤ê¤ä¤¹¤¤¤À¤í¤¦¡£ +# ソースを見てみても、このことはなかなか気がつきにくい。やはり、left, +# right は各 Huffman 符号用に領域を割り当てた方がわかりやすいだろう。 -³¤­¤ò¸«¤Æ¤¤¤³¤¦¡¢(E) ¤Î½èÍý +続きを見ていこう、(E) の処理 /* (E) */ /* initialize */ @@ -5270,31 +5270,31 @@ left right for (i = j; i < k; i++) table[i] = 0; -table[] ¤Î½é´üÃͤȤ·¤Æ 0 ¤òÀßÄꤷ¤Æ¤¤¤ë¡£ +table[] の初期値として 0 を設定している。 -Huffman Éä¹æ¤Ç¤¢¤ë j ¤Ë¤Ï¡¢start[tablebits + 1] ¤Ä¤Þ¤ê¡¢¥Ó¥Ã¥ÈĹ -tablebits ¤ÎºÇÂç¤Î Huffman Éä¹æ+1¤¬ÀßÄꤵ¤ì¤ë¡£(·«¤êÊÖ¤·¤Ë¤Ê¤ë¤¬¡¢¥Ó¥Ã -¥ÈĹ i ¤Î Huffman Éä¹æ¤Ï¡¢start[i] ¤«¤é start[i+1]-1¤ÎÈϰϤÎÉä¹æ¤Ç¤¢¤ë) -m ¤Ç±¦¥·¥Õ¥È¤·¤Æ¤¤¤ë¤Î¤Ï¡¢(D) ¤Ç¤Ï¡¢tablebits ¤Þ¤Ç¤Î start[i] ¤·¤« ±¦ -¥·¥Õ¥È¤·¤Æ¤¤¤Ê¤¤¤¿¤á¤Ç¤¢¤ë¡£ +Huffman 符号である j には、start[tablebits + 1] つまり、ビット長 +tablebits の最大の Huffman 符号+1が設定される。(繰り返しになるが、ビッ +ト長 i の Huffman 符号は、start[i] から start[i+1]-1の範囲の符号である) +m で右シフトしているのは、(D) では、tablebits までの start[i] しか 右 +シフトしていないためである。 -k ¤Ï¡¢1 << tablebits ¤«¤é tablebits + 1 ÈÖÌܤΥӥåȤ¬Î©¤Ã¤¿¿ôÃͤȤʤ롣 -¤³¤ÎÃͤϤĤޤê¤Ï tablebits ¥Ó¥Ã¥È¤Î Huffman Éä¹æ¤ÎºÇÂçÃÍ + 1 ¤Ç¤¢¤ë¡£ +k は、1 << tablebits から tablebits + 1 番目のビットが立った数値となる。 +この値はつまりは tablebits ビットの Huffman 符号の最大値 + 1 である。 -¤½¤·¤Æ¡¢start[j...k] ¤ÎÈϰϤˤĤ¤¤Æ 0 ¤òÀßÄꤷ¤Æ¤¤¤ë¡£·ë¶É tablebits ¤Î -¥Ó¥Ã¥ÈŤÎÈϰϤdzä¤êÅö¤Æ¤é¤ì¤ë¤³¤È¤¬¤Ê¤¤ Huffman Éä¹æ¤ËÂФ·¤Æ 0 ¤Ç½é´ü -²½¤·¤Æ¤¤¤ë¤È¤¤¤¦¤³¤È¤Î¤è¤¦¤À¡£ -³ä¤êÅö¤Æ¤é¤ì¤ë¤³¤È¤¬¤Ê¤¤ Huffman Éä¹æ¤È¤¤¤¦¤³¤È¤Ï 0 ¤Ç½é´ü²½¤·¤Æ¤¤¤ë¤Î -¤Ï¤³¤Î¸å¤Î½èÍý¤ÇÌڤΥΡ¼¥É¤Ë¤Ê¤ëͽÄê¤ÎÎΰè¤À¤È»×¤ï¤ì¤ë¡£ +そして、start[j...k] の範囲について 0 を設定している。結局 tablebits の +ビット長の範囲で割り当てられることがない Huffman 符号に対して 0 で初期 +化しているということのようだ。 +割り当てられることがない Huffman 符号ということは 0 で初期化しているの +はこの後の処理で木のノードになる予定の領域だと思われる。 -¤Á¤ç¤Ã¤Èµ¿Ìä¤Ê¤Î¤À¤¬¡¢if (j != 0) ¤ÏɬÍפʤΤÀ¤í¤¦¤«¡©¤Ä¤Þ¤ê¡¢j = 0 ¤È -¤·¤Æ¡¢table[] ¤ÎÁ´Í×ÁǤˤĤ¤¤Æ 0 ¤Ç½é´ü²½¤·¤Æ¤â²¿¤âÌäÂê¤Ï¤Ê¤¤¤Î¤Ç¤Ï¤Ê¤¤ -¤«¡©¤½¤·¤Æ¤½¤ì¤Ê¤é make_table() ¤Ë table[] ¤Î¥µ¥¤¥º(sizeof)¤òÅϤ· -memset() ¤Ë¤Æ 0 ¤Ç½é´ü²½¤·¤¿Êý¤¬(µ¡³£¸ì¤ÎÌ¿Îá¿ô¤¬¾¯¤Ê¤¯¤Ê¤ë¤Î¤Ç)®¤¤¤Î -¤Ç¤Ï¤Ê¤¤¤À¤í¤¦¤«¡©¤Þ¤¢¡¢Â®¤µ¤ÏÎɤ¤¤È¤·¤Æ¤â memset() ¤ÎÊý¤¬¤è¤ê¤ï¤«¤ê¤ä -¤¹¤¤¤È»×¤¦¡£ +ちょっと疑問なのだが、if (j != 0) は必要なのだろうか?つまり、j = 0 と +して、table[] の全要素について 0 で初期化しても何も問題はないのではない +か?そしてそれなら make_table() に table[] のサイズ(sizeof)を渡し +memset() にて 0 で初期化した方が(機械語の命令数が少なくなるので)速いの +ではないだろうか?まあ、速さは良いとしても memset() の方がよりわかりや +すいと思う。 -¼¡¤Ë (F) ¤Î½èÍý¡£¾¯¤·Â礭¤¤¤Î¤Ç²¼µ­¤ÎÄ̤ê (F.1), (F.2) ¤ÈºÙʬ²½¤·¤Æ¤ß¤¿¡£ +次に (F) の処理。少し大きいので下記の通り (F.1), (F.2) と細分化してみた。 /* (F) */ /* create table and tree */ @@ -5333,22 +5333,22 @@ memset() start[k] = l; } -¤³¤ÎÉôʬ¤Ï°ì»þÊÑ¿ô¤ÎÎ̤¬Â¿¤¤¡¢i,j,k,l,m,n,p ¤Þ¤Ç»È¤ï¤ì¤Æ¤¤¤ë¡£ -(¤·¤«¤â¡¢Á°¤Î½èÍý¤Þ¤Ç¤ÈÊÑ¿ô¤ÎÍÑÅÓ¤¬°ã¤Ã¤Æ¤¿¤ê¤¹¤ë¤Î¤Ç¡¢Ê£»¨¤Ë¤Ê¤Ã¤Æ¤¤¤ë) +この部分は一時変数の量が多い、i,j,k,l,m,n,p まで使われている。 +(しかも、前の処理までと変数の用途が違ってたりするので、複雑になっている) -°ìö¡¢°Ê²¼¤ËÀ°Íý¤·¤Æ¤ß¤¿¡£(¤É¤ÎÊÑ¿ô¤Îź¤¨»ú¤Ë»È¤ï¤ì¤Æ¤¤¤ë¤«¤Ê¤É¤«¤é¤Ò¤È -¤á¤ÇȽÃǤ·¤Æ¤ß¤¿·ë²Ì¤Ç¤¢¤ë¡£¤½¤·¤Æ¡¢n, p ¤Ë¤Ä¤¤¤Æ¤Ï¤Ò¤È¤á¤Ç¤Ï¤ï¤«¤é¤Ê¤«¤Ã -¤¿)¡£ +一旦、以下に整理してみた。(どの変数の添え字に使われているかなどからひと +めで判断してみた結果である。そして、n, p についてはひとめではわからなかっ +た)。 - i: Huffman Éä¹æ - j: Éü¹æʸ»ú - k: j ¤Î Huffman Éä¹æ¤Î¥Ó¥Ã¥ÈĹ(i ¤Î¥Ó¥Ã¥ÈĹ) - l: ¥Ó¥Ã¥ÈĹ k ¤ËÂФ¹¤ëHuffman Éä¹æ¤Î½ªÃÍ (start[k] <= Huffman(k) < l) - m: Huffman Éä¹æɽ¤òshift¤¹¤ë¥Ó¥Ã¥È¿ô + i: Huffman 符号 + j: 復号文字 + k: j の Huffman 符号のビット長(i のビット長) + l: ビット長 k に対するHuffman 符号の終値 (start[k] <= Huffman(k) < l) + m: Huffman 符号表をshiftするビット数 n: ?? p: ?? -¤³¤ì¤òƧ¤Þ¤¨¤Æ½èÍýÆâÍƤò¸«¤Æ¤ß¤è¤¦¡£¤Þ¤º¡¢(F)Á´ÂΤˤĤ¤¤Æ +これを踏まえて処理内容を見てみよう。まず、(F)全体について /* (F) */ /* create table and tree */ @@ -5362,24 +5362,24 @@ memset() start[k] = l; } -Éü¹æʸ»ú j = 0 ... nchar ¤ò¥ë¡¼¥×¤·¤Æ¤¤¤ë(j ¤¬Éü¹æʸ»ú¤Ç¤¢¤ë¤³¤È¤Ï¡¢ -bitlen[] ¤Îź¤¨»ú¤Ç¤¢¤ë¤³¤È¤«¤é¤ï¤«¤ë)¡£ +復号文字 j = 0 ... nchar をループしている(j が復号文字であることは、 +bitlen[] の添え字であることからわかる)。 -¤½¤·¤Æ¡¢k = bitlen[j] ¤¬ 0 ¤Ç¤¢¤ì¤Ð¡¢(Huffman Éä¹æ²½¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð)¤½ -¤Îʸ»ú¤Ï¥¹¥­¥Ã¥×¤·¤Æ¤¤¤ë¡£¤³¤³¤Ï¡¢Îɤ¤¤À¤í¤¦¡£ +そして、k = bitlen[j] が 0 であれば、(Huffman 符号化されていなければ)そ +の文字はスキップしている。ここは、良いだろう。 -(F.1), (F.2) ¤Ï¡¢HuffmanÉä¹æ²½¤µ¤ì¤Æ¤¤¤ëʸ»ú j ¤Ë¤Ä¤¤¤Æ -¤Î½èÍý¤È¤Ê¤ë¡£(ºÇ¸å¤Î start[k] = l ¤Ï¸å¤Ç¹Í¤¨¤è¤¦) +(F.1), (F.2) は、Huffman符号化されている文字 j について +の処理となる。(最後の start[k] = l は後で考えよう) -½èÍý¤ÎÌÜŪ¤«¤éÂ绨ÇÄ¤Ë +処理の目的から大雑把に - (F.1) k <= tablebits ¤Î¾ì¹ç¡¢É½°ú¤­²Äǽ¤ÊÈϰϤʤΤǡ¢ - table[i] ¤ËÉü¹æʸ»ú¤ò¥»¥Ã¥È¡£ + (F.1) k <= tablebits の場合、表引き可能な範囲なので、 + table[i] に復号文字をセット。 - (F.2) k > tablebits ¤Î¾ì¹ç¡¢É½°ú¤­ÉÔ²Äǽ¤ÊÈϰϤʤΤǡ¢ - left[],right[]¤ËÉü¹æʸ»ú¤ò¥»¥Ã¥È¡£ + (F.2) k > tablebits の場合、表引き不可能な範囲なので、 + left[],right[]に復号文字をセット。 -¤È¤¤¤Ã¤¿½èÍý¤ò¤·¤Æ¤¤¤ë¤³¤È¤¬Í½ÁۤǤ­¤ë¡£¤Ç¤Ï¡¢(F.1) ¤ò¸«¤Æ¤ß¤ë¡£ +といった処理をしていることが予想できる。では、(F.1) を見てみる。 /* (F.1) */ l = start[k] + weight[k]; @@ -5389,30 +5389,30 @@ bitlen[] table[i] = j; } -¥Ó¥Ã¥ÈĹ k <= tablebits ¤Î¾ì¹ç¡¢¥Ó¥Ã¥ÈĹ k ¤¬¼è¤ê¤¦¤ëÈϰϤΠHuffman Éä¹æ -¤Ë¤Ä¤¤¤Æ¡¢Ê¸»ú j ¤ò³ä¤êÅö¤Æ¤Æ¤¤¤ë¡£ -¥Ó¥Ã¥ÈĹ k ¤¬¼è¤ê¤¦¤ëÈϰϤΠHuffman Éä¹æ¤È¤Ï +ビット長 k <= tablebits の場合、ビット長 k が取りうる範囲の Huffman 符号 +について、文字 j を割り当てている。 +ビット長 k が取りうる範囲の Huffman 符号とは - start[k] <= HuffmanÉä¹æ i < l + start[k] <= Huffman符号 i < l -¤Ç¤¢¤ë¡£Îã¤Ç¼¨¤¹¤È(´ÊÊز½¤Î¤¿¤á¤Ë¡¢ºÇÂç¤Î Huffman Éä¹æŤò 2 ¤È¤¹¤ë) +である。例で示すと(簡便化のために、最大の Huffman 符号長を 2 とする) /\ a: 0 a /\ b: 10 b c c: 11 -ʸ»ú a ¤Ï¡¢1 ¥Ó¥Ã¥È¤Ç¡¢00...10 ¤ÎÈÏ°Ï(¤Ä¤Þ¤ê¡¢00 ¤È 01) -ʸ»ú b ¤Ï¡¢2 ¥Ó¥Ã¥È¤Ç¡¢10...11 ¤ÎÈÏ°Ï(¤Ä¤Þ¤ê¡¢10) +文字 a は、1 ビットで、00...10 の範囲(つまり、00 と 01) +文字 b は、2 ビットで、10...11 の範囲(つまり、10) -¤È¤Ê¤ë¡£¤³¤³¤Ç¡¢Ê¸»ú c ¤Ë¤Ä¤¤¤Æ¤Ïʸ»ú b ¤ÈƱ¤¸Éä¹æ¤¬³ä¤êÅö¤Æ¤é¤ì¤ë¤«¤Î¤è¤¦¤Ë -¸«¤¨¤ë¤¬¡¢¼ÂºÝ¤Ï(F) ¤Î¥ë¡¼¥×¤ÎËöÈø¤Ë½Ð¤Æ¤­¤¿ +となる。ここで、文字 c については文字 b と同じ符号が割り当てられるかのように +見えるが、実際は(F) のループの末尾に出てきた start[k] = l; -¤Ë¤è¤Ã¤Æ¡¢¥Ó¥Ã¥ÈĹ k ¤ËÂФ¹¤ë½é´üÃÍ start[k] ¤¬Êѹ¹¤µ¤ì¤ë¤¿¤á¡¢¤½¤Î¿´ÇÛ -¤Ï¤Ê¤¤¤è¤¦¤À¡£ +によって、ビット長 k に対する初期値 start[k] が変更されるため、その心配 +はないようだ。 -¼¡¤Ë¡¢(F.2) +次に、(F.2) /* (F.2) */ else { @@ -5435,62 +5435,62 @@ bitlen[] *p = j; } -¤Á¤ç¤Ã¤ÈÊ£»¨¤À¤¬Æñ¤·¤¤¤³¤È¤Ï¤Ê¤¤¡£¤Þ¤º¡¢ +ちょっと複雑だが難しいことはない。まず、 p = &table[(i = start[k]) >> m]; -¤ÎÉôʬ¤Ï¡¢ +の部分は、 i = start[k]; p = &table[i >> m] -¤Ç¤¢¤ê¡¢i ¤Ï Huffman Éä¹æ¤Î½é´üÃͤǤ¢¤ë¡£i ¤¬ m ¤Ç±¦¥·¥Õ¥È¤µ¤ì¤Æ¤¤¤ë¤¬¡¢ -½é´ü²½Éôʬ¤Î (D) ¤Ç¤Ï¡¢tablebits ¤Þ¤Ç¤Î¥¤¥ó¥Ç¥Ã¥¯¥¹¤ËÂФ¹¤ëÃÍ -(start[1..tablebits])¤·¤«¥·¥Õ¥È¤·¤Æ¤ª¤é¤º¡¢¤³¤Î (F.2) ¤ÎÉôʬ¤Ï k > -tablebits ¤Ç¤¢¤ë¤«¤é start[k] ¤Ï m ¤Ç¥·¥Õ¥È¤µ¤ì¤Æ¤¤¤Ê¤¤ Huffman Éä¹æ¤È -¤Ê¤Ã¤Æ¤¤¤ë¡£ +であり、i は Huffman 符号の初期値である。i が m で右シフトされているが、 +初期化部分の (D) では、tablebits までのインデックスに対する値 +(start[1..tablebits])しかシフトしておらず、この (F.2) の部分は k > +tablebits であるから start[k] は m でシフトされていない Huffman 符号と +なっている。 -p ¤Ï¡¢HuffmanÉä¹æ i ¤Î table ¾å¤Î°ÌÃÖ¤ò»Ø¤¹¡£¸å¤Ç¡¢*p ¤Ë¤ÏÌڤΥ롼¥È°Ì -ÃÖ¤òµ­Ï¿¤¹¤ë¤³¤È¤¬Í½ÁÛ¤µ¤ì¤ë¡£ +p は、Huffman符号 i の table 上の位置を指す。後で、*p には木のルート位 +置を記録することが予想される。 -¼¡¤Ë¡¢ +次に、 i <<= tablebits; -i ¤ò tablebits ¤Ç¥·¥Õ¥È¤¹¤ë¤³¤È¤Ç¡¢Huffman Éä¹æ i ¤ÏºÇ¾å°Ì¤Î tablebits -¤ò½ü¤¤¤¿»Ä¤ê¤Î¥Ó¥Ã¥ÈÉôʬ¤Ë¤Ê¤ë¡£¤³¤Î»Ä¤ê¤Î¥Ó¥Ã¥ÈÉôʬ¤òÌÚ¤Çɽ¤¹¡£ +i を tablebits でシフトすることで、Huffman 符号 i は最上位の tablebits +を除いた残りのビット部分になる。この残りのビット部分を木で表す。 -¼¡¤Ë +次に n = k - tablebits; -n ¤Ï¡¢½ñ¤­´¹¤¨¤¿ i ¤Î¥Ó¥Ã¥ÈŤò¼¨¤¹¡£ÌڤˤϿ¼¤µ n ¤Î³¬Áؤ¬ºîÀ®¤µ¤ì¤ë¤Î -¤À¤í¤¦¡£ +n は、書き換えた i のビット長を示す。木には深さ n の階層が作成されるの +だろう。 ---------------------------------------------------------------------------- - k: Huffman Éä¹æ i ¤Î¥Ó¥Ã¥ÈĹ + k: Huffman 符号 i のビット長 |----------------| - tablebits (ɽ°ú¤­¤ÇÉü¹æ¤¹¤ëÉôʬ) + tablebits (表引きで復号する部分) |-----------| +-----------+----+ - Huffman Éä¹æ i| |xxxx| + Huffman 符号 i| |xxxx| +-----------+----+ |----| - n: ÌÚ¤òé¤ë¤³¤È¤ÇÉü¹æ¤¹¤ëÉôʬ + n: 木を辿ることで復号する部分 -tablebits ¥Ó¥Ã¥È¥·¥Õ¥È¤Ë¤è¤ê¡¢½ñ¤­´¹¤¨¤¿ i ¤Ï¡¢xxxx ¤ÎÉôʬ¤¬ºÇ¾å°Ì¥Ó¥Ã -¥È¤È¤Ê¤ë¡£ +tablebits ビットシフトにより、書き換えた i は、xxxx の部分が最上位ビッ +トとなる。 +----+-----------+ - Huffman Éä¹æ i|xxxx| | + Huffman 符号 i|xxxx| | +----+-----------+ |----| - n: ÌÚ¤òé¤ë¤³¤È¤ÇÉü¹æ¤¹¤ëÉôʬ + n: 木を辿ることで復号する部分 ---------------------------------------------------------------------------- -¤½¤¦¤·¤Æ¡¢½ñ¤­´¹¤¨¤¿ i ¤Ë¤Ä¤¤¤Æ¡¢ÌÚ¤ò¹½ÃÛ¤¹¤ë¡£ +そうして、書き換えた i について、木を構築する。 /* make tree (n length) */ while (--n >= 0) { @@ -5509,91 +5509,91 @@ tablebits /* (F.2.3) */ *p = j; -(F.2.1) *p ¤¬ 0 ¤Ç¤¢¤ì¤Ð¡¢*p ¤Ë avail ¤È¤·¤Æ¡¢¿·¤¿¤Êº¬¤ò³ä¤êÅö¤Æ¤½¤Î±¦ -¤Èº¸¤Î»Ò¤Ï 0 ¤Ç½é´ü²½¤·¤Æ¤ª¤¯¡£ +(F.2.1) *p が 0 であれば、*p に avail として、新たな根を割り当てその右 +と左の子は 0 で初期化しておく。 -(F.2.2) Huffman Éä¹æ(¤Î tablebits °Ê¹ß¤Î¥Ó¥Ã¥È) i ¤Ë¤Ä¤¤¤ÆºÇ¾å°Ì¥Ó¥Ã¥È -¤¬Î©¤Ã¤Æ¤¤¤ì¤Ð±¦¤Î»Ò right¡¢¥Ó¥Ã¥È¤¬Î©¤Ã¤Æ¤¤¤Ê¤±¤ì¤Ðº¸¤Î»Ò left ¤òºîÀ® -(Îΰè¤ò p ¤ÇͽÌó)¤¹¤ë¡£ +(F.2.2) Huffman 符号(の tablebits 以降のビット) i について最上位ビット +が立っていれば右の子 right、ビットが立っていなければ左の子 left を作成 +(領域を p で予約)する。 -¥ë¡¼¥×¤Ë¤è¤Ã¤Æ¥Ó¥Ã¥È¥Ñ¥¿¡¼¥ó¤Ë±è¤Ã¤Æ *p ¤Ë¤Ï¡¢avail++ ¤¬½ç¤Ë³ä¤êÅö¤Æ¤é¤ì¤ë¡£ +ループによってビットパターンに沿って *p には、avail++ が順に割り当てられる。 -avail ¤Î½é´üÃÍ¤Ï nchar ¤Ê¤Î¤Ç¡¢*p >= nchar (c_table[] ¤Ê¤é NC)¤Ç¤¢¤ë¡£ +avail の初期値は nchar なので、*p >= nchar (c_table[] なら NC)である。 -¤³¤Î while ¥ë¡¼¥×¤òÈ´¤±¤¿¸å¤Ë(F.2.3)¤Ë¤Æ +この while ループを抜けた後に(F.2.3)にて *p = j; -¤¬ÀßÄꤵ¤ì¡¢ÌÚ¤ÎÍդˤϡ¢*p < nchar ¤Ç¤¢¤ëÃͤ¬ÀßÄꤵ¤ì¤Æ¤¤¤ë¡£(¤½¤·¤Æ¤³ -¤ì¤¬µá¤á¤ëÉü¹æʸ»ú¤Ç¤¢¤ë¡£) +が設定され、木の葉には、*p < nchar である値が設定されている。(そしてこ +れが求める復号文字である。) -(F.2.1) ¤Ç¡¢if (*p == 0) ¤È¤·¤Æ¤¤¤ëÍýͳ¤Ï¡¢ +(F.2.1) で、if (*p == 0) としている理由は、 . \ . <- p / -¤È p ¤¬´û¤Ë³ä¤êÅö¤Æ¤é¤ì¤Æ¤ª¤ê¡¢¤½¤ÎÀá¤Ë +と p が既に割り当てられており、その節に . \ . <- p / \ -¤È±¦¤Î»Ò¤¬Äɲ䵤ì¤ë¾ì¹ç¤òÁÛÄꤷ¤Æ¤¤¤ë¡£¤³¤Î»þÅÀ¤Ç (E) ¤Ë¤Æ table ¤ò 0 -¤Ç½é´ü²½¤·¤Æ¤¤¤ëÍýͳ¤¬¤ï¤«¤Ã¤¿¡£Ìڤꬤ¬³ä¤êÅö¤Æ¤é¤ì¤Æ¤¤¤ë¤«¤É¤¦¤«¤ÎȽ -Ä꤬ɬÍפÀ¤«¤é½é´ü²½¤Ç̤³ä¤êÅö¤Æ¤Ë¤·¤Æ¤ª¤¯É¬Íפ¬¤¢¤ë¤È¤¤¤¦¤³¤È¤À¡£¤â¤Á -¤í¤ó (E) ¤Ç½ñ¤¤¤¿Ä̤ê table Á´ÂΤò 0 ¤Ç½é´ü²½¤·¤Æ¤âÌäÂê¤Ï¤Ê¤¤¤·¤½¤ÎÊý¤¬ -¤ï¤«¤ê¤ä¤¹¤¤¤È»×¤Ã¤¿¡£ +と右の子が追加される場合を想定している。この時点で (E) にて table を 0 +で初期化している理由がわかった。木の根が割り当てられているかどうかの判 +定が必要だから初期化で未割り当てにしておく必要があるということだ。もち +ろん (E) で書いた通り table 全体を 0 で初期化しても問題はないしその方が +わかりやすいと思った。 -¤³¤³¤Þ¤Ç¡¢c_table[] ¤òºîÀ®¤¹¤ë¾ì¹ç¤À¤±¤ò¸«¤Æ¤­¤¿¤¬¡¢pt_table ¤Î¾ì¹ç¤â¹Í -¤¨Êý¤ÏƱ¤¸¤Ê¤Î¤Ç²òÀϤÎɬÍפϤʤ¤¤À¤í¤¦¡£ +ここまで、c_table[] を作成する場合だけを見てきたが、pt_table の場合も考 +え方は同じなので解析の必要はないだろう。 -¤¿¤À¡¢¤³¤ÎÃʳ¬¤Çɽ°ú¤­¤Î¥Ó¥Ã¥È¿ô¤¬ c_table ¤Ë¤Ä¤¤¤Æ¤Ï 12 ¤¬ p_table ¤Ë¤Ä -¤¤¤Æ¤Ï 8 ¤¬Áª¤Ð¤ì¤Æ¤¤¤ëÍýͳ¤¬ÉÔÌÀ¤Ç¤¢¤ë¡£¤³¤ì¤Ï¡¢ÁÛÁü¤À¤¬ pt_table ¤¬Éä¹æ²½¤¹¤ë -ʸ»ú¼ï¤Ï¾¯¤Ê¤¤¤Î¤Ç pt_table ¤Ë¤Ä¤¤¤Æ¤Ï 8 bit ¤Î¥Æ¡¼¥Ö¥ë¤ò»È¤Ã¤Æ¤âɽ°ú¤­¤Î³ÎΨ¤¬ -¹â¤¤¤Î¤Ç¤Ï¤Ê¤¤¤À¤í¤¦¤«¡£¤Ä¤Þ¤ê¡¢Îΰè(pt_table ¤Î¥µ¥¤¥º)¤ÎÀáÌó¤òÍ¥À褷¤Æ¤¤¤ë¤Î¤Ç -¤Ï¤Ê¤¤¤«¤È»×¤¦¡£ +ただ、この段階で表引きのビット数が c_table については 12 が p_table につ +いては 8 が選ばれている理由が不明である。これは、想像だが pt_table が符号化する +文字種は少ないので pt_table については 8 bit のテーブルを使っても表引きの確率が +高いのではないだろうか。つまり、領域(pt_table のサイズ)の節約を優先しているので +はないかと思う。 -LHA ¥Õ¥¡¥¤¥ë¤Î¹½Â¤(¤Þ¤È¤á) +LHA ファイルの構造(まとめ) -------------------------- -°Ê¾å¤Ç LHa for UNIX ¤Ë¤Ä¤¤¤Æ¤Î°ìÄ̤ê¤Î½èÍý¤ò¸«¤¿¤³¤È¤Ë¤Ê¤ë¡£¤³¤³¤«¤é¤Ï -¼ÂÁõ¤Ë¤Ä¤¤¤Æ¤Ï¶ËÎÏ¿¨¤ì¤º¤Ë LHA ¥Õ¥¡¥¤¥ë¤Î¹½Â¤(°µ½Ì·Á¼°)¤Ë¤Ä¤¤¤Æ¤Þ¤È¤á¤Æ -¤ß¤è¤¦¡£¤Þ¤¿¡¢¤³¤³¤Þ¤ÇºÙÉô¤Ë¤Ä¤¤¤Æ¤Ê¤¾¤Î¤Þ¤Þ¤È¤·¤Æ¤¤¤¿Éôʬ¤¬¤¢¤ë¤Î¤Ç¤³ -¤ì¤é¤âºÆ¸¡Æ¤¤·¤ÆÌÀ¤é¤«¤Ë¤·¤è¤¦¡£ +以上で LHa for UNIX についての一通りの処理を見たことになる。ここからは +実装については極力触れずに LHA ファイルの構造(圧縮形式)についてまとめて +みよう。また、ここまで細部についてなぞのままとしていた部分があるのでこ +れらも再検討して明らかにしよう。 ---------------------------------------------------------------------------- -< LHA ¥Õ¥¡¥¤¥ë¤Î¹½Â¤(1 ¥Ö¥í¥Ã¥¯Ê¬) > +< LHA ファイルの構造(1 ブロック分) > +-----------+ | blocksize | +-----------+ 16bit - t_len: c_len ¤Î¥Ï¥Õ¥Þ¥óÌÚ | + t_len: c_len のハフマン木 | +-----+--------------------+ | +-----+-----+ | len | t_len | | | 0 |root | +-----+--------------------+ | +-----+-----+ TBIT ?? bit | TBIT TBIT | - c_len: ʸ»ú¤ÈŤµ¤Î¥Ï¥Õ¥Þ¥óÌÚ | + c_len: 文字と長さのハフマン木 | +-------+------------------+ | +-------+-------+ | len | c_len | | | 0 | root | +-------+------------------+ | +-------+-------+ CBIT ?? bit | CBIT CBIT | - p_len: °ÌÃÖ¤Î(¥Ó¥Ã¥ÈŤÎ)¥Ï¥Õ¥Þ¥óÌÚ | + p_len: 位置の(ビット長の)ハフマン木 | +-----+--------------------+ | +-----+-----+ | len | p_len | | | 0 |root | +-----+--------------------+ | +-----+-----+ pbit ?? bit | - °µ½Ìʸ(ʸ»ú¤ÈŤµ¡¢°ÌÃ֤ΥӥåÈĹ¤Î¥Ï¥Õ¥Þ¥óÉä¹æ¤È°ÌÃÖ¤ÎÃÍ) + 圧縮文(文字と長さ、位置のビット長のハフマン符号と位置の値) +---------------------+ - | °µ½Ìʸ | + | 圧縮文 | +---------------------+ @@ -5613,111 +5613,111 @@ LHA TBIT 4 (NT <= 2^4) ---------------------------------------------------------------------------- -# ¥½¡¼¥¹¾å pt_len ¤È½ñ¤¤¤Æ¤¤¤¿Éôʬ¤Ï¡¢°Ê¹ß¤ÎÀâÌÀ¤Î¤¿¤á¤Ë p_len, t_len -# ¤È̾Á°¤òÊѹ¹¤·¤Æ¤¤¤ë¡£ÊÑ¿ô pt_len ¤Ïñ¤ËÎΰè¤ÎÀáÌó¤Î¤¿¤á¤Ë»È¤¤²ó¤µ¤ì -# ¤Æ¤¤¤¿¤À¤±¤Ç¤¢¤ê¤¤¤ï¤Ð¼ÂÁõ¤ÎÅÔ¹ç¤Ç¤¢¤ë¤«¤é¤À¡£ +# ソース上 pt_len と書いていた部分は、以降の説明のために p_len, t_len +# と名前を変更している。変数 pt_len は単に領域の節約のために使い回され +# ていただけでありいわば実装の都合であるからだ。 -¾åµ­¤Ï¡¢LHA ¥Õ¥¡¥¤¥ë¤Î¹½Â¤(¥Ø¥Ã¥À¤ò½ü¤¯)¤òɽ¤·¤¿¤â¤Î¤Ç¤¢¤ë¡£LHA ¥Õ¥¡¥¤ -¥ë¤Ï¥Ø¥Ã¥À¤È¾åµ­¥Õ¥¡¥¤¥ë¹½Â¤¤Î½¸¤Þ¤ê¤Ç 1 ¥Õ¥¡¥¤¥ë¤Î°µ½Ì¥Õ¥¡¥¤¥ë¤È¤Ê¤ê¡¢ -¤½¤ì¤¬Ê£¿ô½¸¤Þ¤Ã¤Æ¥¢¡¼¥«¥¤¥Ö¤ò¹½À®¤¹¤ë¡£¤Þ¤¿¡¢Ìó«»ö¤È¤·¤Æ¥¢¡¼¥«¥¤¥Ö¤Î -ºÇ¸å¤Ï 1 ¥Ð¥¤¥È¤Î 0 ¤¬Éղ䵤ì¤ë¤³¤È¤È¤Ê¤Ã¤Æ¤¤¤ë¡£ +上記は、LHA ファイルの構造(ヘッダを除く)を表したものである。LHA ファイ +ルはヘッダと上記ファイル構造の集まりで 1 ファイルの圧縮ファイルとなり、 +それが複数集まってアーカイブを構成する。また、約束事としてアーカイブの +最後は 1 バイトの 0 が付加されることとなっている。 -°µ½Ì·Á¼°¤Ï method ¤Ë¤è¤Ã¤Æ¥Ñ¥é¥á¡¼¥¿¤¬ÊѲ½¤¹¤ë¡£¤³¤³¤Ç¤Ï¡¢method -lh4,5,6,7 ¤Î·Á¼°¤·¤«¿¨¤ì¤Ê¤¤¤Î¤Ç method ¤Î°ã¤¤¤Ï slide ¼­½ñ¤Î¼­½ñ¥µ¥¤¥º -¤Î°ã¤¤¤·¤«¤Ê¤¤¡£¤¿¤À¤·¡¢¼­½ñ¤Î¥µ¥¤¥º¤Î°ã¤¤¤ËϢư¤·¤Æ°µ½Ì·Á¼°¤Ë±Æ¶Á¤òÍ¿ -¤¨¤ëÊÑ¿ô¤¬¤¢¤ë¤Î¤Ç¤³¤ì¤â¾åµ­¤Ë¤Þ¤È¤á¤Æ¤¤¤ë¡£(¾®Ê¸»ú¤Ï¤½¤ÎÊÑ¿ô¡¢Âçʸ»ú¤Ï -Äê¿ô¤ò°ÕÌ£¤¹¤ë) +圧縮形式は method によってパラメータが変化する。ここでは、method +lh4,5,6,7 の形式しか触れないので method の違いは slide 辞書の辞書サイズ +の違いしかない。ただし、辞書のサイズの違いに連動して圧縮形式に影響を与 +える変数があるのでこれも上記にまとめている。(小文字はその変数、大文字は +定数を意味する) -¿ÞÃæ t_len, c_len, p_len ¤Ï¡¢Huffman ÌڤξðÊó¤ò³ÊǼ¤·¤¿Îΰè¤òɽ¤·¡¢¡Ö°µ -½Ìʸ¡×¤¬Ê¿Ê¸¤ò°µ½Ì¤·¤¿ËÜÂΤȤʤ롣 +図中 t_len, c_len, p_len は、Huffman 木の情報を格納した領域を表し、「圧 +縮文」が平文を圧縮した本体となる。 -¤Þ¤¿¡¢3 ¤Ä¤Î¥Ï¥Õ¥Þ¥óÌڤνÐÎÏ·Á¼°¤Ë¤Ä¤¤¤Æ¥Ï¥Õ¥Þ¥óÌÚ¤¬¹½ÃۤǤ­¤Ê¤¤¾ì¹ç¤Î -·Á¼°¤ò±¦Â¦¤ËÊ»µ­¤·¤¿¡£¤³¤ì¤Ï¡¢Éü¹æ¸ì¤¬ 1 ¼ïÎष¤«¤Ê¤¤¾ì¹ç¤ò¼¨¤·¤Æ¤ª¤ê -root ¤¬¤½¤ÎÉü¹æ¸ì¤È¤Ê¤ë¡£Î㤨¤Ð¡¢c_len ¤¬¤³¤Î·Á¼°¤Ç½ñ¤«¤ì¤Æ¤¤¤¿¾ì¹ç¤Ï°µ -½Ìʸ¤ò¸«¤º¤Ë blocksize ʬ root ¤ò½ÐÎϤ¹¤ì¤ÐÉü¹æ¤È¤Ê¤ë¡£¤Ê¤ª¡¢c_len ¤¬¤³ -¤Î·Á¼°¤Î¾ì¹ç c_len ¤Î¥Ï¥Õ¥Þ¥óÌÚ¤ò¼¨¤¹ t_len ¤â±¦Â¦¤Î·Á¼°¤Ë¤Ê¤ë¤¬¡¢¤³¤Î -¤È¤­¤Î t_len ¤Î root ¤ÎÃÍ¤Ï 0 ¤Ë¤¹¤ë»ö¤Ë¤Ê¤Ã¤Æ¤¤¤ë¤è¤¦¤À(¤¿¤À¡¢Éü¹æ½èÍý -¤Ï¤³¤ÎÃͤ˰͸¤·¤Ê¤¤Êý¤¬Îɤ¤¤À¤í¤¦)¡£ +また、3 つのハフマン木の出力形式についてハフマン木が構築できない場合の +形式を右側に併記した。これは、復号語が 1 種類しかない場合を示しており +root がその復号語となる。例えば、c_len がこの形式で書かれていた場合は圧 +縮文を見ずに blocksize 分 root を出力すれば復号となる。なお、c_len がこ +の形式の場合 c_len のハフマン木を示す t_len も右側の形式になるが、この +ときの t_len の root の値は 0 にする事になっているようだ(ただ、復号処理 +はこの値に依存しない方が良いだろう)。 -°µ½Ìʸ¤Ï¡¢Ê¸»ú c(0 .. 255)¤È <Ťµ len, °ÌÃÖ off> ¤ò Huffman ÌÚ¤ÇÉä¹æ²½ -¤·¤¿ Huffman Éä¹æ¤ÎʤÓ(¤È°ÌÃÖ¤ÎÃÍ)¤Çɽ¤µ¤ì¤ë¡£ +圧縮文は、文字 c(0 .. 255)と <長さ len, 位置 off> を Huffman 木で符号化 +した Huffman 符号の並び(と位置の値)で表される。 - ¤Ï¡¢slide ¼­½ñË¡¤Ç¤Î°µ½Ì·ë²Ì¤Ç¤¢¤ê¡¢¼­½ñ¾å¤Î off °ÌÃ֤Πlen -ʸ»ú¤ò¤³¤Î·Á¼°¤Çɽ¤·¤Æ¤¤¤ë¡£¼­½ñ¤È¤Ï¡¢¸½ºßÉü¹æ¤ò³«»Ï¤·¤Æ¤¤¤ë°ÌÃÖ¤«¤é¼­ -½ñ¥µ¥¤¥ºÊ¬Á̤ä¿Éü¹æºÑ¤ß¤Îʿʸ¤ò»Ø¤¹¡£ + は、slide 辞書法での圧縮結果であり、辞書上の off 位置の len +文字をこの形式で表している。辞書とは、現在復号を開始している位置から辞 +書サイズ分遡った復号済みの平文を指す。 -°ÌÃÖ off ¤Ï¡¢0 ¤¬¡Ö1ʸ»úÁ°¡×¤ò¸½¤¹¡£½¾¤Ã¤Æ¼­½ñ¥µ¥¤¥º dicsiz ¤¬ 2^3 ¤Î¾ì -¹ç¤òÎã¤Ë¹Í¤¨¤ë¤È°ÌÃÖ¤ÎÃͤ¬ 2^3-1 ¤Î¾ì¹ç¤Ç¤Ï¡¢2^3 ʸ»úÁ°¤ò¼¨¤¹¤³¤È¤Ë¤Ê¤ë¡£ +位置 off は、0 が「1文字前」を現す。従って辞書サイズ dicsiz が 2^3 の場 +合を例に考えると位置の値が 2^3-1 の場合では、2^3 文字前を示すことになる。 - ¼­½ñ¥µ¥¤¥º - (Éü¹æºÑ¤ß¤Îʿʸ) + 辞書サイズ + (復号済みの平文) |-------------| 8 7 6 5 4 3 2 1 x ^ \ - | Éü¹æ³«»Ï°ÌÃÖ - x ¤«¤é 8 ʸ»úÁ°¤Î°ÌÃÖ + | 復号開始位置 + x から 8 文字前の位置 -½¾¤Ã¤Æ¡¢off ¤ÎÃͤÎÈÏ°Ï¤Ï 0 ... 2^dicbit ¤Ç¤¢¤ë¡£ +従って、off の値の範囲は 0 ... 2^dicbit である。 -Ťµ len ¤ÎÃÍ¤Ï 256 ¤Î¾ì¹ç¤Ëʸ»úÎóĹ 3 ¥Ð¥¤¥È¤ò¼¨¤¹¡£¤Ä¤Þ¤ê¡¢len ¤ËÂФ· -¤Æ len-256+3 ¤¬¼ÂºÝ¤ÎŤµ¤ò¼¨¤¹¡£(¤¿¤À¤·¡¢-lzs- ¤Î¾ì¹ç¤Ï len-256+2 ¤¬¼Â -ºÝ¤ÎŤµ¤È¤Ê¤ë) +長さ len の値は 256 の場合に文字列長 3 バイトを示す。つまり、len に対し +て len-256+3 が実際の長さを示す。(ただし、-lzs- の場合は len-256+2 が実 +際の長さとなる) -len ¤ÎÃͤ¬ 256 ¤«¤é»Ï¤Þ¤ë¤Î¤Ï¡¢1 ¥Ð¥¤¥È¤Îʸ»ú c ¤ÎÈÏ°Ï 0..255 ¤È½Å¤Ê¤é -¤Ê¤¤¤è¤¦¤Ë¤¹¤ë¤¿¤á¤Ç¤¢¤ë¡£(Ťµ¤Ï¡¢Ê¸»ú¤ÈƱ¤¸ Huffman ÌÚ¤ÇÉä¹æ²½¤µ¤ì¤ë) +len の値が 256 から始まるのは、1 バイトの文字 c の範囲 0..255 と重なら +ないようにするためである。(長さは、文字と同じ Huffman 木で符号化される) -¼ÂºÝ¤ÎŤµ¤¬ 3 ¤«¤é»Ï¤Þ¤ë¤Î¤Ï¡¢ ¤ÎÁȤ¬ 4 ¥Ð¥¤¥È¤Çɽ¤µ¤ì¤ë¤¿¤á¡¢ -¥Þ¥Ã¥ÁĹ¤È¤·¤Æ 2 ʸ»ú°Ê²¼¤ò ¤Î·Á¼°¤Çɽ¤¹¤È°µ½Ì¤Ç¤Ï¤Ê¤¯¿­Ä¹¤Ë -¤Ê¤Ã¤Æ¤·¤Þ¤¦¤«¤é¤Ç¤¢¤ë¡£ +実際の長さが 3 から始まるのは、 の組が 4 バイトで表されるため、 +マッチ長として 2 文字以下を の形式で表すと圧縮ではなく伸長に +なってしまうからである。 -# Ťµ¤¬ 3 ¤Î¾ì¹ç¤âƱ¤¸¤è¤¦¤Ë»×¤¨¤ë¤¬¤Ê¤¼¤À¤í¤¦¤«¡© -# ¤Ï¤è¤êÀµ³Î¤Ë¤Ï 3 ¥Ð¥¤¥È¤È 1 ¥Ó¥Ã¥È¤Ç¤¢¤ë¡£¤È¤¤¤¦¤Î¤â len ¤¬ -# 256 ¤«¤é»Ï¤Þ¤ë¤«¤é len ¤Î¾ðÊó¤Ï 9 ¥Ó¥Ã¥ÈɬÍפǤ¢¤ë¡£len ¤¬ 256...510 -# ¤ÎÈÏ°Ï¤Ê¤Î¤Ç 8 ¥Ó¥Ã¥È¤Î¤è¤¦¤Ë¤â»×¤¨¤ë¤¬Ê¸»ú c ¤È¤ÎȽÊ̤Τ¿¤á¤Î¾ðÊó¤È -# ¤·¤Æ 1 ¥Ó¥Ã¥È;·×¤ËɬÍפʤΤǤ¢¤ë¡£¤³¤ì¤ÏͽÁÛ¤À¤¬¡¢Åö»þ -lh5- ¤Î¼­½ñ -# ¥µ¥¤¥º¤Ç¤¢¤ì¤Ð°ÌÃÖ¤Ï 13 ¥Ó¥Ã¥È¤·¤«»ÈÍѤ·¤Ê¤¤¤¿¤á¡¢ ¤Ï 22 ¥Ó¥Ã -# ¥È¤È¸«¤Ê¤»¤ë¡£½¾¤Ã¤Æ 3 ¥Ð¥¤¥È¤Ï ¤Î·Á¼°¤Ë¤·¤¿Êý¤¬Îɤ¤¤ÈȽÃÇ -# ¤·¤¿¤Î¤Ç¤Ï¤Ê¤¤¤À¤í¤¦¤«¡©¤Ç¤Ï¡¢-lh7- ¤Î¾ì¹ç¤Ï¡¢Ä¹¤µ¤ÎºÇ¾®ÃÍ¤Ï 4 ¤Ë¤·¤¿ -# Êý¤¬Îɤ¤¤Î¤À¤í¤¦¤«¡©¤ª¤½¤é¤¯°ÌÃÖ¤ÎÃÍ¤Ë 16 ¥Ó¥Ã¥È¤¹¤Ù¤Æ»ÈÍѤ¹¤ë³ÎΨ¤¬ -# Ä㤯¿¤¯¤Î¾ì¹ç¤Ï¸ú²Ì¤¬¤Ê¤¤¤Î¤Ç¤Ï¤Ê¤¤¤«¤È»×¤¦¡£ +# 長さが 3 の場合も同じように思えるがなぜだろうか? +# はより正確には 3 バイトと 1 ビットである。というのも len が +# 256 から始まるから len の情報は 9 ビット必要である。len が 256...510 +# の範囲なので 8 ビットのようにも思えるが文字 c との判別のための情報と +# して 1 ビット余計に必要なのである。これは予想だが、当時 -lh5- の辞書 +# サイズであれば位置は 13 ビットしか使用しないため、 は 22 ビッ +# トと見なせる。従って 3 バイトは の形式にした方が良いと判断 +# したのではないだろうか?では、-lh7- の場合は、長さの最小値は 4 にした +# 方が良いのだろうか?おそらく位置の値に 16 ビットすべて使用する確率が +# 低く多くの場合は効果がないのではないかと思う。 -¤Þ¤¿¡¢maxmatch{256}¤¬ºÇÂç¥Þ¥Ã¥ÁŤǤ¢¤ê¡¢¤³¤Î¤È¤­¤Î len ¤ÎÃÍ¤Ï -256+maxmatch-3{509=NC-1} ¤Ç¤¢¤ë¡£ +また、maxmatch{256}が最大マッチ長であり、このときの len の値は +256+maxmatch-3{509=NC-1} である。 - len(Éü¹æ¸ì) ¼ÂºÝ¤ÎŤµ + len(復号語) 実際の長さ ---------------------------------- 256..256+maxmatch-3 3..maxmatch -¤³¤ì¤é¤ò°µ½Ì¤·¤¿·Á¼°¡Ö°µ½Ìʸ¡×¤Ï Huffman Éä¹æ¤ÎϢ³(¤ª¤è¤Ó°ÌÃÖ¤ÎÃÍ¡£¸å -½Ò)¤Ç¤¢¤ë¡£°µ½Ìʸ¤Î¥µ¥¤¥º¤Ï¾å¿Þ¤Î blocksize ¤Çɽ¤µ¤ì¡¢blocksize ¿ô¤Îʸ -»ú¿ô¤Î¾ðÊ󤬽ÐÎϤµ¤ì¤Æ¤¤¤ë¡£¤³¤³¤Ç¡¢¡Öʸ»ú¿ô¡×¤Ï <Ťµ, °ÌÃÖ> ¤ÎÁȤâ 1 -ʸ»ú¤È¤·¤Æ¥«¥¦¥ó¥È¤µ¤ì¤ë¡£Ê¸»ú¤Ê¤Î¤«¡¢<Ťµ, °ÌÃÖ> ¤ÎÁȤʤΤ«¤ÎȽÊ̤ϡ¢ +これらを圧縮した形式「圧縮文」は Huffman 符号の連続(および位置の値。後 +è¿°)である。圧縮文のサイズは上図の blocksize で表され、blocksize 数の文 +字数の情報が出力されている。ここで、「文字数」は <長さ, 位置> の組も 1 +文字としてカウントされる。文字なのか、<長さ, 位置> の組なのかの判別は、 - Éü¹æ¤·¤¿1ʸ»ú >= 256 ¤Î¾ì¹ç - Ťµ¤ò¼¨¤¹¡£(¤½¤·¤Æ¤½¤Îľ¸å¤Ë°ÌÃ֤ΠHuffman Éä¹æ¤¬¤¢¤ë) + 復号した1文字 >= 256 の場合 + 長さを示す。(そしてその直後に位置の Huffman 符号がある) - Éü¹æ¤·¤¿1ʸ»ú < 256 ¤Î¾ì¹ç - ʸ»ú¤ò¼¨¤¹¡£ + 復号した1文字 < 256 の場合 + 文字を示す。 -¤È¤Ê¤Ã¤Æ¤¤¤ë¡£¤½¤·¤Æ¡¢Ê¸»ú¤ÈŤµ¤Î Huffman Éä¹æ¤Ï¡¢Huffman ÌڤξðÊó¤ò¼¨ -¤¹ c_len ¤Ë¤è¤êÉü¹æ¤µ¤ì¡¢°ÌÃ֤ΠHuffman Éä¹æ¤Ï¡¢p_len ¤Ë¤è¤êÉü¹æ¤µ¤ì¤ë¡£ +となっている。そして、文字と長さの Huffman 符号は、Huffman 木の情報を示 +す c_len により復号され、位置の Huffman 符号は、p_len により復号される。 -°ÌÃ֤ΠHuffman Éä¹æ¤Ï°ÌÃ֤ξðÊó¤Î¾å°Ì¥Ó¥Ã¥È¤¬ 0 ¤Ç¤¢¤ëÉôʬ¤ò½ü¤¤¤¿¥Ó¥Ã -¥ÈŤòÉä¹æ²½¤·¤¿¤â¤Î¤Ç¤¢¤ê°ÌÃÖ¤½¤Î¤â¤Î¤ÎÉä¹æ¤Ç¤Ï¤Ê¤¤¡£½¾¤Ã¤Æ°ÌÃÖ¤ÎÉä¹æ -¤Ï +位置の Huffman 符号は位置の情報の上位ビットが 0 である部分を除いたビッ +ト長を符号化したものであり位置そのものの符号ではない。従って位置の符号 +は +------------------------------+----------+ - | °ÌÃ֤ΥӥåÈŤΠHuffman Éä¹æ| °ÌÃÖ¤ÎÃÍ | - | (p_len¤«¤éÉü¹æ) | | + | 位置のビット長の Huffman 符号| 位置の値 | + | (p_lenから復号) | | +------------------------------+----------+ -¤È Huffman Éä¹æ¤Ë³¤¤¤Æ¼ÂºÝ¤ÎÃͤò¼¨¤¹¥Ó¥Ã¥ÈÎ󤬽ÐÎϤµ¤ì¤Æ¤¤¤ë¡£Îã¤Ç¼¨¤¹ -¤È°Ê²¼¤ÎÄ̤ê¤À¡£ +と Huffman 符号に続いて実際の値を示すビット列が出力されている。例で示す +と以下の通りだ。 ---------------------------------------------------------------------------- -°ÌÃÖ(off)¤Î½ÐÎÏÎã +位置(off)の出力例 -off = 64 ¤Î¾ì¹ç +off = 64 の場合 |---- 16 bit -------| +----+----+----+----+ @@ -5725,17 +5725,17 @@ off |0000 0000 0100 0000| +----+----+----+----+ |-7 bit-| -¤³¤Î°µ½Ìʸ¤Ï°Ê²¼(Ťµ¤¬ 7 bit ¤Ç¤¢¤ë¤È¤¤¤¦¾ðÊó(HuffmanÉä¹æ²½)¤ÈÃͤΥڥ¢) +この圧縮文は以下(長さが 7 bit であるという情報(Huffman符号化)と値のペア) |-6 bit-| +-----------------+-------+ - | 7 ¤ÎHuffmanÉä¹æ |00 0000| + | 7 のHuffman符号 |00 0000| +-----------------+-------+ -¤³¤ÎÎã¤Ç ɬÍץӥåȤǤ¢¤ë 7 bit ÌܤÏɬ¤º 1 ¤Ç¤¢¤ë¤¿¤áÃͤÎÉôʬ¤Ï 6 bit -½ÐÎϤ¹¤ì¤Ð¤è¤¤¡£ +この例で 必要ビットである 7 bit 目は必ず 1 であるため値の部分は 6 bit +出力すればよい。 -off = 1 ¤Î¾ì¹ç +off = 1 の場合 |---- 16 bit -------| +----+----+----+----+ @@ -5744,13 +5744,13 @@ off |0000 0000 0000 0001| |-| 1 bit -¤³¤Î°µ½Ìʸ¤Ï°Ê²¼(Ťµ¤¬ 1 bit ¤Ç¤¢¤ë¤È¤¤¤¦¾ðÊó¤Î¤ß) +この圧縮文は以下(長さが 1 bit であるという情報のみ) +-----------------+ - | 1 ¤ÎHuffmanÉä¹æ | + | 1 のHuffman符号 | +-----------------+ -off = 0 ¤Î¾ì¹ç +off = 0 の場合 |---- 16 bit -------| +----+----+----+----+ @@ -5759,247 +5759,247 @@ off |0000 0000 0000 0000| || 0 bit -¤³¤Î°µ½Ìʸ¤Ï°Ê²¼(Ťµ¤¬ 0 bit ¤Ç¤¢¤ë¤È¤¤¤¦¾ðÊó¤ÏÃͤ¬ 0 ¤È¸«¤Ê¤µ¤ì¤ë) +この圧縮文は以下(長さが 0 bit であるという情報は値が 0 と見なされる) +-----------------+ - | 0 ¤ÎHuffmanÉä¹æ | + | 0 のHuffman符号 | +-----------------+ ---------------------------------------------------------------------------- -# °ÌÃÖ¤òľÀÜ Huffman Éä¹æ²½¤·¤Ê¤¤Íýͳ¤Ï°ÌÃÖ¤ò»Ø¤¹ÃÍ¤Ï slide ¼­½ñ¾å¤ÎǤ -# °Õ¤Î°ÌÃÖ¤ò»Ø¤¹¤¿¤á¥Ç¡¼¥¿¤ÎÈϰϤ¬¹­¤¯(-lh7- ¤Î¾ì¹ç¤Ç 0 ... 2^16) -# Huffman Éä¹æ²½¤Ë¤è¤ë°µ½Ì¤Î¸ú²Ì¤¬´üÂԤǤ­¤Ê¤¤¤¿¤á¤À¤È»×¤ï¤ì¤ë¡£°ìÊý¡¢ -# °ÌÃÖ¾ðÊó¤Ï¼­½ñ¾å¤ÎÈæ³ÓŪ¶á¤¤°ÌÃ֤˥ޥåÁ¤·¤ä¤¹¤¤¤Ï¤º¤Ç¤¢¤ë¤«¤é Huffman -# Éä¹æ²½¤ÎÂоݤǤ¢¤ë¥Ó¥Ã¥ÈĹ¤Ï¾®¤µ¤¤ÃͤËÊФê¤ä¤¹¤¤¤Ï¤º¤À¡£(ÊФ꤬¤¢¤ë -# ¤È Huffman Éä¹æ²½¤Î¸ú²Ì¤¬¹â¤¤) +# 位置を直接 Huffman 符号化しない理由は位置を指す値は slide 辞書上の任 +# 意の位置を指すためデータの範囲が広く(-lh7- の場合で 0 ... 2^16) +# Huffman 符号化による圧縮の効果が期待できないためだと思われる。一方、 +# 位置情報は辞書上の比較的近い位置にマッチしやすいはずであるから Huffman +# 符号化の対象であるビット長は小さい値に偏りやすいはずだ。(偏りがある +# と Huffman 符号化の効果が高い) -Huffman ÌڤξðÊó¤ò¥Õ¥¡¥¤¥ë¤Ë½ÐÎϤ¹¤ëºÝ p_len, c_len ¤Ï¡¢¤½¤ì¤¾¤ì¤ËŬ¤· -¤¿·Á¼°¤Ç°µ½Ì¤·¤Æ½ÐÎϤµ¤ì¤ë¡£ÆÃ¤Ë c_len ¤Ï¡¢¤µ¤é¤Ë¡¢t_len ¤Ë¤è¤ê -Huffman Éä¹æ²½¤¹¤ë¤³¤È¤Ç°µ½Ì¤ò¹Ô¤¦¡£ +Huffman 木の情報をファイルに出力する際 p_len, c_len は、それぞれに適し +た形式で圧縮して出力される。特に c_len は、さらに、t_len により +Huffman 符号化することで圧縮を行う。 -¥Ï¥Õ¥Þ¥óÌÚ {p,c,t}_len ¤Ï¤É¤ÎÉü¹æ¸ì¤¬¥Ï¥Õ¥Þ¥óÌڤΤɤγ¬Áؤˤ¢¤ë¤«¤Î¾ðÊó -¤Ë¤è¤êɽ¤µ¤ì¤Æ¤¤¤ë¡£¤³¤ì¤À¤±¤Î¾ðÊó¤À¤ÈÌÚ¤ò°ì°Õ¤Ëɽ¤¹¤³¤È¤¬¤Ç¤­¤Ê¤¤¤è¤¦ -¤Ë»×¤¨¤ë¤¬¡¢¼ÂºÝ¤Ï +ハフマン木 {p,c,t}_len はどの復号語がハフマン木のどの階層にあるかの情報 +により表されている。これだけの情報だと木を一意に表すことができないよう +に思えるが、実際は - Huffman ÌÚ1 Huffman ÌÚ2 + Huffman 木1 Huffman 木2 . . / \ / \ . a a . / \ / \ c b b c -Huffman ÌÚ1 ¤È Huffman ÌÚ2 ¤Ï»Þ¤Î¿­¤ÓÊý¤ÈÍÕ¤Ëʸ»ú¤ò¿¶¤ë½çÈ֤ΰ㤤¤Ç¤·¤« -¤Ê¤¤¡£¤½¤³¤Ç¡¢LHA ¤Ç¤Ï¡¢°Ê²¼¤Îµ¬Â§¤òÀߤ±¤ë¤³¤È¤Ç¡¢{p,c,t}_len ¤Î¾ðÊó¤À -¤±¤ÇÌÚ¤ò¹½ÃۤǤ­¤ë¤è¤¦¤Ë¤·¤Æ¤¤¤ë¡£ +Huffman 木1 と Huffman 木2 は枝の伸び方と葉に文字を振る順番の違いでしか +ない。そこで、LHA では、以下の規則を設けることで、{p,c,t}_len の情報だ +けで木を構築できるようにしている。 - o ±¦¤ÎÌÚ¤òÍ¥À褷¤ÆºîÀ®¤¹¤ë¡£(±¦¤Î»Þ¤ò¥Ó¥Ã¥È 1 ¤È¤¹¤ë) - o Ʊ¤¸³¬ÁؤÎÉü¹æ¸ì¤Î³ä¤êÅö¤Æ¤Ï¥³¡¼¥É½ç¤Ëº¸¤«¤é³ä¤êÅö¤Æ¤ë¡£ + o 右の木を優先して作成する。(右の枝をビット 1 とする) + o 同じ階層の復号語の割り当てはコード順に左から割り当てる。 -Î㤨¤Ð¡¢ +例えば、 c_len['a'] = 2 c_len['b'] = 1 c_len['c'] = 3 c_len['d'] = 3 -¤È¤¤¤¦¾ðÊ󤬽ñ¤«¤ì¤Æ¤¤¤ë¾ì¹ç¡¢ +という情報が書かれている場合、 - ³¬ÁØ 1 ¤Ë 1 ¸Ä¤ÎÍÕ(Ãͤ¬ 1 ¤Ç¤¢¤ë c_len[] ¤¬ 1 ¸Ä) - ³¬ÁØ 2 ¤Ë 1 ¸Ä¤ÎÍÕ(Ãͤ¬ 2 ¤Ç¤¢¤ë c_len[] ¤¬ 1 ¸Ä) - ³¬ÁØ 3 ¤Ë 2 ¸Ä¤ÎÍÕ(Ãͤ¬ 3 ¤Ç¤¢¤ë c_len[] ¤¬ 2 ¸Ä) + 階層 1 に 1 個の葉(値が 1 である c_len[] が 1 個) + 階層 2 に 1 個の葉(値が 2 である c_len[] が 1 個) + 階層 3 に 2 個の葉(値が 3 である c_len[] が 2 個) -¤¬¤¢¤ë¤Î¤Ç°Ê²¼¤Î Huffman ÌڤηÁ¤Ë·è¤Þ¤ë(±¦¤ÎÌÚ¤òÍ¥À褷¤ÆºîÀ®¤¹¤ë)¡£ +があるので以下の Huffman 木の形に決まる(右の木を優先して作成する)。 . / \ - . . -- ³¬ÁØ1 + . . -- 階層1 / \ - . . -- ³¬ÁØ2 + . . -- 階層2 / \ - . . -- ³¬ÁØ3 + . . -- 階層3 -¤½¤·¤Æ³Æ³¬ÁØËè¤Ëʸ»ú¤ò¥³¡¼¥É½ç¤Ë³ä¤êÅö¤Æ¤ë¤È +そして各階層毎に文字をコード順に割り当てると . / \ - b . -- ³¬ÁØ1 + b . -- 階層1 / \ - a . -- ³¬ÁØ2 + a . -- 階層2 / \ - c d -- ³¬ÁØ3 + c d -- 階層3 -¤È°ì°Õ¤ËÄê¤Þ¤ë¤³¤È¤È¤Ê¤ë¡£(¡Ö³¬ÁØËè¤Î¥³¡¼¥É½ç¡×¤Î°ÕÌ£¤¬Àµ³Î¤ËÅÁ¤ï¤ë¤è¤¦ -¤¢¤¨¤Æ¡¢b ¤È a ¤òµÕ¤Ë¤·¤Æ¤ß¤¿¡£) +と一意に定まることとなる。(「階層毎のコード順」の意味が正確に伝わるよう +あえて、b と a を逆にしてみた。) -¤Ê¤ª¡¢{p,c,t}_len ¤ÎÃͤϤ¢¤ëʸ»ú¤Î³¬ÁؤΰÌÃÖ¤ò¼¨¤¹¤¬¡¢¤³¤ì¤Ï¤Ä¤Þ¤ê¤¢¤ë -ʸ»ú¤ò Huffman Éä¹æ²½¤·¤¿¤È¤­¤ÎÉä¹æĹ(bit Ĺ)¤ò¼¨¤·¤Æ¤¤¤ë¤³¤È¤Ë¤Ê¤ë¡£°Ê -¹ß¡¢{p,c,t}_len ¤Ïź¤¨»ú¤¬Éü¹æ¸ì¡¢Ãͤ¬Éä¹æŤòɽ¤¹ÇÛÎó¤Ç¤¢¤ë¤â¤Î¤È¤·¤Æ -ÀâÌÀ¤ò¹Ô¤¦¡£¤³¤ÎÇÛÎó¤Î¥µ¥¤¥º¤Ï°µ½ÌÂоݤÎʸ»ú½¸¹ç¤ÎÍ×ÁÇ¿ô¤Ç¤¢¤ê¡¢³ÆÍ×ÁÇ -¤Ï 0..16 ¤ÎÃͤò»ý¤Ä¡£(LHA ¤Î Huffman ÌڤΥ롼¥ë¤ÇÌڤγ¬ÁØ¤Ï 16 ¤Þ¤Ç¤È¤Ê¤Ã -¤Æ¤¤¤ë)¤½¤·¤Æ¡¢ÃÍ 0 ¤Ï¤½¤Îʸ»ú¤¬Ê¿Ê¸¤Ë¸½¤ì¤Ê¤¤¤³¤È¤ò¼¨¤¹¡£ +なお、{p,c,t}_len の値はある文字の階層の位置を示すが、これはつまりある +文字を Huffman 符号化したときの符号長(bit 長)を示していることになる。以 +降、{p,c,t}_len は添え字が復号語、値が符号長を表す配列であるものとして +説明を行う。この配列のサイズは圧縮対象の文字集合の要素数であり、各要素 +は 0..16 の値を持つ。(LHA の Huffman 木のルールで木の階層は 16 までとなっ +ている)そして、値 0 はその文字が平文に現れないことを示す。 ---------------------------------------------------------------------------- - Huffman ÌÚ¡¡ Í×ÁÇ¿ô ÃͤÎÈÏ°Ï Í×ÁÇ¿ô¤Î¥Ó¥Ã¥ÈĹ - (ÇÛÎó) (Éä¹æĹ) + Huffman 木  要素数 値の範囲 要素数のビット長 + (配列) (符号長) p_len np 0..16 pbit c_len NC 0..16 CBIT t_len NT 0..16 TBIT - Í×ÁÇ¿ô¤Ï°µ½ÌÂоݤÎʸ»ú½¸¹ç¤Î¿ô - c_len[x]=0 ¤Î¾ì¹ç¡¢Ê¸»ú x ¤ÏÊ£¹ç¤·¤¿·ë²Ì¤Ë¸½¤ì¤Ê¤¤ - Í×ÁÇ¿ô¤Î¥Ó¥Ã¥ÈŤÏÍ×ÁÇ¿ô¤òɽ¸½¤¹¤ë¤Î¤ËɬÍ×¤Ê¥Ó¥Ã¥È - Ĺ(¤³¤Î¸å¤Ç½Ð¤Æ¤¯¤ë) + 要素数は圧縮対象の文字集合の数 + c_len[x]=0 の場合、文字 x は複合した結果に現れない + 要素数のビット長は要素数を表現するのに必要なビット + 長(この後で出てくる) ---------------------------------------------------------------------------- -¤Ç¤Ï¡¢Huffman ÌڤξðÊó¤Î½ÐÎÏ·Á¼°¤Ë¤Ä¤¤¤ÆÀ°Íý¤·¤è¤¦¡£ +では、Huffman 木の情報の出力形式について整理しよう。 -¤Þ¤º¡¢p_len[] ¤Î½ÐÎÏ¥Õ¥©¡¼¥Þ¥Ã¥È¤ò²¼µ­¤Ë¼¨¤¹¡£ +まず、p_len[] の出力フォーマットを下記に示す。 ---------------------------------------------------------------------------- -< p_len[] ¤Î½ÐÎÏ¥Õ¥©¡¼¥Þ¥Ã¥È > +< p_len[] の出力フォーマット > 0 pbit{4 or 5} +-------+-----------+-----------+-- --+-----------+ | n | p_len[0] | p_len[1] | ... p_len[n-1]| +-------+-----------+-----------+-- --+-----------+ -p_len[i] <= 6 ¤Î¾ì¹ç +p_len[i] <= 6 の場合 0 3bit +-----+ p_len[i] | | | | +-----+ -p_len[i] >= 7 ¤Î¾ì¹ç +p_len[i] >= 7 の場合 0 p_len[i] - 3 +----------------+ p_len[i] |1 1 1 1 ... 1 0 | +----------------+ -p_len[n...np] ¤Ï¡¢0 ¤È¤Ê¤ë¡£ +p_len[n...np] は、0 となる。 ---------------------------------------------------------------------------- -Àè¤Ë¤â½ñ¤¤¤¿Ä̤ê p_len ¤Ï°ÌÃÖ¤ÎÃͤÎɬÍץӥåÈŤò Huffman Éä¹æ²½¤·¤¿¤È -¤­¤ÎÌڤξðÊó¤Ç¤¢¤ë¡£¥¹¥é¥¤¥É¼­½ñ¤Î¥µ¥¤¥º¤Ï dicsiz ¤Ç¤¢¤ê -lh7- ¤Î¾ì¹ç¤Ç -16 bit ¤Ç°ÌÃÖ¤ò»Ø¤¹¤³¤È¤¬¤Ç¤­¤ë¤«¤é¡¢p_len ¤Ï°ÌÃ֤ΥӥåÈĹ 0 .. 16 ¤Î -17¸Ä(np¸Ä)¤ÎÃͤò Huffman Éä¹æ²½¤·¤¿·ë²Ì(¤ÎÉä¹æĹ)¤Ç¤¢¤ë¡£ +先にも書いた通り p_len は位置の値の必要ビット長を Huffman 符号化したと +きの木の情報である。スライド辞書のサイズは dicsiz であり -lh7- の場合で +16 bit で位置を指すことができるから、p_len は位置のビット長 0 .. 16 の +17個(np個)の値を Huffman 符号化した結果(の符号長)である。 -¤½¤·¤Æ p_len ¼«ÂΤνÐÎϤˤĤ¤¤Æ¤Ï¡¢0 .. 6 ¤ÎÃÍ(Huffman Éä¹æĹ)¤Ë¤Ä¤¤¤Æ -¤Ï 3 bit ¤Ç¡¢¤½¤ì°Ê¾å¤ÎÃͤˤĤ¤¤Æ¤Ï 0 ¤¬¸½¤ì¤ë¤Þ¤Ç¤Î¥Ó¥Ã¥È¿ô¤Çɽ¤¹¤è¤¦ -¤Ë¤Ê¤Ã¤Æ¤¤¤ë¡£ +そして p_len 自体の出力については、0 .. 6 の値(Huffman 符号長)について +は 3 bit で、それ以上の値については 0 が現れるまでのビット数で表すよう +になっている。 -¤Ê¤ª p_len ¤Ï np ¸Ä¤Î¸ÇÄêĹÇÛÎó¤À¤¬¡¢½ÐÎÏ·Á¼°¤È¤·¤Æ¤ÏÀèƬ¤Ë pbit Éý -¤Î p_len ¤ÎÍ×ÁÇ¿ô¤ò½ÐÎϤ·¤Æ¤¤¤ë¡£¤³¤ì¤Ï¤Ê¤¼¤«¤È¤¤¤¦¤È p_len ¤Î¸åÊý¤ÎÃÍ -(p_len[n...np])¤¬ 0 ¤Ç¤¢¤ë¾ì¹ç¤Ë¤½¤ÎÍ×ÁǤò½ÐÎϤ·¤Ê¤¤¤ÇºÑ¤à¤è¤¦¤Ë¤¹¤ë¤¿ -¤á¤Ç¤¢¤ë¡£¤³¤ì¤Ï¾¤Î Huffman Éä¹æ¤Ë¤Ä¤¤¤Æ¤âƱÍͤǤ¢¤ë¡£ +なお p_len は np 個の固定長配列だが、出力形式としては先頭に pbit 幅 +の p_len の要素数を出力している。これはなぜかというと p_len の後方の値 +(p_len[n...np])が 0 である場合にその要素を出力しないで済むようにするた +めである。これは他の Huffman 符号についても同様である。 -# ¤Ê¤¼¡¢¤³¤Î¤è¤¦¤Ê·Á¼°¤Ë¤Ê¤Ã¤Æ¤¤¤ë¤«¹Í¤¨¤Æ¤ß¤¿¡£ +# なぜ、このような形式になっているか考えてみた。 # -# p_len ¤ÎÃͤÎÈϰϤϡ¢0 .. 16 ¤Ç¤¢¤ë¤«¤é 1 Í×ÁÇ¤Ï 6 bit ¤Çɽ¸½²Äǽ¤Ç¤¢¤ê¡¢ -# ñ½ã¤Ë½ÐÎϤ·¤¿¾ì¹ç¤ò¹Í¤¨¤ë¤È 6 * 17 = 102 bit ¤Çɽ¸½²Äǽ¤Ç¤¢¤ë¡£ +# p_len の値の範囲は、0 .. 16 であるから 1 要素は 6 bit で表現可能であり、 +# 単純に出力した場合を考えると 6 * 17 = 102 bit で表現可能である。 # -# °ìÊý¡¢p_len ¤Î½ÐÎÏ·Á¼°¤Î¾ì¹ç¡¢LHA ¤Î Huffman Ìڤγ¬ÁؤϺÇÂç 16 ³¬ÁؤǤ¢ -# ¤ë¤³¤È¤«¤é¡¢ºÇ°­¤¹¤Ù¤Æ¤Î p_len[] ¤Ë¤Ä¤¤¤Æ¥Ó¥Ã¥È¿ô¤Î·Á¼°(p_len[] >= 7) -# ¤¬»È¤ï¤ì¤¿¾ì¹ç 1 ¤Ä¤Î p_len[] ¤Ë 16-3 bit * 17 = 221 bit »È¤¦¤³¤È¤Ë¤Ê -# ¤êºÇ°­¤Î»ÈÍÑÎΰè¤ÏÂ礭¤¯¤Ê¤Ã¤Æ¤·¤Þ¤¦¤è¤¦¤Ë»×¤¨¤Æ¤·¤Þ¤¦¡£ +# 一方、p_len の出力形式の場合、LHA の Huffman 木の階層は最大 16 階層であ +# ることから、最悪すべての p_len[] についてビット数の形式(p_len[] >= 7) +# が使われた場合 1 つの p_len[] に 16-3 bit * 17 = 221 bit 使うことにな +# り最悪の使用領域は大きくなってしまうように思えてしまう。 # -# ¤·¤«¤·¡¢¼ÂºÝ¤Ë¤Ï¤½¤¦¤Ï¤Ê¤é¤Ê¤¤¡£¤È¤¤¤¦¤Î¤â np ¤¬ 17 ¤Ç¤¢¤ë¤«¤é -# Huffman ÌÚ¤ÎÍդοô¤ÏºÇÂç¤Ç¤â 17 ¤Ë¤·¤«¤Ê¤é¤Ê¤¤¡£¤½¤·¤ÆÍդοô¤¬ np ¤Ç -# ºÇ¤â Huffman ÌÚ¤¬¿¼¤¯¤Ê¤ë¤Î¤Ï +# しかし、実際にはそうはならない。というのも np が 17 であるから +# Huffman 木の葉の数は最大でも 17 にしかならない。そして葉の数が np で +# 最も Huffman 木が深くなるのは # # . # / \ -# . . -- 1 ³¬ÁØÌÜ +# . . -- 1 階層目 # / \ -# . . -- 2 ³¬ÁØÌÜ +# . . -- 2 階層目 # / \ -# . . -- 3 ³¬ÁØÌÜ +# . . -- 3 階層目 # : # . # / \ -# . . -- 16 ³¬ÁØÌÜ +# . . -- 16 階層目 # -# ¤È¤Ê¤ë¾ì¹ç¤Ç¡¢¤³¤Î¾ì¹ç¤Î p_len ¤Î½ÐÎϥӥåÈĹ¤Ï +# となる場合で、この場合の p_len の出力ビット長は # # 2*(16-3) + (15-3) + ... (7-3) + 6*3 # = 2*13 + 12 + ... 4 + 18 # = 167 bit # -# ¤Ç¤¢¤ë¡£Ã±½ã¤Ë½ÐÎϤ¹¤ë¾ì¹ç¤ËÈæ¤Ù¤ë¤È 65 bit Áý¤¨¤ë¡£¤Þ¤¿¡¢1 ³¬Áظº¤é -# ¤·¤¿¾ì¹ç¤Ï¡¢ +# である。単純に出力する場合に比べると 65 bit 増える。また、1 階層減ら +# した場合は、 # # . # / \ -# . . -- 1 ³¬ÁØÌÜ +# . . -- 1 階層目 # / \ -# . . -- 2 ³¬ÁØÌÜ +# . . -- 2 階層目 # / \ -# . . -- 3 ³¬ÁØÌÜ +# . . -- 3 階層目 # : # / \ -# . . -- 13 ³¬ÁØÌÜ +# . . -- 13 階層目 # / \ # . . # / \ / \ -# . . . . -- 15 ³¬ÁØÌÜ +# . . . . -- 15 階層目 # # 4*(15-3) + (13-3) + ... (7-3) + 6*3 # = 4*12 + 10 + ... 4 + 18 # = 115 bit # -# ¤Ç¤¢¤ë¡£¤Ê¤ª¡¢¤³¤ÎÌڤξì¹ç¤âÍÕ¤Ï np ¸Ä¤¢¤ë¡£¤â¤¦ 1 ³¬Áظº¤é¤·¤Æ¤ß¤è¤¦¡£ +# である。なお、この木の場合も葉は np 個ある。もう 1 階層減らしてみよう。 # # . # / \ -# . . -- 1 ³¬ÁØÌÜ +# . . -- 1 階層目 # / \ -# . . -- 2 ³¬ÁØÌÜ +# . . -- 2 階層目 # / \ -# . . -- 3 ³¬ÁØÌÜ +# . . -- 3 階層目 # : # / \ -# . . -- 12 ³¬ÁØÌÜ +# . . -- 12 階層目 # / \ / \ -# . . . . -- 13 ³¬ÁØÌÜ +# . . . . -- 13 階層目 # / \ / \ -# . . . . -- 14 ³¬ÁØÌÜ +# . . . . -- 14 階層目 # # # 4*(14-3) + 2*(13-3) + (11-3) + ... + (7-3) + 6*3 # = 4*11 + 2*10 + 8 + ... 4 + 18 # = 112 bit # -# ¤³¤ÎÄ´»Ò¤Ç¡¢¤µ¤é¤Ë¸º¤é¤¹¤È +# この調子で、さらに減らすと # -# 13 ³¬ÁØ +# 13 階層 # 4*(13-3) + 2*(12-3) + (10-3) + ... + (7-3) + 6*3 # = 4*10 + 2*9 + 7 + ... + 4 + 18 # = 98 # -# 13 ³¬ÁØÌܤÇñ½ã¤Ë½ÐÎϤ¹¤ë¤è¤ê¤â¾®¤µ¤¯¤Ê¤Ã¤¿¡£´¶³ÐŪ¤Ë¤Ï¡¢¤¢¤Þ¤ê¸ú²Ì¤Ï -# ´üÂԤǤ­¤Ê¤¤¤è¤¦¤Ë¸«¤¨¤ë¡£¤³¤ì¤Ï¶²¤é¤¯¤Ï p_len ¤Ë¤Ä¤¤¤Æ¤ÏÉä¹æŤ¬ 6 -# °Ê²¼¤Ë¤Ê¤ë¾ì¹ç¤¬Â¿¤¤¤Î¤Ç¤¢¤í¤¦(¤¹¤Ù¤Æ¤¬ 6 °Ê²¼¤Ç¤¢¤ì¤Ð¡¢3 * 17 = 51 -# bit ¤Ç¤¢¤ê 51 bit ºï¸º¤Ç¤­¤ë)¡£³¬Áؤ¬ 6 ¤Ç¤¢¤ë Huffman ÌÚ¤ÎÍդοô¤ÏºÇ -# Âç 2^6 {64} ¤Ç¤¢¤ë¤«¤é NP{17} ¼ïÎà¤Îʿʸ¤ò³ÊǼ¤¹¤ëΨ¤¬¹â¤¤¤Î¤Ç¤¢¤í¤¦¡£ +# 13 階層目で単純に出力するよりも小さくなった。感覚的には、あまり効果は +# 期待できないように見える。これは恐らくは p_len については符号長が 6 +# 以下になる場合が多いのであろう(すべてが 6 以下であれば、3 * 17 = 51 +# bit であり 51 bit 削減できる)。階層が 6 である Huffman 木の葉の数は最 +# 大 2^6 {64} であるから NP{17} 種類の平文を格納する率が高いのであろう。 -³¤¤¤Æ c_len[] ¤Î½ÐÎÏ¥Õ¥©¡¼¥Þ¥Ã¥È¤ò¼¨¤¹¡£ +続いて c_len[] の出力フォーマットを示す。 ---------------------------------------------------------------------------- -< c_len[] ¤Î½ÐÎÏ¥Õ¥©¡¼¥Þ¥Ã¥È > +< c_len[] の出力フォーマット > 0 CBIT{9} +-------+-----------+-----------+-- --+-----------+ | n | c_len[0] | c_len[1] | ... c_len[n-1]| +-------+-----------+-----------+-- --+-----------+ -c_len[i] == 0 ¤Î¾ì¹ç +c_len[i] == 0 の場合 - 0 ¤¬Â³¤¯¿ô¤ò count ¤È¤¹¤ë¤È¡¢ + 0 が続く数を count とすると、 - count == 1 ¤Î¾ì¹ç + count == 1 の場合 t_len[0] <----------> @@ -6007,7 +6007,7 @@ c_len[i] == 0 | t_code[0] | +------------+ - count == 2 ¤Î¾ì¹ç + count == 2 の場合 t_len[0] t_len[0] <----------> <----------> @@ -6015,7 +6015,7 @@ c_len[i] == 0 | t_code[0] | t_code[0] | +------------+------------+ - count == 3..18 ¤Î¾ì¹ç + count == 3..18 の場合 t_len[1] 4 bit <----------> <------> @@ -6023,7 +6023,7 @@ c_len[i] == 0 | t_code[1] |count-3| +------------+-------+ - count == 19 ¤Î¾ì¹ç + count == 19 の場合 t_len[0] t_len[1] 4 bit <----------> <----------> <------> @@ -6031,7 +6031,7 @@ c_len[i] == 0 | t_code[0] | t_code[1] |count-3| +------------+------------+-------+ - count >= 20 ¤Î¾ì¹ç + count >= 20 の場合 t_len[2] CBIT{9} <----------> <------> @@ -6039,7 +6039,7 @@ c_len[i] == 0 | t_code[2] |count-20| +------------+--------+ -c_len[i] > 0 ¤Î¾ì¹ç +c_len[i] > 0 の場合 t_len[c_len[i]+2] <-----------------> @@ -6047,46 +6047,46 @@ c_len[i] > 0 | t_code[c_len[i]+2]| +-------------------+ -c_len[n...NC] ¤Ï¡¢0 ¤È¤Ê¤ë¡£ +c_len[n...NC] は、0 となる。 ---------------------------------------------------------------------------- -c_len[] ¤ÎÃͤϤ¢¤ëÄøÅÙ 0 ¤¬Ï¢Â³¤¹¤ë¾ì¹ç¤¬Â¿¤¤¤³¤È¤¬´üÂԤǤ­¤ë¡£ -c_len[i]=0 ¤Î¾ì¹ç¤È¤¤¤¦¤Î¤Ï¤½¤Îʸ»ú(i)¤¬Ê¿Ê¸¤Ë¸½¤ì¤Ê¤¤¾ì¹ç¤ò¼¨¤¹¡£ -ASCII ¥Æ¥­¥¹¥È¥Õ¥¡¥¤¥ë¤Î°µ½Ì¤Ê¤é 0..127 ¤ÎÈϰϤΥ³¡¼¥É¤·¤«»È¤ï¤ì¤Ê¤¤¤¿ -¤á¤½¤ì°Ê³°¤Ï 0 ¤Ë¤Ê¤ë¤Ê¤É¤Ç¤¢¤ë¡£¤½¤·¤Æ c_len ¤òñ½ã¤Ë½ÐÎϤ¹¤ë¤³¤È¤ò¹Í -¤¨¤¿¤È¤­ c_len[i]=0 ¤Ç¤¢¤ë¾ðÊó¤ò¿¿ô½ÐÎϤ¹¤ë¤³¤È¤Ë¤Ê¤êÎΰ褬̵Â̤Ǥ¢¤ë -(c_len ¤Ï NC{255+256+2-3=510} ¸Ä¤ÎÍ×ÁǤò»ý¤Á¤½¤ÎÃæ¤Ç̤»ÈÍÑʸ»ú¤ä̤»ÈÍÑ -Ť¬Â¿¿ô¤¢¤ë¤³¤È¤òÁÛÄꤷ¤Æ¤¤¤ë)¡£¤½¤³¤Ç 0 ¤¬Ï¢Â³¤·¤Æ¸½¤ì¤ëÆÃħ¤òÀ¸¤«¤· - - o c_len[]=0 ¤¬Ï¢Â³¤Ç1¸Ä - o c_len ¤¬Ï¢Â³¤Ç 3¡Á18 ¸Ä - o c_len ¤¬Ï¢Â³¤Ç20¸Ä°Ê¾å(20¡ÁNC{510}) - -¤ò¤½¤ì¤¾¤ì°ì¤Ä¤Îʸ»ú¤È¸«¤Ê¤·¤Æ Huffman Éä¹æ²½¤¹¤ë¤³¤È¤Ç c_len ¼«ÂΤνР-ÎÏ¥µ¥¤¥º¤ò¾®¤µ¤¯¤·¤Æ¤¤¤ë¡£¤³¤ì¤Ï 0 ¤Î½Ð¸½ÉÑÅÙ¤òñ½ã¤Ë Huffman Éä¹æ²½¤¹ -¤ë¤è¤ê¤â¸ú²Ì¤¬´üÂԤǤ­¤ë¡£ - -¾å¿Þ¤Ç t_code ¤Ï c_len ¤ò Huffman Éä¹æ²½¤·¤¿¤È¤­¤ÎÉä¹æɽ¤ò¼¨¤·¤Æ¤ª¤ê - - o t_code[0] ... c_len[i] ¤Ï 0 ¤¬ 1 ¸Ä - o t_code[1] ... c_len[i] ¤Ï 0 ¤¬ 3¡Á18 ¸Ä(³¤¯4 ¥Ó¥Ã¥È¤Î¥Ó¥Ã¥ÈÎó¤Ç - ¸Ä¿ô¤¬¤ï¤«¤ë) - o t_code[2] ... c_len[i] ¤Ï 0 ¤¬ 20¡ÁNC-1¸Ä(³¤¯ CBIT ¥Ó¥Ã¥È¤Î¥Ó¥Ã¥È - Îó¤Ç¸Ä¿ô¤¬¤ï¤«¤ë) +c_len[] の値はある程度 0 が連続する場合が多いことが期待できる。 +c_len[i]=0 の場合というのはその文字(i)が平文に現れない場合を示す。 +ASCII テキストファイルの圧縮なら 0..127 の範囲のコードしか使われないた +めそれ以外は 0 になるなどである。そして c_len を単純に出力することを考 +えたとき c_len[i]=0 である情報を多数出力することになり領域が無駄である +(c_len は NC{255+256+2-3=510} 個の要素を持ちその中で未使用文字や未使用 +長が多数あることを想定している)。そこで 0 が連続して現れる特徴を生かし + + o c_len[]=0 が連続で1個 + o c_len が連続で 3〜18 個 + o c_len が連続で20個以上(20〜NC{510}) + +をそれぞれ一つの文字と見なして Huffman 符号化することで c_len 自体の出 +力サイズを小さくしている。これは 0 の出現頻度を単純に Huffman 符号化す +るよりも効果が期待できる。 + +上図で t_code は c_len を Huffman 符号化したときの符号表を示しており + + o t_code[0] ... c_len[i] は 0 が 1 個 + o t_code[1] ... c_len[i] は 0 が 3〜18 個(続く4 ビットのビット列で + 個数がわかる) + o t_code[2] ... c_len[i] は 0 が 20〜NC-1個(続く CBIT ビットのビット + 列で個数がわかる) o t_code[x] ... c_len[i]=x-2 (x>2) -¤ÈÉü¹æ¤¹¤ë¤³¤È¤Ë¤Ê¤ë¡£c_len[i] = 0 ¤¬ 2 ¸Ä¤¢¤ë¤¤¤Ï 19 ¸Ä³¤¯¾ì¹ç¤Ï +と復号することになる。c_len[i] = 0 が 2 個あるいは 19 個続く場合は - t_code[0] ¤¬ 2 ¸Ä - t_code[0] ¤È t_code[1] ¤¬ 1 ¸Ä¤º¤Ä + t_code[0] が 2 個 + t_code[0] と t_code[1] が 1 個ずつ -¤Ç½ÐÎϤµ¤ì¤Æ¤¤¤ë¡£ +で出力されている。 -ºÇ¸å¤Ë¡¢t_len[] ¤Î½ÐÎÏ¥Õ¥©¡¼¥Þ¥Ã¥È¤ò¼¨¤¹¡£ +最後に、t_len[] の出力フォーマットを示す。 ---------------------------------------------------------------------------- -< t_len[] ¤Î½ÐÎÏ¥Õ¥©¡¼¥Þ¥Ã¥È > +< t_len[] の出力フォーマット > 2 bit 0 TBIT{5} |--| @@ -6094,36 +6094,36 @@ ASCII | n | t_len[0] | t_len[1] | t_len[2] | x|t_len[x+3]| ... | t_len[n-1]| +-------+----------+----------+----------+--+----------+- -+-----------+ -t_len[i] <= 6 ¤Î¾ì¹ç +t_len[i] <= 6 の場合 0 3bit +-----+ t_len[i] | | | | +-----+ -t_len[i] >= 7 ¤Î¾ì¹ç +t_len[i] >= 7 の場合 0 t_len[i] - 3 +----------------+ t_len[i] |1 1 1 1 ... 1 0 | +----------------+ -t_len[2] ¤Îľ¸å¤Ï 2 bit ¤Î¾ðÊó¤¬Éղ䵤ì¤ë¡£¤³¤ÎÃͤò x{0..3} ¤È¤¹¤ë¤È¡¢ -t_len[3 .. x+2] ¤ÎÈÏ°Ï¤Ç 0 ¤¬Â³¤¯¤³¤È¤ò°ÕÌ£¤·¡¢¤³¤Î 2 bit °Ê¹ß¤Ï¡¢ -t_len[x+3] ¤¬Â³¤¯¤³¤È¤Ë¤Ê¤ë¡£x ¤¬ 0 ¤Î¾ì¹ç¤Ï¡¢t_len[3] ¤Ï 0 ¤Ç¤Ï¤Ê¤¤¡£ +t_len[2] の直後は 2 bit の情報が付加される。この値を x{0..3} とすると、 +t_len[3 .. x+2] の範囲で 0 が続くことを意味し、この 2 bit 以降は、 +t_len[x+3] が続くことになる。x が 0 の場合は、t_len[3] は 0 ではない。 -t_len[n...NT] ¤Ï¡¢0 ¤È¤Ê¤ë¡£ +t_len[n...NT] は、0 となる。 ---------------------------------------------------------------------------- -´ðËÜŪ¤Ê¹Í¤¨¤Ï p_len[] ¤Î¾ì¹ç¤ÈƱ¤¸¤Ç¤¢¤ë(t_len[] ¤ÎÍ×ÁÇ¿ô NT ¤Ï 19)¡£ -¤¿¤À¤· t_len[3..5] ¤Ë¤Ä¤¤¤ÆÆÃÊÌ°·¤¤¤µ¤ì¤Æ¤¤¤ë¡£ +基本的な考えは p_len[] の場合と同じである(t_len[] の要素数 NT は 19)。 +ただし t_len[3..5] について特別扱いされている。 -¤Þ¤º¡¢t_len[] ¤È c_len[x] ¤ÎÃͤÎÂбþ¤ò°Ê²¼¤ËÀ°Íý¤·Ä¾¤¹¡£ +まず、t_len[] と c_len[x] の値の対応を以下に整理し直す。 - t_len[0] c_len[x]=0 1 ¤Ä¤ò 1 ʸ»ú¤È¤ß¤Ê¤¹ - t_len[1] c_len[x]=0 ¤¬ 3¡Á18 ¸ÄϢ³¤·¤Æ¤¤¤ë²ô¤ò 1 ʸ»ú¤È¤ß¤Ê¤¹ - t_len[2] c_len[x]=0 ¤¬ 20¡ÁNC{510} ¸ÄϢ³¤·¤Æ¤¤¤ë²ô¤ò 1 ʸ»ú¤È¤ß¤Ê¤¹ + t_len[0] c_len[x]=0 1 つを 1 文字とみなす + t_len[1] c_len[x]=0 が 3〜18 個連続している塊を 1 文字とみなす + t_len[2] c_len[x]=0 が 20〜NC{510} 個連続している塊を 1 文字とみなす t_len[3] c_len[x]=1 t_len[4] c_len[x]=2 t_len[5] c_len[x]=3 @@ -6131,15 +6131,15 @@ t_len[n...NT] : t_len[18] c_len[x]=16 -t_len[3..5] ¤ÎÆÃÊÌ°·¤¤¤Ë¤Ä¤¤¤Æ¹Í¤¨¤ë¤È t_len[3..5] ¤ÎÈÏ°Ï¤Ç 0 ¤¬Ï¢Â³¤¹ -¤ë¾ì¹ç¤Ë¡¢2 ¥Ó¥Ã¥È¤Ç¤½¤Î¤³¤È¤òɽ¤·¤Æ¤¤¤ë¡£¤³¤ì¤Ï¤Ä¤Þ¤ê¤³¤Î¤è¤¦¤Ê¾ì¹ç¤¬ -¿¤¤¤Î¤Ç¤¢¤í¤¦¡£¾å¤ÇÂбþ´Ø·¸¤ò¼¨¤·¤¿¤È¤ª¤ê¡¢t_len[3..5] ¤¬ 0 ¤Ç¤¢¤ë¾ì¹ç -¤È¤¤¤¦¤Î¤Ï¡¢¤Ä¤Þ¤ê¥Ó¥Ã¥ÈĹ c_len[x] ¤¬ 1..3 ¤ÎÈϰϤÎÃͤò»ý¤¿¤Ê¤¤¾ì¹ç¤ò -¼¨¤¹¡£ +t_len[3..5] の特別扱いについて考えると t_len[3..5] の範囲で 0 が連続す +る場合に、2 ビットでそのことを表している。これはつまりこのような場合が +多いのであろう。上で対応関係を示したとおり、t_len[3..5] が 0 である場合 +というのは、つまりビット長 c_len[x] が 1..3 の範囲の値を持たない場合を +示す。 -# c_len[x] ¤¬ 1..3 ¤ÎÃͤò»ý¤Ä¾ì¹ç¤È¤¤¤¦¤Î¤Ï Huffman Ìڤˤª¤¤¤Æ¤¢¤ë 3 ʸ»ú -# ¤Î½Ð¸½ÉÑÅÙ¤¬¶Ëü¤Ë¿¤¤¾ì¹ç¤ò¼¨¤¹¡£¤³¤Î¤è¤¦¤Ê¾ì¹ç¤Ï¤¢¤Þ¤ê¤Ê¤¤¤Ç¤¢¤ë¤ÈÁÛ -# Äꤷ¤Æ¤¤¤ë¤Î¤À¤í¤¦¡£ +# c_len[x] が 1..3 の値を持つ場合というのは Huffman 木においてある 3 文字 +# の出現頻度が極端に多い場合を示す。このような場合はあまりないであると想 +# 定しているのだろう。 # # . # / \ @@ -6152,93 +6152,93 @@ t_len[3..5] # . . # / \ / \ -°Ê¾å¤Ç LHA ¥Õ¥¡¥¤¥ë¤Î¹½Â¤¤Ë¤Ä¤¤¤Æ¤Ò¤È¤È¤ª¤êÀâÌÀ¤·¤¿¤³¤È¤Ë¤Ê¤ë¡£¤¿¤À¤·¡¢ -Éü¹æ½èÍý¤ò¹Í¤¨¤ë¾ì¹ç¤ËÃí°Õ»ö¹à¤¬¤¢¤ë¡£¤³¤ì¤Ï¤³¤Î¸å¤ÇÀâÌÀ¤·¤è¤¦¡£ +以上で LHA ファイルの構造についてひととおり説明したことになる。ただし、 +復号処理を考える場合に注意事項がある。これはこの後で説明しよう。 -¥»¥­¥å¥ê¥Æ¥£¥Ð¥°¤Î¹Í»¡ +セキュリティバグの考察 ---------------------- -2006ǯ8·î LHA ¤ÎÉü¹æ½èÍý¤Ë¥»¥­¥å¥ê¥Æ¥£¥Ð¥°(CVE-2006-4335,4337,4338)¤¬È¯ -¸«¤µ¤ì¤¿¡£¤³¤ÎÌäÂê¤Ï LHA ¤Î¼ÂÁõ¤Ë¤ª¤¤¤ÆÉä¹æ²½¤Ë¤Ä¤¤¤Æ¤ÏÅöÁ³¤¢¤é¤æ¤ëÆþÎÏ -¥Õ¥¡¥¤¥ë¤òÁÛÄꤷ¤¿½èÍý¤È¤Ê¤Ã¤Æ¤¤¤ë¤¬Éü¹æ¤Ë¤Ä¤¤¤Æ¤Ï°µ½Ì¥Õ¥¡¥¤¥ë¤¬Àµ¤·¤¤ -¹½Â¤¡¢ÃͤǺîÀ®¤µ¤ì¤Æ¤¤¤ë¤³¤È¤·¤«ÁÛÄꤻ¤º¤Ë½èÍý¤¬ºî¤é¤ì¤Æ¤¤¤¿¤¿¤á¤Ç¤¢¤ë¡£ -¤Ä¤Þ¤ê¡¢ÉÔÀµ¤Ê°µ½Ì¥Õ¥¡¥¤¥ë¤¬Í¿¤¨¤é¤ì¤¿¾ì¹ç¤ÎÆ°ºî¤¬ÉÔÄê¤À¤Ã¤¿¤Î¤À¡£ +2006å¹´8月 LHA の復号処理にセキュリティバグ(CVE-2006-4335,4337,4338)が発 +見された。この問題は LHA の実装において符号化については当然あらゆる入力 +ファイルを想定した処理となっているが復号については圧縮ファイルが正しい +構造、値で作成されていることしか想定せずに処理が作られていたためである。 +つまり、不正な圧縮ファイルが与えられた場合の動作が不定だったのだ。 -¤³¤³¤Ç¤Ï¡¢LHA ¤ÎÉü¹æ¤Ë¤ª¤¤¤ÆÃí°Õ¤¹¤Ù¤­ÅÀ¤Ë¤Ä¤¤¤Æ¹Í»¡¤¹¤ë¡£¤Þ¤¿¡¢¤³¤³¤Þ -¤Ç¤Ë²òÆɤ·¤¿ LHa for UNIX ver.1.14i ¤Î¥½¡¼¥¹¤Ï¤³¤Î¥»¥­¥å¥ê¥Æ¥£¥Ð¥°¤¬ -»Ä¤Ã¤Æ¤¤¤¿¤â¤Î¤Ê¤Î¤Ç¡¢¥»¥­¥å¥ê¥Æ¥£¥Ð¥°¤ÎÂкö¤ò¹Ô¤Ã¤¿¥½¡¼¥¹¤Ë¤Ä¤¤¤Æ¤â¸å -¤Ç²òÀϤò¹Ô¤¦¤³¤È¤È¤¹¤ë¡£ +ここでは、LHA の復号において注意すべき点について考察する。また、ここま +でに解読した LHa for UNIX ver.1.14i のソースはこのセキュリティバグが +残っていたものなので、セキュリティバグの対策を行ったソースについても後 +で解析を行うこととする。 -°Ê²¼¡¢LHA ¤Î¹½Â¤¤òºÆ·Ç¤·¡¢³ÆÉü¹æ½èÍý¤ÇÃí°Õ¤¹¤Ù¤­ÅÀ¤ò³Îǧ¤·¤è¤¦¡£ -°Ê¹ß¤ÎÀâÌÀ¤Ç¤ÏÃí°ÕÅÀËè¤Ë (1) (2) ¤Î¤è¤¦¤ËÈÖ¹æ¤ò¿¶¤Ã¤Æ¤¤¤ë¡£ºÇ½ªÅª¤Ë -Á´¥Á¥§¥Ã¥¯¥Ý¥¤¥ó¥È¤ò¥Á¥§¥Ã¥¯¤¹¤ë¤è¤¦¤Ë½¤Àµ¤·¤¿¥½¡¼¥¹¤òºÜ¤»¡¢¤³¤ÎÈÖ¹æ¤Ç -ɳÉÕ¤±¤ò¹Ô¤¦¤³¤È¤È¤¹¤ë¡£ +以下、LHA の構造を再掲し、各復号処理で注意すべき点を確認しよう。 +以降の説明では注意点毎に (1) (2) のように番号を振っている。最終的に +全チェックポイントをチェックするように修正したソースを載せ、この番号で +紐付けを行うこととする。 ---------------------------------------------------------------------------- -< LHA ¥Õ¥¡¥¤¥ë¤Î¹½Â¤(1 ¥Ö¥í¥Ã¥¯Ê¬) > +< LHA ファイルの構造(1 ブロック分) > +-----------+ | blocksize | +-----------+ 16bit - ¥Ï¥Õ¥Þ¥óÌÚ + ハフマン木 +-----------+-----------+-----------+ | t_len | c_len | p_len | +-----------+-----------+-----------+ - °µ½Ìʸ(ʸ»ú¤ÈŤµ¡¢°ÌÃ֤ΥӥåÈĹ¤Î¥Ï¥Õ¥Þ¥óÉä¹æ¤È°ÌÃÖ¤ÎÃÍ) + 圧縮文(文字と長さ、位置のビット長のハフマン符号と位置の値) +---------------------+ - | °µ½Ìʸ | + | 圧縮文 | +---------------------+ ---------------------------------------------------------------------------- (1) -blocksize ¤ÎÆɤ߹þ¤ß¤Ë¤Ä¤¤¤Æ¤³¤ÎÃÍ¤Ï 1¡Á0xffff ¤Ë¤Ä¤¤¤Æ¤ÏÀµ¤·¤¤¤¬ 0 ¤Ë -¤Ê¤ë¤³¤È¤Ï¤¢¤êÆÀ¤Ê¤¤¤Î¤Ç 0 ¤Î¾ì¹ç¤ËÉÔÀµ¤ÈȽÃǤ·¤Æ¤â¤è¤¤¤È»×¤ï¤ì¤ë¡£ +blocksize の読み込みについてこの値は 1〜0xffff については正しいが 0 に +なることはあり得ないので 0 の場合に不正と判断してもよいと思われる。 (2) -¡Ö°µ½Ìʸ¡×¼«ÂΤˤĤ¤¤Æ¤Ï¡¢blocksize ¿ô¤òÍê¤ê¤ËÆɤ߹þ¤à¤Î¤Ç blocksize ¤ò -±Û¤¨¤Æ°µ½Ìʸ¤¬Â¸ºß¤·¤Æ¤â¼¡¤Î block ¤È¤·¤ÆÆɤޤì¤ë¤À¤±¤Ç¤¢¤ë¡£blocksize -¤ËËþ¤¿¤Ê¤¤¾ì¹ç¤Ï¡¢EOF ¤ò¸¡ÃΤ·¤ÆÁá´ü¤ËÉÔÀµ¤ÈȽÃǤ¹¤ë¤è¤¦¤Ë½èÍý¤·¤¿Êý¤¬ -¤è¤¤¤À¤í¤¦¡£ +「圧縮文」自体については、blocksize 数を頼りに読み込むので blocksize を +越えて圧縮文が存在しても次の block として読まれるだけである。blocksize +に満たない場合は、EOF を検知して早期に不正と判断するように処理した方が +よいだろう。 -°µ½ÌʸÃæ¤Îʸ»ú¤ÈŤµ¤Ë¤Ä¤¤¤Æ¤Ï +圧縮文中の文字と長さについては - Éü¹æ¤·¤¿1ʸ»ú >= 256 ¤Î¾ì¹ç - Ťµ¤ò¼¨¤¹¡£(¤½¤·¤Æ¤½¤Îľ¸å¤Ë°ÌÃ֤ΠHuffman Éä¹æ¤¬¤¢¤ë) + 復号した1文字 >= 256 の場合 + 長さを示す。(そしてその直後に位置の Huffman 符号がある) - Éü¹æ¤·¤¿1ʸ»ú < 256 ¤Î¾ì¹ç - ʸ»ú¤ò¼¨¤¹¡£ + 復号した1文字 < 256 の場合 + 文字を示す。 -¤Ç¤¢¤ê¡¢Ê¸»ú¤È¤·¤Æ¤Ï 0 .. 255 ¤¹¤Ù¤Æ¤Ë¤Ä¤¤¤ÆÀµ¤·¤¤ÃͤʤΤÇÌäÂê¤Ï¤Ê¤¤¡£ +であり、文字としては 0 .. 255 すべてについて正しい値なので問題はない。 (3) -Ťµ¤Ï 256 ... NC{256+maxmatch-3+1} ¤ÎÈϰϤÎÃͤò¼è¤ë¤Î¤Ç¤³¤ì¤òĶ¤¨¤ëÃÍ -¤òÊÖ¤¹¾ì¹ç¤ÏÉÔÀµ¤ÈȽÃǤ·¤Æ¤â¤è¤¤¡£¤¿¤À¤·¡¢¤³¤ÎȽÄ꼫ÂÎ¤Ï c_len ¤òÆɤ߹þ -¤ß Huffman ÌÚ¤ò¹½ÃÛ¤¹¤ë¤È¤­¤Ë¹Ô¤¦¤³¤È¤â¤Ç¤­¤ë¡£(¼ÂºÝ¡¢¼ÂÁõ¤Ç¤Ï -Huffman Ìڤˤ³¤ÎÈÏ°ÏÆâ¤ÎÉü¹æ¸ì¤·¤«³ä¤êÅö¤Æ¤Ê¤¤¤Î¤Ç¥Ð¥°¤Ç¤Ê¤¤¸Â¤ê¤ÏȯÀ¸ -¤·¤Ê¤¤¤À¤í¤¦) +長さは 256 ... NC{256+maxmatch-3+1} の範囲の値を取るのでこれを超える値 +を返す場合は不正と判断してもよい。ただし、この判定自体は c_len を読み込 +み Huffman 木を構築するときに行うこともできる。(実際、実装では +Huffman 木にこの範囲内の復号語しか割り当てないのでバグでない限りは発生 +しないだろう) -°ÌÃ֤ˤĤ¤¤Æ¤Ï²¼¿Þ¤ÎÄ̤ê°ÌÃ֤ΥӥåÈŤΠHuffman Éä¹æ¤È°ÌÃÖ¤ÎÃͤ¬½ñ¤«¤ì -¤Æ¤¤¤ë¡£ +位置については下図の通り位置のビット長の Huffman 符号と位置の値が書かれ +ている。 +------------------------------+----------+ - | °ÌÃ֤ΥӥåÈŤΠHuffman Éä¹æ| °ÌÃÖ¤ÎÃÍ | + | 位置のビット長の Huffman 符号| 位置の値 | +------------------------------+----------+ (4) -°ÌÃÖ¤ÎÃͤȤ·¤Æ¤Ï 0 ... 2^dicbit ¤ÎÈϰϤÎÃͤò»ý¤Ä¤Î¤Ç Huffman Éä¹æ¤ÎÉü¹æ -·ë²Ì¤¬ 0 ... np{dicbit+1} ¤ÎÈϰϤǤ¢¤ì¤Ð°ÌÃÖ¤ÎÃÍÉôʬ¤Ë¤Ä¤¤¤Æ¥Á¥§¥Ã¥¯¤¹ -¤ëɬÍפϤʤ¤¡£½¾¤Ã¤Æ¡¢c_len ¤ÈƱ¤¸¤¯¥Ï¥Õ¥Þ¥óÌڤι½ÃÛ¤ÎÃʳ¬¤ÇÉÔÀµ¤ÊÉü¹æ -¸ì¤òÊÖ¤µ¤Ê¤¤¤è¤¦¤Ë¤·¤Æ¤¤¤ì¤Ð¤è¤¤¡£ +位置の値としては 0 ... 2^dicbit の範囲の値を持つので Huffman 符号の復号 +結果が 0 ... np{dicbit+1} の範囲であれば位置の値部分についてチェックす +る必要はない。従って、c_len と同じくハフマン木の構築の段階で不正な復号 +語を返さないようにしていればよい。 -¤Ç¤Ï¡¢t_len ¤Ë¤Ä¤¤¤Æ¸«¤Æ¤ß¤ë¡£ +では、t_len について見てみる。 ---------------------------------------------------------------------------- -< t_len[] ¤Î½ÐÎÏ¥Õ¥©¡¼¥Þ¥Ã¥È > +< t_len[] の出力フォーマット > 2 bit 0 TBIT{5} |--| @@ -6246,37 +6246,37 @@ Huffman | n | t_len[0] | t_len[1] | t_len[2] | x|t_len[x+3]| ... | t_len[n-1]| +-------+----------+----------+----------+--+----------+- -+-----------+ -t_len[i] <= 6 ¤Î¾ì¹ç +t_len[i] <= 6 の場合 0 3bit +-----+ t_len[i] | | | | +-----+ -t_len[i] >= 7 ¤Î¾ì¹ç +t_len[i] >= 7 の場合 0 t_len[i] - 3 +----------------+ t_len[i] |1 1 1 1 ... 1 0 | +----------------+ -t_len[2] ¤Îľ¸å¤Ï 2 bit ¤Î¾ðÊó¤¬Éղ䵤ì¤ë¡£¤³¤ÎÃͤò x{0..3} ¤È¤¹¤ë¤È¡¢ -t_len[3 .. x+2] ¤ÎÈÏ°Ï¤Ç 0 ¤¬Â³¤¯¤³¤È¤ò°ÕÌ£¤·¡¢¤³¤Î 2 bit °Ê¹ß¤Ï¡¢ -t_len[x+3] ¤¬Â³¤¯¤³¤È¤Ë¤Ê¤ë¡£x ¤¬ 0 ¤Î¾ì¹ç¤Ï¡¢t_len[3] ¤Ï 0 ¤Ç¤Ï¤Ê¤¤¡£ +t_len[2] の直後は 2 bit の情報が付加される。この値を x{0..3} とすると、 +t_len[3 .. x+2] の範囲で 0 が続くことを意味し、この 2 bit 以降は、 +t_len[x+3] が続くことになる。x が 0 の場合は、t_len[3] は 0 ではない。 -t_len[n...NT] ¤Ï¡¢0 ¤È¤Ê¤ë¡£ +t_len[n...NT] は、0 となる。 ---------------------------------------------------------------------------- (5) -t_len ¤Î½ÐÎÏ¥Õ¥©¡¼¥Þ¥Ã¥È¤ÎÀèƬ TBIT{5} ¤Ï 0 ... 2^5{32} ¤ÎÈϰϤÎÃͤò³Ê -Ǽ¤Ç¤­¤ë¤¬ t_len ¤ÎÎΰ襵¥¤¥º¤Ï NT{19} ¤Ê¤Î¤Ç 0..19 ¤ÎÈϰϤòĶ¤¨¤ë¾ì¹ç -¤ÏÉÔÀµ¤ÈȽÃǤ·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£ +t_len の出力フォーマットの先頭 TBIT{5} は 0 ... 2^5{32} の範囲の値を格 +納できるが t_len の領域サイズは NT{19} なので 0..19 の範囲を超える場合 +は不正と判断しなければならない。 (6) -¤Þ¤¿¡¢t_len[i] >= 7 ¤Î¾ì¹ç¤Î·Á¼°¤Ï bit 0 ¤ò¸¡½Ð¤¹¤ë¤Þ¤Ç¤Î¥Ó¥Ã¥ÈŤ¬ÃÍ¤È -¤Ê¤ë¤¬ t_len[i] ¤ÎÃͤϥϥեޥóÉä¹æĹ¤Ê¤Î¤Ç 0 .. 16 ¤ÎÈϰϤǤʤ±¤ì¤Ð¤Ê¤é -¤Ê¤¤¡£t_len[i] >= 7 ¤Î·Á¼°¤Î¶ñÂÎŪ¤ÊÃͤÎÂбþ¤Ï +また、t_len[i] >= 7 の場合の形式は bit 0 を検出するまでのビット長が値と +なるが t_len[i] の値はハフマン符号長なので 0 .. 16 の範囲でなければなら +ない。t_len[i] >= 7 の形式の具体的な値の対応は 7: 1110 8: 1111 0 @@ -6284,42 +6284,42 @@ t_len 15: 1111 1111 1110 16: 1111 1111 1111 0 -¤È¤Ê¤Ã¤Æ¤¤¤ë¤Î¤Ç¡¢16 ¤Î¾ì¹ç¤Î¥Ó¥Ã¥ÈĹ(1 ¤¬ 12 bit ³¤¯)¤è¤ê¥Ó¥Ã¥ÈŤ¬Ä¹ -¤¤¾ì¹ç¤ÏÉÔÀµ¤Ç¤¢¤ë¡£(ºÇÂç 12 ¥Ó¥Ã¥È¤Þ¤Ç¤·¤«¸«¤Ê¤¤¤È¤¹¤ë¤³¤È¤â¹Í¤¨¤é¤ì¤ë¤¬¡¢ -LHA ¤Î°µ½Ì½èÍý¤Ï¾å¤ÎÎã¤Î¤è¤¦¤Ë 16 ¤Î¾ì¹ç¤Ç¤â¥Ó¥Ã¥È 0 ¤ò½ÐÎϤ¹¤ë¤Î¤Ç¡¢¤½ -¤Î¤è¤¦¤ÊÆɤßÊý¤ò¤¹¤ë¤ÈÀµ¾ï¤Ê°µ½Ìʸ¤òÉü¹æ¤Ç¤­¤Ê¤¯¤Ê¤ë¡£) +となっているので、16 の場合のビット長(1 が 12 bit 続く)よりビット長が長 +い場合は不正である。(最大 12 ビットまでしか見ないとすることも考えられるが、 +LHA の圧縮処理は上の例のように 16 の場合でもビット 0 を出力するので、そ +のような読み方をすると正常な圧縮文を復号できなくなる。) (7) -¤µ¤é¤Ë¡¢t_len ¤òÆɤ߹þ¤ó¤À¸å¤Ë¹½ÃÛ¤·¤¿ Huffman ÌÚ¤Ï Huffman ÌڤȤ·¤ÆÀ°¹ç -À­¤¬Êݤ¿¤ì¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£ -¤¿¤È¤¨¤Ð¡¢LHA ¤Ë¤ª¤±¤ë Huffman Ìڤϰʲ¼¤ÎÀ­¼Á¤¬¼é¤é¤ì¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¤Ï -¤º¤À¡£ +さらに、t_len を読み込んだ後に構築した Huffman 木は Huffman 木として整合 +性が保たれなければならない。 +たとえば、LHA における Huffman 木は以下の性質が守られなければならないは +ずだ。 - o t_len[x] <= 16 (LHA ¤Î Huffman Ìڤγ¬ÁØ¤Ï 16 ¤Þ¤Ç¤Ç¤¢¤ë) + o t_len[x] <= 16 (LHA の Huffman 木の階層は 16 までである) - o ³Æ³¬ÁؤÎÍդοô¤ÏÀ°¹çÀ­¤¬Êݤ¿¤ì¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£Î㤨¤Ð¡¢1 ³¬ÁØÌܤΠ- Íդοô¤ÏºÇÂç 2 ¤Ç¤¢¤ê¡¢¤³¤Î¤È¤­²¼°Ì¤Î³¬ÁؤÎÍդοô¤Ï 0 ¤Ç¤¢¤ë¡£³ÆÀá - ¤Ïɬ¤ºÀᤫÍÕ¤ò»ý¤Ä¡£¤Ê¤É¡£ + o 各階層の葉の数は整合性が保たれなければならない。例えば、1 階層目の + 葉の数は最大 2 であり、このとき下位の階層の葉の数は 0 である。各節 + は必ず節か葉を持つ。など。 -¸å¤Ç½èÍý¤ò²òÀϤ¹¤ëºÝ¤Ë¤³¤ÎÊÕ¤ê¤ò³Îǧ¤·¤è¤¦¡£ -¤Ê¤ª¡¢1 ÅÀÌܤˤĤ¤¤Æ¤ÏÁ°½Ò¤ÎÄ̤ê t_len ¤ÎÆɤ߹þ¤ß»þ¤Ë¥Á¥§¥Ã¥¯¤Ç¤­¤ë¡£ -2 ÅÀÌܤˤĤ¤¤Æ¤Ï¼ÂÁõ¤Ç¤ÏÈó¾ï¤Ë¤¦¤Þ¤¤ÊýË¡¤Ç¥Á¥§¥Ã¥¯¤·¤Æ¤¤¤ë¡£ +後で処理を解析する際にこの辺りを確認しよう。 +なお、1 点目については前述の通り t_len の読み込み時にチェックできる。 +2 点目については実装では非常にうまい方法でチェックしている。 -³¤¤¤Æ c_len[] ¤Î½ÐÎÏ¥Õ¥©¡¼¥Þ¥Ã¥È¤Ë¤Ä¤¤¤Æ¹Í¤¨¤ë¡£ +続いて c_len[] の出力フォーマットについて考える。 ---------------------------------------------------------------------------- -< c_len[] ¤Î½ÐÎÏ¥Õ¥©¡¼¥Þ¥Ã¥È > +< c_len[] の出力フォーマット > 0 CBIT{9} +-------+-----------+-----------+-- --+-----------+ | n | c_len[0] | c_len[1] | ... c_len[n-1]| +-------+-----------+-----------+-- --+-----------+ -c_len[i] == 0 ¤Î¾ì¹ç +c_len[i] == 0 の場合 - 0 ¤¬Â³¤¯¿ô¤ò count ¤È¤¹¤ë¤È¡¢ + 0 が続く数を count とすると、 - count == 1 ¤Î¾ì¹ç + count == 1 の場合 t_len[0] <----------> @@ -6327,7 +6327,7 @@ c_len[i] == 0 | t_code[0] | +------------+ - count == 2 ¤Î¾ì¹ç + count == 2 の場合 t_len[0] t_len[0] <----------> <----------> @@ -6335,7 +6335,7 @@ c_len[i] == 0 | t_code[0] | t_code[0] | +------------+------------+ - count == 3..18 ¤Î¾ì¹ç + count == 3..18 の場合 t_len[1] 4 bit <----------> <------> @@ -6343,7 +6343,7 @@ c_len[i] == 0 | t_code[1] |count-3| +------------+-------+ - count == 19 ¤Î¾ì¹ç + count == 19 の場合 t_len[0] t_len[1] 4 bit <----------> <----------> <------> @@ -6351,7 +6351,7 @@ c_len[i] == 0 | t_code[0] | t_code[1] |count-3| +------------+------------+-------+ - count >= 20 ¤Î¾ì¹ç + count >= 20 の場合 t_len[2] CBIT{9} <----------> <------> @@ -6359,7 +6359,7 @@ c_len[i] == 0 | t_code[2] |count-20| +------------+--------+ -c_len[i] > 0 ¤Î¾ì¹ç +c_len[i] > 0 の場合 t_len[c_len[i]+2] <-----------------> @@ -6367,76 +6367,76 @@ c_len[i] > 0 | t_code[c_len[i]+2]| +-------------------+ -c_len[n...NC] ¤Ï¡¢0 ¤È¤Ê¤ë¡£ +c_len[n...NC] は、0 となる。 ---------------------------------------------------------------------------- (8) -c_len ¤Î½ÐÎÏ¥Õ¥©¡¼¥Þ¥Ã¥È¤ÎÀèƬ CBIT{9} ¤Ï 0 ... 2^9(512) ¤ÎÈϰϤÎÃͤò -³ÊǼ¤Ç¤­¤ë¤¬ c_len ¤ÎÎΰ襵¥¤¥º¤Ï NC{510} ¤Ê¤Î¤Ç 0..510 ¤ÎÈϰϤòĶ¤¨¤ë -¾ì¹ç¤Ï ÉÔÀµ¤ÈȽÃǤ·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£ +c_len の出力フォーマットの先頭 CBIT{9} は 0 ... 2^9(512) の範囲の値を +格納できるが c_len の領域サイズは NC{510} なので 0..510 の範囲を超える +場合は 不正と判断しなければならない。 (9) -¤Þ¤¿¡¢ - count >= 20 ¤Î¾ì¹ç - count == 3..18 ¤Î¾ì¹ç +また、 + count >= 20 の場合 + count == 3..18 の場合 -¤Î¤½¤ì¤¾¤ì¤Î·Á¼°¤Ë¤ª¤¤¤Æ count ¤Î¿ô¤À¤± c_len[i] ¤Ï 0 ¤¬Â³¤¯¤¬¤³¤ì¤¬ -c_len ¤Î*»Ä¤ê*¥µ¥¤¥º¤ò±Û¤¨¤ë¾ì¹ç¤âÉÔÀµ¤Ç¤¢¤ë¡£ +のそれぞれの形式において count の数だけ c_len[i] は 0 が続くがこれが +c_len の*残り*サイズを越える場合も不正である。 (10) -¤µ¤é¤Ë¡¢c_len[i] > 0 ¤Î¾ì¹ç¤Î·Á¼°¤Ë¤ª¤¤¤Æ¡¢t_len[x] ¤òÉü¹æ¤·¤¿·ë²Ì¤¬ -c_len[i]{x}+2 ¤ÎÃͤǤ¢¤êc_len[i] ¤ÎÃͤϥϥեޥóÉä¹æĹ¤Ê¤Î¤Ç 0 .. 16 ¤Î -ÈϰϤǤʤ±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£¤³¤ì¤Ï¡¢c_len, p_len ¤ÈƱ¤¸¤¯ t_len ¤Î¥Ï¥Õ¥Þ¥ó -Ìڤι½ÃÛ¤ÎÃʳ¬¤ÇÉÔÀµ¤ÊÉü¹æ¸ì¤òÊÖ¤µ¤Ê¤¤¤è¤¦¤Ë¤·¤Æ¤¤¤ì¤Ð¤è¤¤¡£ +さらに、c_len[i] > 0 の場合の形式において、t_len[x] を復号した結果が +c_len[i]{x}+2 の値でありc_len[i] の値はハフマン符号長なので 0 .. 16 の +範囲でなければならない。これは、c_len, p_len と同じく t_len のハフマン +木の構築の段階で不正な復号語を返さないようにしていればよい。 (11) -¤â¤Á¤í¤ó¡¢t_len ¤Î¤È¤­¤ÈƱÍÍ c_len ¤òÆɤ߹þ¤ó¤À¸å¤Ë¹½ÃÛ¤·¤¿ Huffman ÌÚ -¤Ï Huffman ÌڤȤ·¤ÆÀ°¹çÀ­¤¬Êݤ¿¤ì¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£ +もちろん、t_len のときと同様 c_len を読み込んだ後に構築した Huffman 木 +は Huffman 木として整合性が保たれなければならない。 -³¤¤¤Æ p_len[] ¤Î½ÐÎÏ¥Õ¥©¡¼¥Þ¥Ã¥È¤Ë¤Ä¤¤¤Æ¹Í¤¨¤ë¡£ +続いて p_len[] の出力フォーマットについて考える。 ---------------------------------------------------------------------------- -< p_len[] ¤Î½ÐÎÏ¥Õ¥©¡¼¥Þ¥Ã¥È > +< p_len[] の出力フォーマット > 0 pbit{4 or 5} +-------+-----------+-----------+-- --+-----------+ | n | p_len[0] | p_len[1] | ... p_len[n-1]| +-------+-----------+-----------+-- --+-----------+ -p_len[i] <= 6 ¤Î¾ì¹ç +p_len[i] <= 6 の場合 0 3bit +-----+ p_len[i] | | | | +-----+ -p_len[i] >= 7 ¤Î¾ì¹ç +p_len[i] >= 7 の場合 0 p_len[i] - 3 +----------------+ p_len[i] |1 1 1 1 ... 1 0 | +----------------+ -p_len[n...np] ¤Ï¡¢0 ¤È¤Ê¤ë¡£ +p_len[n...np] は、0 となる。 ---------------------------------------------------------------------------- (12) -p_len ¤Î½ÐÎÏ¥Õ¥©¡¼¥Þ¥Ã¥È¤ÎÀèƬ pbit{4 or 5} ¤Ï 0 ... 2^4{16} or -2^5{32} ¤ÎÈϰϤÎÃͤò³ÊǼ¤Ç¤­¤ë¤¬ p_len ¤ÎÎΰ襵¥¤¥º¤Ï np{14..17} ¤Ê¤Î¤Ç -0..np ¤ÎÈϰϤòĶ¤¨¤ë¾ì¹ç¤ÏÉÔÀµ¤ÈȽÃǤ·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£ - -¤È¤³¤í¤ÇÉü½¬¤Ë¤Ê¤ë¤¬ np ¤Ï¡¢³Æ°µ½Ì¥á¥½¥Ã¥É¤Î¼­½ñ¤Î¥µ¥¤¥º¤Ç·è¤Þ¤ë¡£Âбþ -¤ò°Ê²¼¤ËºÆ·Ç¤¹¤ë¤Î¤Ç³Îǧ¤·¤Æ¤Û¤·¤¤¡£(-lh4- ¤Î¾ì¹ç¤Î np ¤Ï¤Ê¤¼¤« 13 ¤Ç¤Ï -¤Ê¤¯14 ¤È¤Ê¤Ã¤Æ¤¤¤ë¡£¤ª¤½¤é¤¯¡¢LHA ¤¬¼ÂÁõ¤µ¤ì¤¿Åö»þ -lh6-, -lh7- ¤Ï¸ºß -¤»¤º np ¤ä pbit ¤Ï¸ÇÄêÃÍ(Äê¿ô NP, PBIT)¤Ç¤¢¤Ã¤¿¤¿¤á¡¢-lh4-, -lh5- ¤Îξ -Êý¤ËÂбþ¤Ç¤­¤ë¤è¤¦ÊÑ¿ô¤ÎÎΰ襵¥¤¥º¤ò¹ç¤ï¤»¤¿¤À¤±¤Ç¤¢¤ë¤È»×¤¦¡£¤Ä¤Þ¤ê¡¢ -°µ½Ì½èÍý¤Ë¤ª¤¤¤Æ¤Ï¡¢-lh4- ¤Î np ¤ò 13 ¤ÈÁÛÄꤷ¤Æ¤âÌäÂê¤ÏȯÀ¸¤·¤Ê¤¤¤È»× -¤ï¤ì¤ë¡£µÕ¤ËÉü¹æ½èÍý¤Ë¤ª¤¤¤Æ¤Ï¡¢(gzip ¤Î¤è¤¦¤Ë)°µ½Ì method ¤Î¾ðÊó¤¬ÅϤµ -¤ì¤Ê¤¤¾ì¹ç¤Ï np ¤ò 14 ¤È¤¹¤ë¤·¤«¤Ê¤¤¡£¤Ê¤ª¡¢¤³¤Î¤è¤¦¤Ê(gzip ¤Î¤è¤¦¤Ê) -¾ì¹ç -lh6,7- ¤Ø¤ÎÂбþ¤Ï¤Ç¤­¤Ê¤¤¡£ºÇ½é¤Î pbit ʬ¤ÎÍ×ÁÇ¿ô¤ÎÆɤ߹þ¤ß¤Ç 4 -¥Ó¥Ã¥ÈÆɤá¤Ð¤è¤¤¤Î¤« 5 ¥Ó¥Ã¥ÈÆɤá¤Ð¤è¤¤¤Î¤«¤¬¤ï¤«¤é¤Ê¤¤¤«¤é¤Ç¤¢¤ë) +p_len の出力フォーマットの先頭 pbit{4 or 5} は 0 ... 2^4{16} or +2^5{32} の範囲の値を格納できるが p_len の領域サイズは np{14..17} なので +0..np の範囲を超える場合は不正と判断しなければならない。 + +ところで復習になるが np は、各圧縮メソッドの辞書のサイズで決まる。対応 +を以下に再掲するので確認してほしい。(-lh4- の場合の np はなぜか 13 では +なく14 となっている。おそらく、LHA が実装された当時 -lh6-, -lh7- は存在 +せず np や pbit は固定値(定数 NP, PBIT)であったため、-lh4-, -lh5- の両 +方に対応できるよう変数の領域サイズを合わせただけであると思う。つまり、 +圧縮処理においては、-lh4- の np を 13 と想定しても問題は発生しないと思 +われる。逆に復号処理においては、(gzip のように)圧縮 method の情報が渡さ +れない場合は np を 14 とするしかない。なお、このような(gzip のような) +場合 -lh6,7- への対応はできない。最初の pbit 分の要素数の読み込みで 4 +ビット読めばよいのか 5 ビット読めばよいのかがわからないからである) method maxmatch dicsiz dicbit np(dicbit+1) pbit ----------------------------------------------------------- @@ -6446,25 +6446,25 @@ p_len -lh7- 256 2^16 16 17 5 (17<2^5) (13) -¤Þ¤¿¡¢t_len ¤ÈƱÍͤˡ¢p_len[i] >= 7 ¤Î·Á¼°¤Ç¤Ï¡¢1 ¤¬ 12 bit ¤è¤ê¿¤¯Ï¢ -³¤·¤¿¾ì¹ç¤ËÉÔÀµ¤Ç¤¢¤ë¡£ +また、t_len と同様に、p_len[i] >= 7 の形式では、1 が 12 bit より多く連 +続した場合に不正である。 (14) -¤â¤Á¤í¤ó¡¢p_len[] ¤òÆɤ߹þ¤ó¤À¸å¤Ë¹½ÃÛ¤·¤¿ Huffman ÌÚ¤ÎÀ°¹çÀ­¤¬Êݤ¿¤ì¤Ê -¤±¤ì¤Ð¤Ê¤é¤Ê¤¤ÅÀ¤Ï¾¤Î Huffman ÌÚ¤ÈƱ¤¸¤À¡£ +もちろん、p_len[] を読み込んだ後に構築した Huffman 木の整合性が保たれな +ければならない点は他の Huffman 木と同じだ。 -¤Ç¤Ï¡¢¼ÂºÝ¤ËÉü¹æ½èÍý¤Î¥»¥­¥å¥ê¥Æ¥£Âбþ¤ò¹Ô¤Ã¤¿¥½¡¼¥¹¤ò´ð¤Ë¼ÂÁõ¤ò³Îǧ¤·¤è¤¦¡£ +では、実際に復号処理のセキュリティ対応を行ったソースを基に実装を確認しよう。 -°Ê²¼¤Ï¡¢ +以下は、 https://bugzilla.redhat.com/show_bug.cgi?id=204676 -¤Ë¤Æ·ÇºÜ¤µ¤ì¤¿¥Ñ¥Ã¥Á¤¢¤ë¡£¤³¤Î¥Ñ¥Ã¥Á¤Ï gzip ÍѤΥѥåÁ¤Ç¤¢¤Ã¤¿¤¬ÆâÍÆ¤Ï -¤Û¤È¤ó¤ÉƱ¤¸¤Ç¤¢¤ë¡£(gzip ¤Ï LHA ¤È¤Û¤È¤ó¤ÉƱ¤¸Éü¹æ½èÍý¤Î¥½¡¼¥¹¤ò´Þ¤ó¤Ç -¤ª¤ê¡¢LHA ¤Î°µ½Ì·Á¼°¤òÉü¹æ¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤¿¤À¡¢LHA ¥Ø¥Ã¥À¤òÆɤळ¤È -¤Ï¤Ç¤­¤Ê¤¤¤¿¤á lzh ¥Õ¥¡¥¤¥ë¤òŸ³«¤Ç¤­¤ë¤ï¤±¤Ç¤Ï¤Ê¤¤¡£¸«¤¿¤È¤³¤í --lh4,lh5- ¤Ë¤Î¤ßÂбþ¤·¤Æ¤¤¤ë¡£) +にて掲載されたパッチある。このパッチは gzip 用のパッチであったが内容は +ほとんど同じである。(gzip は LHA とほとんど同じ復号処理のソースを含んで +おり、LHA の圧縮形式を復号することができる。ただ、LHA ヘッダを読むこと +はできないため lzh ファイルを展開できるわけではない。見たところ +-lh4,lh5- にのみ対応している。) diff -ru gzip-1.3.5.orig/unlzh.c gzip-1.3.5/unlzh.c --- gzip-1.3.5.orig/unlzh.c 1999-10-06 06:00:00.000000000 +0100 @@ -6480,9 +6480,9 @@ diff -ru gzip-1.3.5.orig/unlzh.c gzip-1.3.5/unlzh.c + else count[bitlen[i]]++; + } -bitlen ¤Ï¡¢c_len, p_len, t_len ¤Ç¤¢¤ê¡¢¤¤¤º¤ì¤Î Huffman ÌÚ¤âºÇÂç¤Î³¬ÁØ -¤Ï 16 ¤Þ¤Ç¤Ç¤¢¤ë¤«¤é¤½¤ÎÈϰϤòĶ¤¨¤¿¤â¤Î¤¬¤Ê¤¤¤«¥Á¥§¥Ã¥¯¤·¤Æ¤¤¤ë¡£¤³¤ì -¤Ï¡¢¥»¥­¥å¥ê¥Æ¥£¥Ð¥°¤Î½ÅÍפʲþ½¤ÅÀ¤Î£±ÅÀÌܤǤ¢¤ë¡£ +bitlen は、c_len, p_len, t_len であり、いずれの Huffman 木も最大の階層 +は 16 までであるからその範囲を超えたものがないかチェックしている。これ +は、セキュリティバグの重要な改修点の1点目である。 start[1] = 0; for (i = 1; i <= 16; i++) @@ -6495,15 +6495,15 @@ bitlen jutbits = 16 - tablebits; for (i = 1; i <= (unsigned)tablebits; i++) { -tablebits ¤Ï¡¢make_table() ¤ò¸Æ¤Ó½Ð¤¹¤È¤­¤Ë»ØÄꤹ¤ë°ú¿ô¤Ç°Ê²¼¤ÎÄ̤ê¸ÇÄê -ÃÍ(8 or 12)¤Ç¤¢¤ë¡£½¾¤Ã¤Æɬ¤º¤·¤âɬÍפǤϤʤ¤¡£(¤¢¤¨¤Æ¥Á¥§¥Ã¥¯¤¹¤ë¤Ê¤é -Bad table ¤Ç¤Ê¤¯ Bug ¤Èɽ¼¨¤¹¤ë¤Ù¤­¤À¤í¤¦) +tablebits は、make_table() を呼び出すときに指定する引数で以下の通り固定 +値(8 or 12)である。従って必ずしも必要ではない。(あえてチェックするなら +Bad table でなく Bug と表示するべきだろう) make_table(nn, pt_len, 8, pt_table); make_table(NC, c_len, 12, c_table); -total & 0xffff ¤Ï¤É¤ÎÄøÅÙ¤ÎÉÔÀµ¥Æ¡¼¥Ö¥ë¤ò¸¡½Ð¤¹¤ë¤Î¤À¤í¤¦¤«¡© -³ºÅö¤Î½èÍý¤ò°Ê²¼¤Ë¼¨¤½¤¦¡£ +total & 0xffff はどの程度の不正テーブルを検出するのだろうか? +該当の処理を以下に示そう。 for (i = 1; i <= 16; i++) count[i] = 0; for (i = 0; i < (unsigned)nchar; i++) count[bitlen[i]]++; @@ -6514,8 +6514,8 @@ total & 0xffff if ((start[17] & 0xffff) != 0) error("Bad table\n"); -¤³¤ì¤Ï¡¢LHa for UNIX ¤Ç°Ê²¼¤Î¤è¤¦¤Ë½ñ¤«¤ì¤Æ¤¤¤¿Éôʬ¤Ç¤¢¤ê¡¢¥í¥¸¥Ã¥¯¤È¤· -¤Æ¤Ï¤Þ¤Ã¤¿¤¯°ì½ï¤Ç¤¢¤ë¡£ +これは、LHa for UNIX で以下のように書かれていた部分であり、ロジックとし +てはまったく一緒である。 /* (A) */ avail = nchar; @@ -6541,24 +6541,24 @@ total & 0xffff if ((total & 0xffff) != 0) error("make_table()", "Bad table (5)\n"); -Î㤨¤Ð¡¢°Ê²¼¤Î Huffman ÌڤǤϳƳ¬ÁØ¤Î½Å¤ß weight ¤ÎÃÍ(gzip ¤Î¥½¡¼¥¹¤Ç¡¢ -(16 - i)¤ÎÃÍ)¤Ï +例えば、以下の Huffman 木では各階層の重み weight の値(gzip のソースで、 +(16 - i)の値)は /\ a /\ weight[1] = 0x8000 b c weight[2] = 0x4000 -¤Ç¤¢¤ê¡¢Íդοô¤Ë½Å¤ß¤ò¤«¤±¤¿ÃͤÎÁíÏÂ¤Ï +であり、葉の数に重みをかけた値の総和は 0x10000 -¤È¤Ê¤ë¡£¤³¤ì¤ÏÀµ¤·¤¤ Huffman ÌڤˤĤ¤¤Æɬ¤ºÀ®¤êΩ¤¿¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤É¬Í× -½½Ê¬¾ò·ï¤Ç¤¢¤ë¡£ +となる。これは正しい Huffman 木について必ず成り立たなければならない必要 +十分条件である。 -¤·¤«¤·¡¢´û¸¤Î½èÍý¤Ç¤ÏÎ㤨¤Ð 1 ³¬ÁØÌܤÎÍդοô¤¬ 4 ¤Ç¤¢¤ë¾ì¹ç¤Ë total ¤¬ -0x20000 ¤È¤Ê¤ê 0xffff ¤È¤ÎÏÀÍýÀѤǤϰ۾ï¤ò¸¡ÃΤǤ­¤Ê¤¤¡£ +しかし、既存の処理では例えば 1 階層目の葉の数が 4 である場合に total が +0x20000 となり 0xffff との論理積では異常を検知できない。 -¤³¤³¤Ï¡¢°Ê²¼¤Î¤è¤¦¤Ë¤¹¤ë¤Ù¤­¤Ç¤¢¤í¤¦(°Ê²¼¤Ï¡¢LHa for UNIX¤Î¥½¡¼¥¹)¡£ +ここは、以下のようにするべきであろう(以下は、LHa for UNIXのソース)。 /* (C) */ /* calculate first code */ @@ -6572,30 +6572,30 @@ total & 0xffff if (total != 0x10000) error("make_table()", "Bad table (5)\n"); -¥ë¡¼¥×¤ÎÃæ¤Ë total ¤Î¥Á¥§¥Ã¥¯¤òÆþ¤ì¤Æ¤¤¤ë¤Î¤ÏÇ°¤Î¤¿¤á¤Ç¤¢¤ë¡£¤â¤Á¤í¤ó¡¢ -total ¤ÎÊÑ¿ô¤Î¥µ¥¤¥º¤Ï 16 bit ¤Ç¤Ï­¤ê¤Ê¤¤¤Î¤Ç 32 bit À°¿ô¤Ë¤¹¤ëɬÍפ¬ -¤¢¤ë¡£(gzip ¤Î¥½¡¼¥¹¤Ç¤Ï¡¢total ÊÑ¿ô¤ÎÂå¤ï¤ê¤Ë start[17] ¤¬»È¤ï¤ì¤Æ¤¤¤ë -¤Î¤Ç¡¢LHa for UNIX ¤Î¤è¤¦¤Ë total ÊÑ¿ô¤Ë¤¹¤ë¤«¡¢start[] ¼«ÂΤò 32 bit -À°¿ô¤ËÊѤ¨¤ëɬÍפ¬¤¢¤ë¡£) +ループの中に total のチェックを入れているのは念のためである。もちろん、 +total の変数のサイズは 16 bit では足りないので 32 bit 整数にする必要が +ある。(gzip のソースでは、total 変数の代わりに start[17] が使われている +ので、LHa for UNIX のように total 変数にするか、start[] 自体を 32 bit +整数に変える必要がある。) -¤Ê¤ª¡¢32 bit À°¿ô¤Ë¤·¤¿¾ì¹ç¤Ç¤â¡¢count[1] ¤¬ 0x20000 ¤ÎÃͰʾå¤Ç¤¢¤ë¤È¤­ -total ¤¬¥ª¡¼¥Ð¡¼¥Õ¥í¡¼¤¹¤ë¶²¤ì¤¬¤¢¤ë¤¬°Ê²¼¤Î½èÍý¤è¤ê count[] ¤¬nchar ¤è -¤êÂ礭¤¯¤Ê¤ë¤³¤È¤Ï¤Ê¤¤¡£ +なお、32 bit 整数にした場合でも、count[1] が 0x20000 の値以上であるとき +total がオーバーフローする恐れがあるが以下の処理より count[] がnchar よ +り大きくなることはない。 /* (B) */ /* count */ for (i = 0; i < nchar; i++) count[bitlen[i]]++; -¤½¤·¤Æ¡¢Í×ÁÇ¿ô¤¬ºÇ¤âÂ礭¤¤ c_len ¤Î¾ì¹ç¤Ç nchar ¤Ï NC{510} ¤Ç¤¢¤ë¤«¤é -32 bit À°¿ô¤ÎÈϰϤò¥ª¡¼¥Ð¡¼¤¹¤ë¤³¤È¤Ï¤Ê¤¤¤À¤í¤¦¡£¤³¤Î¤³¤È¤¬¤Ï¤Ã¤­¤ê¤·¤Æ -¤¤¤ì¤Ð¥ë¡¼¥×Ãæ¤ËÆþ¤ì¤¿ +そして、要素数が最も大きい c_len の場合で nchar は NC{510} であるから +32 bit 整数の範囲をオーバーすることはないだろう。このことがはっきりして +いればループ中に入れた if (total > 0x10000) error("make_table()", "Bad table (5)\n"); -¤Ïɬ¤º¤·¤âɬÍפǤϤʤ¤¡£¤¢¤ë¤¤¤Ï¡¢³¬ÁØ n ¤ÎÍդοô¤¬ 2^n ¤ò±Û¤¨¤Ê¤¤¤³¤È¤Î -¥Á¥§¥Ã¥¯¤ËÊѤ¨¤Æ¤âÎɤ¤¤À¤í¤¦¡£ +は必ずしも必要ではない。あるいは、階層 n の葉の数が 2^n を越えないことの +チェックに変えても良いだろう。 /* (C) */ /* calculate first code */ @@ -6609,8 +6609,8 @@ total if (total != 0x10000) error("make_table()", "Bad table (5)\n"); -¤³¤ì¤Ê¤é¤Ð¡¢total ¤ÏºÇÂç¤Ç¤â 16 * 0x10000 = 0x100000 ¤Ë¤·¤«¤Ê¤é¤Ê¤¤¤³¤È -¤¬Êݾڤµ¤ì¤ë¡£ +これならば、total は最大でも 16 * 0x10000 = 0x100000 にしかならないこと +が保証される。 @@ -169,15 +173,15 @@ @@ -6622,7 +6622,7 @@ total + while (i < k) table[i++] = 0; } -tablebits ¤Ï¸ÇÄêÃͤʤΤǡ¢É¬¤º¤·¤â¤³¤Î¥Á¥§¥Ã¥¯¤ÏɬÍפǤϤʤ¤¡£ +tablebits は固定値なので、必ずしもこのチェックは必要ではない。 avail = nchar; mask = (unsigned) 1 << (15 - tablebits); @@ -6634,40 +6634,40 @@ tablebits for (i = start[len]; i < nextcode; i++) table[i] = ch; } else { -DIST_BUFSIZE ¤Ï¡¢c_table[] ¤Î¥Ð¥Ã¥Õ¥¡¥µ¥¤¥º¤Ç 1<<12 ¤Ç¤¢¤ë¡£nextcode ¤Ï¡¢ -LHa for UNIX ¤Ç¤Î³ºÅöÊÑ¿ô¤Ï l ¤Ç¡¢Huffman Éä¹æ¤Î(ÀèƬ tablebits ¥Ó¥Ã¥È -¤Î)¼è¤êÆÀ¤ëºÇÂçÃͤǤ¢¤ë¡£¤³¤ì¤Ï¡¢ÍýÏÀ¾å +DIST_BUFSIZE は、c_table[] のバッファサイズで 1<<12 である。nextcode は、 +LHa for UNIX での該当変数は l で、Huffman 符号の(先頭 tablebits ビット +の)取り得る最大値である。これは、理論上 - tablebits ºÇÂç¤Î Huffman Éä¹æ m + tablebits 最大の Huffman 符号 m c_len 12 1111 1111 1111{4095} 4 p_len 8 1111 1111{255} 8 t_len 8 1111 1111{255} 8 -¤Ç¤¢¤ê¡¢¥ë¡¼¥×æ½Ð¾ò·ï¤¬ÉÔÀµ¤Ç¤Ê¤¤¸Â¤ê¡¢¤³¤Î¥Á¥§¥Ã¥¯¤ÏÉÔÍפǤ¢¤ë¤È»×¤ï -¤ì¤ë¡£(¤½¤â¤½¤â¡¢Ç°¤Î¤¿¤á¤È¤¤¤¦°ÕÌ£¤Ç¥Á¥§¥Ã¥¯¤·¤Æ¤¤¤ë¤Î¤Ê¤é c_len ¤Î¾ì -¹ç¤À¤±¤·¤«¹Íθ¤·¤Æ¤Ê¤¤¤Î¤âÊѤǤ¢¤ë) +であり、ループ脱出条件が不正でない限り、このチェックは不要であると思わ +れる。(そもそも、念のためという意味でチェックしているのなら c_len の場 +合だけしか考慮してないのも変である) -¤¿¤À¤·¡¢Àè¤Û¤É¤Î total ¥Á¥§¥Ã¥¯¤¬ÉÔ´°Á´¤Ê¤Þ¤Þ¤À¤È start[] ¤ÎÃͤ¬ -ÉÔÀµ¤Ë¤Ê¤ê¤¦¤ë¤¿¤á¡¢Ïä¬ÊѤï¤Ã¤Æ¤¯¤ë¡£ -¤½¤¦¤¤¤¦¤ï¤±¤Ç¡¢¤³¤ì¤Ï¥»¥­¥å¥ê¥Æ¥£¥Ð¥°¤Î½ÅÍפʲþ½¤ÅÀ¤Î£²ÅÀÌܤȤʤ뤬¡¢ -ËÜÅö¤Ï total ¤Î¥Á¥§¥Ã¥¯¤ÎÊý¤¬½ÅÍפǤ¢¤ë¡£ +ただし、先ほどの total チェックが不完全なままだと start[] の値が +不正になりうるため、話が変わってくる。 +そういうわけで、これはセキュリティバグの重要な改修点の2点目となるが、 +本当は total のチェックの方が重要である。 -¤µ¤é¤Ë¡¢ÌڤηÁ¤¬Àµ¤·¤¯¤Æ¤â +さらに、木の形が正しくても - Éü¹æ¸ì¤Î¼ïÎà¿ô < Íդοô + 復号語の種類数 < 葉の数 -¤Ç¤¢¤ë¾ì¹ç¡¢ÍÕ¤ËÃͤ¬³ä¤êÅö¤Æ¤é¤ì¤Ê¤¤»Þ¤¬È¯À¸¤·¤Æ¤·¤Þ¤¦¡£¤³¤ì¤â¥Á¥§¥Ã¥¯ -¤·¤Æ¤ª¤¤¤¿Êý¤¬Îɤ¤¤À¤í¤¦¡£Éü¹æ¸ì¤Î¿ô¤Ï nchar ¤ÇÍդοô¤Ï count[] ¤ÎÁíÏ -¤Ç¤¢¤ë¤«¤é +である場合、葉に値が割り当てられない枝が発生してしまう。これもチェック +しておいた方が良いだろう。復号語の数は nchar で葉の数は count[] の総和 +であるから /* (B) */ /* count */ for (i = 0; i < nchar; i++) count[bitlen[i]]++; -¤è¤ê¡¢¤³¤Î¤³¤È¤ÏÊݾڤµ¤ì¤Æ¤¤¤ë¤è¤¦¤À(i >= nchar ¤Ç¤¢¤ë bitlen[i] ¤¬ÀßÄê -¤µ¤ì¤Æ¤¤¤Æ¤â»È¤ï¤ì¤Ê¤¤¡£¤â¤Á¤í¤óÀßÄꤵ¤ì¤¿»þÅÀ¤Ç¡¢¥¨¥é¡¼¤ò¸¡½Ð¤¹¤ëÊý¤¬ -˾¤Þ¤·¤¤¤È¤Ï»×¤¦) +より、このことは保証されているようだ(i >= nchar である bitlen[i] が設定 +されていても使われない。もちろん設定された時点で、エラーを検出する方が +望ましいとは思う) @@ -218,7 +222,7 @@ for (i = 0; i < 256; i++) pt_table[i] = c; @@ -6679,17 +6679,17 @@ LHa for UNIX if (c == 7) { mask = (unsigned) 1 << (BITBUFSIZ - 1 - 3); -n ¤¬ t_len¡¢p_len ¤ÎÎΰè¤ÎÈÏ°ÏÆâ¤ÎÃͤȤϸ¤é¤Ê¤¤¤Î¤Ç¤½¤Î¥Á¥§¥Ã¥¯¤ò¹Ô¤Ã -¤Æ¤¤¤ë¡£¤³¤ì¤Ï¡¢¥»¥­¥å¥ê¥Æ¥£¥Ð¥°¤Î½ÅÍפʲþ½¤ÅÀ¤Î£³ÅÀÌܤǤ¢¤ë¡£ +n が t_len、p_len の領域の範囲内の値とは限らないのでそのチェックを行っ +ている。これは、セキュリティバグの重要な改修点の3点目である。 -µ¤¤Ë¤Ê¤ë¤Î¤Ï¡¢ +気になるのは、 -¡¦p_len, t_len ¤ÎÏÀÍýŪ¤ÊÎΰ襵¥¤¥º¤Ç¤Ê¤¯¼ÂÁõ¾å¤Î pt_len ¤ÎÎΰ襵¥¤¥º¤Ç - ¥Á¥§¥Ã¥¯¤·¤Æ¤¤¤ë¡£¤½¤Î¤¿¤á¡¢¥Ð¥Ã¥Õ¥¡¥ª¡¼¥Ð¡¼¥Õ¥í¡¼¤Î¥»¥­¥å¥ê¥Æ¥£Âкö - ¤È¤·¤Æ¤Ï½½Ê¬¤À¤¬¥¨¥é¡¼¥Á¥§¥Ã¥¯¤È¤·¤Æ¤ÏÉÔ´°Á´¤Ç¤¢¤ë(¥¨¥é¡¼¤Î¸¡½Ð¤¬ÃÙ±ä - ¤µ¤ì¤ë)¡£ +・p_len, t_len の論理的な領域サイズでなく実装上の pt_len の領域サイズで + チェックしている。そのため、バッファオーバーフローのセキュリティ対策 + としては十分だがエラーチェックとしては不完全である(エラーの検出が遅延 + される)。 -¡¦ÉÔÀµ¤ÊÃͤò¥¨¥é¡¼¤È¤»¤º¡£½èÍý¤òºÇÂçÃͤǷѳ¤·¤Æ¤¤¤ë¡£¤½¤Î¤¿¤á¡¢°Ê²¼Æ±Ê¸¡£ +・不正な値をエラーとせず。処理を最大値で継続している。そのため、以下同文。 @@ -228,7 +232,7 @@ pt_len[i++] = c; @@ -6701,9 +6701,9 @@ n } while (i < nn) pt_len[i++] = 0; -i_special ¤Ï 3 ¸ÇÄê¤Ê¤Î¤Ç¡¢¤³¤Î¥Á¥§¥Ã¥¯¤Ïɬ¤º¤·¤âɬÍפȤ¤¤¦¤ï¤±¤Ç¤Ï¤Ê¤¤¡£ -(¸·Ì©¤Ë¤Ï¡¢¼ÂÁõ¾å i_special ¤¬ -1 ¤Ç¤¢¤ë¾ì¹ç¤¬¤¢¤ë¤¬ i ¤¬ -1 ¤Ë¤Ê¤Ã¤¿»þ -ÅÀ¤Ç¥Ð¥°¤Ç¤¢¤ê¡¢¤½¤Î¿´ÇۤϤʤµ¤½¤¦¤À) +i_special は 3 固定なので、このチェックは必ずしも必要というわけではない。 +(厳密には、実装上 i_special が -1 である場合があるが i が -1 になった時 +点でバグであり、その心配はなさそうだ) @@ -248,7 +252,7 @@ for (i = 0; i < 4096; i++) c_table[i] = c; @@ -6715,7 +6715,7 @@ i_special if (c >= NT) { mask = (unsigned) 1 << (BITBUFSIZ - 1 - 8); -n ¤¬ c_len ¤ÎÎΰè¤ÎÈÏ°ÏÆâ¤ÎÃͤȤϸ¤é¤Ê¤¤¤Î¤Ç¡¢Æ±¾å¡£ +n が c_len の領域の範囲内の値とは限らないので、同上。 @@ -256,14 +260,14 @@ if (bitbuf & mask) c = right[c]; @@ -6725,7 +6725,7 @@ n + } while (c >= NT && (mask || c != left[c])); } -¤³¤Î¥ë¡¼¥×Éôʬ¤ÎÁ´ÂΤϥѥåÁÁ°¤À¤È¡¢ +このループ部分の全体はパッチ前だと、 c = pt_table[bitbuf >> (BITBUFSIZ - 8)]; if (c >= NT) { @@ -6738,34 +6738,34 @@ n } fillbuf((int) pt_len[c]); -¤È¤Ê¤Ã¤Æ¤ª¤ê¡¢ +となっており、 mask == 0 && c == left[c] -¤Î¾ò·ï¤òËþ¤¿¤·¤Æ¤·¤Þ¤¦¤è¤¦¤ÊÉÔÀµ¤ÊÌÚ¤¬¹½ÃÛ¤µ¤ì¤Æ¤¤¤ë¤È c ¤ÎÃͤÏÊѲ½¤»¤º -¤¤¤Ä¤Þ¤Ç¤â¥ë¡¼¥×¤·Â³¤±¤ë¤³¤È¤Ë¤Ê¤Ã¤Æ¤·¤Þ¤¦¡£¤½¤³¤Ç¡¢¤³¤Î¾ò·ï¤¬È¯À¸¤·¤¿ -¤È¤­¤Ë¤¹¤°¤Ë¥ë¡¼¥×¤òæ½Ð¤¹¤ë¤è¤¦¤½¤Î¾ò·ï¤ÎÈÝÄê¤Ç¤¢¤ë +の条件を満たしてしまうような不正な木が構築されていると c の値は変化せず +いつまでもループし続けることになってしまう。そこで、この条件が発生した +ときにすぐにループを脱出するようその条件の否定である !(mask == 0 && c == left[c]) -¤Ä¤Þ¤ê¤Ï¡¢ +つまりは、 (mask || c != left[c]) -¤ò¥ë¡¼¥×·Ñ³¤Î while ¾ò·ï¤Ë²Ã¤¨¤Æ¤¤¤ë¤è¤¦¤À¡£¤·¤«¤·¡¢mask ¤Î½é´üÃÍ¤Ï +をループ継続の while 条件に加えているようだ。しかし、mask の初期値は 1 << (BITBUFSIZ{16} - 1 - 8) {0000 0000 1000 0000} -¤Ç¤¢¤ê¥ë¡¼¥×Ëè¤Ë +でありループ毎に mask >>= 1; -¤·¤Æ¤¤¤ë¤Î¤À¤«¤é¡¢mask == 0 ¤Ë¤Ê¤Ã¤¿»þÅÀ¤Ç¡¢8 bit(ºÇ½é¤Îɽ°ú¤­¤È¹ç¤ï¤» -¤Æ 16 bit)¤Î Huffman Éä¹æ¤òÆɤ߹þ¤ó¤Ç¤ª¤ê(¤¯¤É¤¤¤«¤âÃΤì¤Ê¤¤¤¬ LHA ¤Ë¤ª -¤±¤ë Huffman Ìڤγ¬ÁؤϺÇÂç 16)¡¢mask == 0 ¤òæ½Ð¾ò·ï¤Ë²Ã¤¨¤ë¤À¤±¤ÇÎɤ¤ -¤È»×¤¦¡£ +しているのだから、mask == 0 になった時点で、8 bit(最初の表引きと合わせ +て 16 bit)の Huffman 符号を読み込んでおり(くどいかも知れないが LHA にお +ける Huffman 木の階層は最大 16)、mask == 0 を脱出条件に加えるだけで良い +と思う。 -¤â¤Á¤í¤ó¡¢17 bit ¤ÎÉä¹æ¤òÆɤ߹þ¤ó¤À»þÅÀ¤ÇÉÔÀµ¤Ê¤Î¤À¤«¤é +もちろん、17 bit の符号を読み込んだ時点で不正なのだから mask = (unsigned) 1 << (BITBUFSIZ - 1 - 8); do { @@ -6776,9 +6776,9 @@ n mask >>= 1; } while (c >= NT); -¤È°Û¾ï½ªÎ»¤·¤Æ¤âÌäÂê¤Ê¤¤¤È»×¤¦¡£°Ê²¼¤Î¤è¤¦¤Ë for ¤Ç½ñ¤¯¤³¤È¤â¤Ç¤­¤ë¤¬¤Þ -¤¢·ë¶É¤ÏƱ¤¸¤³¤È¤À¡£(¤³¤¦¤¹¤ë¤È¸«¤ä¤¹¤¤¤À¤í¤¦¤«¡©¤È¹Í¤¨¤Æ¤ß¤¿¤À¤±¤Ç¤¢¤ë -¤¬¡¢Æä˸ú²Ì¤Ï¤Ê¤¤¤è¤¦¤À¡£) +と異常終了しても問題ないと思う。以下のように for で書くこともできるがま +あ結局は同じことだ。(こうすると見やすいだろうか?と考えてみただけである +が、特に効果はないようだ。) for (mask = 1 <<(BITBUFSIZ-1-8); mask != 0; mask >>= 1) { if (bitbuf & mask) c = right[c]; @@ -6788,10 +6788,10 @@ n } if (mask == 0) error(...); -¤·¤«¤· c ¤ÎÃÍ¤È left[], right[] ¤ÎÎΰ襵¥¤¥º¤È¤ÎÈæ³Ó¤ÏÎɤ¤¤Î¤À¤í¤¦¤«¡© -¡ÊÍýÏÀ¾å¤Ï Huffman Ìڤι½ÃÛ»þ¤Ë¥Á¥§¥Ã¥¯¤µ¤ì¤Æ¤¤¤ì¤ÐÌäÂê¤Ê¤¤¤Î¤Ç¡¢»ä¤ÏÌä -Âê¤Ê¤¤¤È»×¤¦¤Î¤À¤¬¡¢¤½¤ì¤ò¸À¤¦¤È¤½¤â¤½¤â̵¸Â¥ë¡¼¥×¤Î¥Á¥§¥Ã¥¯¤âÉÔÍפǤ¢ -¤ë¤È»×¤¦¡£) +しかし c の値と left[], right[] の領域サイズとの比較は良いのだろうか? +(理論上は Huffman 木の構築時にチェックされていれば問題ないので、私は問 +題ないと思うのだが、それを言うとそもそも無限ループのチェックも不要であ +ると思う。) fillbuf((int) pt_len[c]); if (c <= 2) { @@ -6804,8 +6804,8 @@ n } while (i < NC) c_len[i++] = 0; -c_len[] ¤ÎÈϰϳ°¤ÎÎΰè¤ËÃͤ¬ÀßÄꤵ¤ì¤Ê¤¤¤è¤¦¥Á¥§¥Ã¥¯¤·¤Æ¤¤¤ë¡£¤·¤«¤·¡¢ -¤ä¤Ï¤ê¥¨¥é¡¼¤È¤·¤Æ¸¡½Ð¤»¤º¤Ë½èÍý¤ò³¹Ô¤·¤Æ¤¤¤ë¡£ +c_len[] の範囲外の領域に値が設定されないようチェックしている。しかし、 +やはりエラーとして検出せずに処理を続行している。 @@ -292,7 +296,7 @@ if (bitbuf & mask) j = right[j]; @@ -6817,7 +6817,7 @@ c_len[] fillbuf((int) c_len[j]); return j; -Ʊ¾å¡£ +同上。 @@ -309,7 +313,7 @@ if (bitbuf & mask) j = right[j]; @@ -6829,7 +6829,7 @@ c_len[] fillbuf((int) pt_len[j]); if (j != 0) j = ((unsigned) 1 << (j - 1)) + getbits((int) (j - 1)); -Ʊ¾å¡£ +同上。 @@ -356,7 +360,7 @@ while (--j >= 0) { @@ -6859,12 +6859,12 @@ c_len[] } -¤µ¤Æ¡¢¤³¤ì¤é¥»¥­¥å¥ê¥Æ¥£Âкö¤Ï¶ñÂÎŪ¤Ë¤É¤Î¤è¤¦¤Ê¾ì¹ç¤òÁÛÄꤷ¤Æ¤¤¤ë¤Î¤À -¤í¤¦¡© +さて、これらセキュリティ対策は具体的にどのような場合を想定しているのだ +ろう? https://bugzilla.redhat.com/show_bug.cgi?id=204676 -¤Ë¤Ï¡¢Ìڤι½ÃÛ¤¬ÉÔÀµ¤Ë¤Ê¤ë¥·¥Ê¥ê¥ª¤È¤·¤Æ°Ê²¼¤¬½ñ¤«¤ì¤Æ¤¤¤ë¡£ +には、木の構築が不正になるシナリオとして以下が書かれている。 > * Construct a pt_len[] such that pt_len[n] is 0. > * Construct a pt_table[] such that pt_table[(code buffer) >> 16 - 8] @@ -6872,17 +6872,17 @@ is n (where n>2) > * Now c_len[] is filled with (n-2), generating exceptionally high values in > count[n-2]. -¤·¤«¤·¡¢¤³¤ì¤òÆɤó¤Ç¤â¤è¤¯¤ï¤«¤é¤Ê¤«¤Ã¤¿¤Î¤Ç°ì½ï¤Ë·ÇºÜ¤µ¤ì¤Æ¤¤¤¿¥µ¥ó¥× -¥ë¤ÎÉÔÀµ¤ÊÉä¹æ¤òÆɤó¤Ç¤ß¤¿¡£ +しかし、これを読んでもよくわからなかったので一緒に掲載されていたサンプ +ルの不正な符号を読んでみた。 > 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 -\x1f\xa0 ¤Ï¥Þ¥¸¥Ã¥¯¥Ê¥ó¥Ð¡¼¤Ç¡¢gzip ¤Ë¤ª¤±¤ë LHA ¤Î°µ½Ì·Á¼°¤ò¼¨¤¹¡£¤½¤· -¤Æ¡¢LHA ¥Õ¥©¡¼¥Þ¥Ã¥È¤Ï¡¢\xab\xcd ¤«¤é»Ï¤Þ¤ë¡£¤³¤ì¤Ï¡¢blocksize ¤Ç¤¢¤ë¡£ +\x1f\xa0 はマジックナンバーで、gzip における LHA の圧縮形式を示す。そし +て、LHA フォーマットは、\xab\xcd から始まる。これは、blocksize である。 blocksize = Oxabcd -¤½¤·¤Æ¡¢t_len ¤Î½ÐÎÏ·Á¼°¤¬Â³¤¯¡£2 ¿Ê¿ô¤È¹ç¤ï¤»¤Æ²¼µ­¤Ë¼¨¤¹¡£ +そして、t_len の出力形式が続く。2 進数と合わせて下記に示す。 f6 40 01 c2 cc 36 1111 0110 0100 0000 0000 0001 1100 0010 1100 1100 0011 0110 @@ -6895,8 +6895,8 @@ is n (where n>2) c8 00 1100 1000, 0000 * 2048 -¤¤¤­¤Ê¤ê¡¢t_len ¤Î¥µ¥¤¥º¤¬ÉÔÀµ(1111 0=0x1e(30) > NT{19})¤Ç¤¢¤ë¡£¤½¤·¤Æ¡¢³Æ -t_len[] ¤òÆɤ߹þ¤à¤È°Ê²¼¤Î¤è¤¦¤Ë¤Ê¤ë +いきなり、t_len のサイズが不正(1111 0=0x1e(30) > NT{19})である。そして、各 +t_len[] を読み込むと以下のようになる t_len[ 0] = 110 :6 t_len[ 1] = 010 :2 @@ -6930,7 +6930,7 @@ t_len[] t_len[28] = 0 00 :0 t_len[29] = 00, 1 :1 -¤³¤Î t_len ¤«¤é Huffman Ìڤγ¬ÁؤÎÍդοô¤Ï¡¢°Ê²¼¤ÎÄ̤ê¤È¤Ê¤ê +この t_len から Huffman 木の階層の葉の数は、以下の通りとなり count[1] = 3 count[2] = 4 @@ -6939,14 +6939,14 @@ t_len[] count[5] = 1 count[6] = 2 -°Ê²¼¤Î¤è¤¦¤ÊÌڤηÁ¤Ë¤Ê¤ë¤Î¤Ç¡¢ÉÔÀµ¤Ç¤¢¤ë(¿ÞÃæ X ¤Ï¡¢count ¤ÎÃͤ¬ÉÔÀµ¤Ê -¤¿¤á¤Ë;·×¤Ë½ÐÍ褿ÍÕ¤ò¼¨¤¹)¡£ +以下のような木の形になるので、不正である(図中 X は、count の値が不正な +ために余計に出来た葉を示す)。 . / / \ - X . . - 1 ³¬ÁØÌÜ + X . . - 1 階層目 / \ / \ - . .. . - 2 ³¬ÁØÌÜ + . .. . - 2 階層目 / \ . . \ / \ @@ -6954,11 +6954,11 @@ t_len[] / \ . . / \ - . . - 6 ³¬ÁØÌÜ + . . - 6 階層目 -·ë²ÌŪ¤Ë¡¢c_len[] ¤Ï¤È¤¤¤¦¤È¤³¤Á¤é¤Ï gzip ¤Î¥½¡¼¥¹¤ò½¤Àµ¤·¤Æ¼ÂºÝ¤ËÃͤò -½ÐÎϤµ¤»¤Æ¤ß¤¿¤È¤³¤í°Ê²¼¤ÎÄ̤ꤹ¤Ù¤Æ¤ÎÃͤ¬ 5 ¤Ë¤Ê¤Ã¤¿¡£¤³¤ì¤Ï¤ä¤Ï¤êÉÔÀµ -¤Ç¤¢¤ë¡£(³¬ÁØ 5 ¤ÎÍդοô¤¬ 288 ¸Ä¤¢¤ë) +結果的に、c_len[] はというとこちらは gzip のソースを修正して実際に値を +出力させてみたところ以下の通りすべての値が 5 になった。これはやはり不正 +である。(階層 5 の葉の数が 288 個ある) size of c_len: 100 1000, 0 0x120(288) @@ -6968,12 +6968,12 @@ t_len[] c_len[287] = 5 -¤È¤³¤í¤Ç¡¢ËÜÅö¤Ï¤³¤¦¤·¤¿¤«¤Ã¤¿¤Î¤Ç¤Ï¤Ê¤¤¤À¤í¤¦¤«¡© +ところで、本当はこうしたかったのではないだろうか? perl -e 'print "\x1f\xa0","\xab\xcd","\x9e\x40\x01\xc2\xcc\x36\x0c\x92","\xc8","\x00"x"2048"' | gzip -d -¤³¤ì¤Ê¤é¤Ð¡¢t_len[] ¤ÎÎΰ襵¥¤¥º¤ÏĶ¤¨¤Ê¤¤¤Î¤Ç¡¢¤³¤Î¥Á¥§¥Ã¥¯¤Ë¤Ï¤«¤«¤é -¤Ê¤¤¡£ +これならば、t_len[] の領域サイズは超えないので、このチェックにはかから +ない。 blocksize: 0xabcd @@ -6999,7 +6999,7 @@ perl -e 'print "\x1f\xa0","\xab\xcd","\x9e\x40\x01\xc2\xcc\x36\x0c\x92","\xc8"," t_len[17]: 2 t_len[18]: 2 -¤½¤·¤Æ¡¢Àè¤Û¤É¤ÈƱ¤¸ count[] ¤Î·ë²Ì¤È¤Ê¤êÉÔÀµ¤ÊÌÚ¤¬¤Ç¤­¤ë¡£ +そして、先ほどと同じ count[] の結果となり不正な木ができる。 count[1] = 3 count[2] = 4 @@ -7008,7 +7008,7 @@ perl -e 'print "\x1f\xa0","\xab\xcd","\x9e\x40\x01\xc2\xcc\x36\x0c\x92","\xc8"," count[5] = 1 count[6] = 2 -·ë²Ì¡¢c_len[] ¤¬ÉÔÀµ¤È¤Ê¤ë¡£ +結果、c_len[] が不正となる。 size of c_len: 0x190 (400) @@ -7016,43 +7016,43 @@ perl -e 'print "\x1f\xa0","\xab\xcd","\x9e\x40\x01\xc2\xcc\x36\x0c\x92","\xc8"," c_len[1] = 5 : -·ë¶É¡¢¤³¤ÎÌäÂê¤ÏÌڤηÁ¤ÎÉÔÀµ¤ò¸¡½Ð¤Ç¤­¤Æ¤Ê¤¤¤¿¤á¤ËȯÀ¸¤·¤Æ¤¤¤ë¡£Ä´¤Ù¤Æ -¤ß¤¿¤È¤³¤í¡¢¤³¤ÎÎã¤Ç¤Ï total ¤¬ 0x30000 ¤Ë¤Ê¤ë¤¿¤á¤Ë¡¢ +結局、この問題は木の形の不正を検出できてないために発生している。調べて +みたところ、この例では total が 0x30000 になるために、 (total & 0xffff) != 0 -¤Ç¸¡½Ð¤Ç¤­¤Æ¤¤¤Ê¤¤¤è¤¦¤Ç¤¢¤ë¡£½¾¤Ã¤Æ¡¢Àè¤Ë¼¨¤·¤¿¤È¤ª¤ê total ¤ò 32 bit -ÊÑ¿ô¤Ë¤·¤Æ¡¢¤³¤Î¾ò·ï¤ò +で検出できていないようである。従って、先に示したとおり total を 32 bit +変数にして、この条件を (total != 0x10000) -¤È¤¹¤ë¤³¤È¤Ç²ò·è¤Ç¤­¤ë¤è¤¦¤Ë»×¤¦¡£ +とすることで解決できるように思う。 -# ¤Á¤Ê¤ß¤Ë¡¢¤³¤Î¾ò·ï¤Ï¡Ö¥¯¥é¥Õ¥È¤ÎÉÔÅù¼°¡×¤òÀ°¿ô¤ËÊÑ·Á¤·¤¿¤â¤Î¤Ç¤¢¤ë¤é¤·¤¤¡£ -# ³¬ÁØ 16 °Ê²¼¤Î¥Ï¥Õ¥Þ¥óÌڤǤ¢¤ì¤Ð¡¢ +# ちなみに、この条件は「クラフトの不等式」を整数に変形したものであるらしい。 +# 階層 16 以下のハフマン木であれば、 # # total == 2^16(0x10000) # -# ¤Ïɬ¤ºÀ®¤êΩ¤Á¡¢¤Þ¤¿¡¢ +# は必ず成り立ち、また、 # # total < 2^16 # -# ¤Ç¤¢¤ì¤Ð¡¢¤³¤ÎÌڤϾéŤÊÉä¹æ¤ò³ä¤êÅö¤Æ¤Æ¤¤¤ë¤³¤È¤ò¼¨¤¹¤½¤¦¤À¡£¤½¤·¤Æ¡¢ +# であれば、この木は冗長な符号を割り当てていることを示すそうだ。そして、 # # total > 2^16 # -# ¤Ï¡¢°ì°Õ¤ÊÉü¹æ¤¬¤Ç¤­¤Ê¤¤¤³¤È¤ò¼¨¤¹¡£ +# は、一意な復号ができないことを示す。 -Ç°¤ò²¡¤·¤Æ¤ª¤¯¤¬¡¢¥»¥­¥å¥ê¥Æ¥£¥Ñ¥Ã¥Á¤¬´Ö°ã¤Ã¤Æ¤¤¤ë¤ï¤±¤Ç¤Ï¤Ê¤¤¡£¥»¥­¥å -¥ê¥Æ¥£¥Ñ¥Ã¥Á¤È¤·¤Æ¤Ï¥Ð¥Ã¥Õ¥¡¥ª¡¼¥Ð¥Õ¥í¡¼¤òËɤ²¤ÐÎɤ¤¤Î¤Çñ¤Ë¤½¤Î¤¿¤á¤Î -¥Á¥§¥Ã¥¯¤òÆþ¤ì¤¿¤À¤±¤Ç¤¢¤ë¡£¤¿¤À¤·¡¢¥×¥í¥°¥é¥Þ¤ÎΩ¾ì¤È¤·¤Æ¤Ï¥»¥­¥å¥ê¥Æ¥£ -¥Ñ¥Ã¥Á¤ÏÂоÉÎÅË¡¤Ê½¤Àµ¤Ç¤·¤«¤Ê¤¯¡¢º¬ËÜŪ¤ÊÌäÂêÅÀ¤ò²ò·è¤·¤Æ¤¤¤Ê¤¤¾ì¹ç¤¬ -¤¢¤ë¤È¤¤¤¦¤³¤È¤ò³Ð¤¨¤Æ¤ª¤¯É¬Íפ¬¤¢¤ë¡£¤È»×¤¦¡£ +念を押しておくが、セキュリティパッチが間違っているわけではない。セキュ +リティパッチとしてはバッファオーバフローを防げば良いので単にそのための +チェックを入れただけである。ただし、プログラマの立場としてはセキュリティ +パッチは対症療法な修正でしかなく、根本的な問題点を解決していない場合が +あるということを覚えておく必要がある。と思う。 -¤³¤Î¥»¥­¥å¥ê¥Æ¥£¥Ð¥°¤ÎÊó¹ð¤ò¼õ¤±¤Æ¡¢gzip ¤È¤·¤Æ¤É¤Î¤è¤¦¤Ë½¤Àµ¤·¤Æ¤¤¤ë¤« -¤ò³Îǧ¤·¤Æ¤ß¤¿¡£°Ê²¼¤Ï¡¢ÌäÂ꤬ȯ¸«¤µ¤ì¤¿ gzip-1.3.5 ¤È¤½¤Î½¤Àµ¤¬¹Ô¤ï¤ì -¤¿ gzip-1.3.6 ¤È¤Îº¹Ê¬(unlzh.c ¤Î¤ß)¤Ç¤¢¤ë¡£ +このセキュリティバグの報告を受けて、gzip としてどのように修正しているか +を確認してみた。以下は、問題が発見された gzip-1.3.5 とその修正が行われ +た gzip-1.3.6 との差分(unlzh.c のみ)である。 --- gzip-1.3.5/unlzh.c 1999-10-06 14:00:00.000000000 +0900 +++ gzip-1.3.6/unlzh.c 2006-11-20 17:40:34.000000000 +0900 @@ -7077,14 +7077,14 @@ perl -e 'print "\x1f\xa0","\xab\xcd","\x9e\x40\x01\xc2\xcc\x36\x0c\x92","\xc8"," +#define NPT (1 << TBIT) -¶²¤é¤¯¡¢NT ¤òĶ¤¨¤ëÃͤ¬°µ½Ìʸ¤ËËä¤á¹þ¤Þ¤ì¤Æ¤â¥Ð¥Ã¥Õ¥¡¥ª¡¼¥Ð¡¼¥Õ¥í¡¼¤·¤Ê -¤¤¤è¤¦¤Ë¤¹¤ë¤¿¤á¤Ë pt_len ¤Î¥Ð¥Ã¥Õ¥¡¥µ¥¤¥º¤òÂ礭¤¯¤·¤¿¤Î¤À¤í¤¦(½ÅÍפʲþ -½¤ÅÀ¤Î£³ÅÀÌܤòÊ̤ÊÊýË¡¤Ç²ò·è¤·¤Æ¤¤¤ë¡£¸Ä¿ÍŪ¤Ë¤Ï¤³¤Î¤è¤¦¤ÊÂнè¤Ï¹¥¤ß¤Ç -¤Ê¤¤¡£°Õ¿Þ¤¬¤ï¤«¤ê¤Ë¤¯¤¤¤«¤é¤À) +恐らく、NT を超える値が圧縮文に埋め込まれてもバッファオーバーフローしな +いようにするために pt_len のバッファサイズを大きくしたのだろう(重要な改 +修点の3点目を別な方法で解決している。個人的にはこのような対処は好みで +ない。意図がわかりにくいからだ) -c_len ¤ÎÎΰ襵¥¤¥º(NC)¤Ï¤È¤¤¤¦¤È¡¢gzip ¤Ç¤Ï¸µ¡¹ c_len ¤Î¥Ð¥Ã¥Õ¥¡¥µ¥¤¥º -¤Ï NC ¤è¤ê¤âÂ礭¤¤(8192 or 16384)¡£¤É¤¦¤ä¤é¾¤ÎÊÑ¿ô¤ò»È¤¤²ó¤·¤·¤Æ¤¤¤ë¤¿ -¤á¤Ë¤³¤Î¤è¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤ë¤è¤¦¤À¡£ +c_len の領域サイズ(NC)はというと、gzip では元々 c_len のバッファサイズ +は NC よりも大きい(8192 or 16384)。どうやら他の変数を使い回ししているた +めにこのようになっているようだ。 /* local ush left[2 * NC - 1]; */ /* local ush right[2 * NC - 1]; */ @@ -7095,7 +7095,7 @@ c_len - error("Bad table\n"); + gzip_error ("Bad table\n"); -¤³¤³¤ÎȽÄê(Ìڤι½ÃÛ¤¬Àµ¤·¤¤¤«¤É¤¦¤«)¤ÏÊѤ¨¤Ê¤«¤Ã¤¿¤è¤¦¤À¡£ +ここの判定(木の構築が正しいかどうか)は変えなかったようだ。 jutbits = 16 - tablebits; for (i = 1; i <= (unsigned)tablebits; i++) { @@ -7109,9 +7109,9 @@ c_len } else { k = start[len]; -Âå¤ï¤ê¤Ë¡¢table[] ¤ÎÎΰèÈϰϤòĶ¤¨¤ëÉä¹æ¤¬¸½¤ì¤¿¾ì¹ç¤Ë¥¨¥é¡¼¤Ë¤Ê¤ë¤è¤¦ -¤Ë¤·¤Æ¤¤¤ë(½ÅÍפʲþ½¤ÅÀ¤Î£²ÅÀÌÜ¡£¤Á¤ã¤ó¤È¡¢c_len ¤À¤±¤Ç¤Ê¤¯¡¢pt_len ¤Î -¾ì¹ç¤â¥Á¥§¥Ã¥¯¤µ¤ì¤ë¤³¤È¤Ë¤Ê¤ë)¡£ +代わりに、table[] の領域範囲を超える符号が現れた場合にエラーになるよう +にしている(重要な改修点の2点目。ちゃんと、c_len だけでなく、pt_len の +場合もチェックされることになる)。 @@ -223,6 +221,8 @@ local void read_pt_len(nn, nbit, i_speci if (c == 7) { @@ -7123,17 +7123,17 @@ c_len fillbuf((c < 7) ? 3 : c - 3); pt_len[i++] = c; -p_len, t_len ¤Ë¤Ä¤¤¤Æ Huffman Éä¹æŤè¤êÂ礭¤¤Ãͤ¬Éü¹æ¸ì¤È¤Ê¤ë¾ì¹ç¤Ë¥¨ -¥é¡¼¤È¤·¤Æ¤¤¤ë¡£(½ÅÍפʲþ½¤ÅÀ¤Î£±ÅÀÌÜ) +p_len, t_len について Huffman 符号長より大きい値が復号語となる場合にエ +ラーとしている。(重要な改修点の1点目) -¥Ñ¥Ã¥Á¤Ç¤Ï¡¢make_table() Æâ¤Ç¥Á¥§¥Ã¥¯¤·¤Æ¤¤¤¿¤È¤³¤í¤ò¼ÂºÝ¤ËÃͤòÆɤ߹þ¤à -²Õ½ê¤Ë°Ü¤·¤¿¤Î¤À¤í¤¦¡£¤½¤·¤Æ¡¢c_len ¤Î¾ì¹ç¤Ï¡¢t_len ¤ÎÉü¹æ¤ÇÌڤι½ÃÛ -¥Á¥§¥Ã¥¯¤Ë¤Æ¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤Ê¤±¤ì¤ÐÌäÂê¤Ê¤¤¤È¤ß¤Ê¤·¤Æ¤¤¤ë¤Î¤À¤í¤¦¤È»× -¤¦¡£ +パッチでは、make_table() 内でチェックしていたところを実際に値を読み込む +箇所に移したのだろう。そして、c_len の場合は、t_len の復号で木の構築 +チェックにてエラーが検出されなければ問題ないとみなしているのだろうと思 +う。 -¤É¤¦¤ä¤éÊý¿Ë¤È¤·¤Æ¤ÏºÇÄã¸Â¤Î¥Á¥§¥Ã¥¯¤ÇºÑ¤Þ¤»¤Æ¤¤¤ë¤é¤·¤¤¡£¤µ¤¹¤¬¤Ë¥¢¥ë -¥´¥ê¥º¥à¤ò½ÏÃΤ·¤¿¾å¤Ç¤Î½¤Àµ¤Î¤è¤¦¤Ë¸«¤¨¤ë¤¬¡¢¤³¤ì¤ÇÎɤ¤¤Î¤«¤¤¤Þ¤Ò¤È¤Ä -³Î¿®¤¬»ý¤Æ¤Ê¤¤¡£¤Þ¤¢¡¢gzip ¤Ë¤Ä¤¤¤Æ¤Ï¤³¤ì°Ê¾å¤Ï¿¨¤ì¤Ê¤¤¤Ç¤ª¤³¤¦¡£ +どうやら方針としては最低限のチェックで済ませているらしい。さすがにアル +ゴリズムを熟知した上での修正のように見えるが、これで良いのかいまひとつ +確信が持てない。まあ、gzip についてはこれ以上は触れないでおこう。 # Local Variables: diff --git a/README.jp.md b/README.jp.md new file mode 100644 index 0000000..8064d74 --- /dev/null +++ b/README.jp.md @@ -0,0 +1,478 @@ +LHa for UNIX with Autoconf +======================= + +このファイルは、Autoconf 版 LHa for UNIX について説明します。 + +1. コンパイルの手順 +-------------------------------- + +コンパイルの手順は以下のようになります。 + +``` + gzip -dc lha-1.14i-acXXXXXXXX.tar.gz | tar xvf - + cd lha-114i-acXXXXXXXX + + sh ./configure + make + make check + make install +``` + +MinGW 対応はα版です。ほとんどテストされていません(make check が成功 +する程度)。Cygwin 環境で MinGW 版を試すには + +``` + sh ./configure CC='gcc -mno-cygwin' + --build=i686-pc-mingw32 + --with-tmp-file=no +``` + +などとしてください。 + +※ Cygwin や MinGW 等、Windows 環境では深いディレクトリでコンパイル + するとmake check が失敗する場合があります。これは長いパスの格納 + チェックをするときに、Windows のフルパス長の制限にひっかかるため + です。このような場合は、以下のように configure を浅めのディレクト + リで実行してください。 + +``` + 例: + cd /tmp/build + sh ~/src/lha/configure .... + make + make check + make install +``` + +autoconf/automake がインストールされている場合で、lha ソースや +configure.ac, Makefile.am をメンテナンスする場合は以下の手順になりま +す。autoconf/automake のバージョンはそれぞれ autoconf 2.5x, automake +1.6.x 以降での利用を前提としています。 + +``` + gzip -dc lha-1.14i-acXXXXXXXX.tar.gz | tar xvf - + cd lha-114i-acXXXXXXXX + + aclocal + automake -a + autoheader + autoconf + + # aclocal から autoconf 実行までの手順は、最近では、autoreconf -is + # でいけるようです。 + + sh ./configure + make + make check + make install +``` + +2. オリジナルからの変更点 +---------------------------------------- + +Autoconf 版 LHa for UNIX は、 +LHa for UNIX ver1.14i +を元に改変されました。 + +主な変更点を下記に挙げます。 + +### -lh6-, -lh7- メソッドのアーカイブ作成 + +オリジナルの LHa for UNIX 1.14i では、SUPPORT_LH7 の定義をせずにコン +パイルした場合、-lh6- および -lh7- メソッドのアーカイブを作成できま +せんでした。このことは別に構わないのですが SUPPORT_LH7 を定義すると +デフォルトで -lh7- メソッドのアーカイブ作成を強制されてしまいます。 +そこで、もう少し柔軟に lha 利用者がこれらを選択できるよう +SUPPORT_LH7 は常に定義するようにし、デフォルトで作成されるアーカイブ +のメソッド指定を configure オプションの --with-default-method=[567] +で指定できるようにしました。 + +このオプションの省略値は -lh5- です。つまりデフォルトでは -lh5- アー +カイブを作成します。(そして、上で述べた通り、lhaの o6 または o7 オプ +ションによりいつでも-lh6-、-lh7- アーカイブを作成でき、configure オ +プションによりデフォルトの挙動を変更することができます) + +### アーカイブ中の漢字ファイル名 + +オリジナルの LHa for UNIX 1.14i はアーカイブに格納するファイル名の漢 +字コードに関して無頓着です。コンパイル時に MULTIBYTE_CHAR を定義した +ときでもアーカイブ中の Shift JIS ファイル名を EUC にすることもなく、 +EUC コードのまま(正確にはシステムの漢字コードのまま)アーカイブに格納 +したりします。 + +autoconf 版では、configure オプション --enable-multibyte-filename に +より漢字ファイル名が使用でき、アーカイブに格納されるファイル名の漢字 +コードを SJIS 固定として扱います。 + +--enable-multibyte-filename の引数(システムのファイル名の漢字コード +指定)は、以下の通りです。 + +``` + --enable-multibyte-filename=sjis + システムの漢字コードを SJIS として扱います。 + --enable-multibyte-filename=euc + システムの漢字コードを EUC として扱います。 + --enable-multibyte-filename=utf8 + システムの漢字コードを UTF-8 として扱います。 + 今のところ Mac OS X でだけこのオプションをサポートします。 + --enable-multibyte-filename=auto (または yes または引数なし) + システムの漢字コードを自動で判別します。自動といっても現状は、 + Cygwin, MinGW, HP-UX の場合に SJIS、Mac OS X の場合 UTF-8、 + それ以外を EUC とみなすだけです。 + --enable-multibyte-filename=no + --disable-multibyte-filename + ファイル名のマルチバイトサポートを無効にします。 +``` + +デフォルトは、auto です。 + +lha のコマンドラインオプションにより、コンパイル時のデフォルト指定を +変更することができます。このコマンドラインオプションは、GNU style の +long option (ダッシュ2つが先行するスタイル)で指定します。 + +``` + --system-kanji-code=xxx + システムのファイル名の漢字コードを指定します。 + + --archive-kanji-code=xxx + アーカイブ内へ格納するときのファイル名のコードを指定し + ます。これは通常 SJIS 固定なので変更するべきではありま + せん。 +``` + +xxx は sjis, euc, utf8, cap のいずれかです。cap は、samba などで使われる +コードで、漢字コードを ":" と 16 進文字で表現するコードです。 + +例えば、 + +``` + $ touch 漢字 + $ lha c foo.lzh 漢字 + $ lha l foo.lzh + + PERMISSION UID GID SIZE RATIO STAMP NAME + ---------- ----------- ------- ------ ------------ -------------------- + -rw-r--r-- 1000/1000 0 ****** Mar 23 21:23 漢字 + ---------- ----------- ------- ------ ------------ -------------------- + Total 1 file 0 ****** Mar 23 21:23 + + $ lha l --system-kanji-code=cap foo.lzh + + PERMISSION UID GID SIZE RATIO STAMP NAME + ---------- ----------- ------- ------ ------------ -------------------- + -rw-r--r-- 1000/1000 0 ****** Mar 23 21:23 :8a:bf:8e:9a + ---------- ----------- ------- ------ ------------ -------------------- +``` + +などとなります。 + +Mac OS X 用の utf8 <-> sjis 変換は、2002/6 に坂井浩人さんに作成して +いただきました。ありがとうございます。 + +また、Mac OS X 以外でも、iconv ライブラリを使用すれば、UTF-8 を使用す +ることができます。ただし、現在のところ iconv ライブラリの存在を自動的 +に検出しないので、libc に iconv() 関数がなく、libiconv が存在する +(iconvライブラリをリンクする必要がある)環境では、コンパイル時に + +``` + sh ./configure LIBS=-liconv +``` + +などとする必要があります。 + +### 標準入力からの展開ファイルの指定 + +オリジナルの LHa for UNIX 1.14i は、 + +``` + echo foo.txt | lha x foo.lzh + find bar -name '*.[ch]' | lha c bar.lzh +``` + +とすると、foo.lzh から foo.txt だけを展開したり、bar ディレクトリ配 +下のファイルを bar.lzh に格納したりできます。つまり、標準入力から、 +圧縮/展開ファイルを指定できる機能なのですが、いまいち使い道がないわ +りに邪魔な機能です(圧縮の例は、cpio 同様まあ使えるけど、このためのオ +プションを新設するのが良いと考えています) + +tty からの利用しか想定してないと思われますが、おそらく daemon から +lha を実行する場合などで意図しない動作をするでしょう。そういうわけで +勝手ながらこの機能は削除しました。以下のような事ができない Windows +環境では意味のある機能だったかもしれませんが + +``` + lha x bar.lzh `echo foo.txt` +``` + +残念ながら MinGW で isatty() がうまく動作しませんでしたから Windows +では使えないのでした。 + +オリジナルの仕様を復活させたい場合は、lharc.c の 568行目付近の #if 0 +を #if 1 にしてください。 + +### 拡張ヘッダ(ユーザ名/グループ名)のサポート + +ユーザ名、グループ名の拡張ヘッダ(0x52, 0x53)を作成できるようにしまし +た(デフォルトはoff)。詳細は header.doc.jp を参照してください。展開 +と一覧表示のときにヘッダにこの情報があれば ID に優先してこの情報が利 +用されます。作成は configure オプション --enable-user-name-header を +指定して build した場合に有効になります。 + +### バックアップファイル作成の抑止 + +オリジナル LHa for UNIX 1.14i では、アーカイブにファイルを追加したと +きやアーカイブからファイルを削除したときに、元のアーカイブを .bak と +いう拡張子で保存します。この挙動は煩わしく感じたので autoconf 版では +バックアップファイルを作成しないようにしました。この autoconf 版パッ +チが信用できないような人は configure オプション --enable-backup-archive +を指定して build してください(ぜひそうするべきです:p)。オリジナルと +同じ挙動になります。 + +### header.c の書き換え + +header.c は作り変えられました。上記に示した変更に加えてオリジナル +LHa for UNIX 1.14i から以下の不具合が修正されました。 + +#### level 2 header のバグ + +total header size (アーカイブヘッダの先頭 2 byte) が 256 以上 +であるアーカイブを正しく読むことができませんでした。また、total +header size がちょうど 256 になるような不正なアーカイブを作成し +ていました。LHA のヘッダ仕様ではヘッダ先頭が 0 であればアーカイ +ブの終端とみなすため total header size を 256 の倍数にできません。 +(256 などは little-endian で 0x00 0x01 となるため、先頭が 0 にな +ります。) + +読み込み時にヘッダの CRC check を行うようにしました。 + +#### level 1 header のバグ + +ファイル名に対して拡張ヘッダを使用することがないため、230 バイト +を越えるファイル名(ディレクトリを含まない)をアーカイブに書くとアー +カイブヘッダのサイズ制限を越えた不正なアーカイブが作成されていま +した。 + +#### level 0 header のバグ + +長いパス名(ディレクトリも含む)に対してアーカイブヘッダのサイズ制 +限を越えた不正なアーカイブが作成されていました(実際には、オリジ +ナルは level 0 header にディレクトリの情報を一切書かないのでこの +制限はやはりファイル名長だけが対象になります)。autoconf 版では制 +限を越えたパス名は warning メッセージを出力し、パス名の後ろを +切り詰めます。(level 0 header は使用するべきではありません) + +空の(ディレクトリ名情報のない) -lhd- ヘッダが作成されていました。 + +``` + $ mkdir foo + $ lha c0 foo.lzh foo + $ lha v foo.lzh + PERMSSN UID GID PACKED SIZE RATIO METHOD CRC STAMP NAME + ---------- ----------- ------- ------- ------ ---------- ------------ ------------- + drwxrwxr-x 1000/1000 0 0 ****** -lhd- 0000 Jul 29 00:18 + ---------- ----------- ------- ------- ------ ---------- ------------ ------------- + Total 1 file 0 0 ****** Jul 29 00:18 +``` + +なお、level 0 header で -lhd- method は使えないという説がある + + + +のですが、吉崎栄泰氏のオリジナル LHA (DOS/Windows版) (ver 2.55, +2.67) などは -lhd- method を level 0 header で作成します。 + +※ この意見は認められ()、 + 上記のドキュメントは、訂正されたようです。 + + + +#### g オプションを付けたときの level 0, 1, 2 header + +g オプションは、アーカイブ作成のとき UNIX 固有の情報をアーカイブ +に作成するのを抑止すると man にはあるのですが、実際にはディレク +トリ情報まで抑止されていました。 + +``` + $ mkdir foo + $ touch foo/bar + $ lha cg1 foo.lzh foo + $ lha foo.lzh + PERMSSN UID GID SIZE RATIO STAMP NAME + ---------- ----------- ------- ------ ------------ -------------------- + [generic] 0 ****** Jul 29 00:02 bar + ---------- ----------- ------- ------ ------------ -------------------- + Total 1 file 0 ****** Jul 29 00:02 +``` + +autoconf 版では上記は foo/bar になります。(オリジナルはわざとそ +うしていたのかもしれませんが、そうする理由はないと判断しました) +g オプションで -lhd- の作成が抑止されるのは同じです。 + +なお、g オプションとヘッダレベルの指定を同時に行うときは上記のよ +うに g オプションを先に指定する必要があります。lha c1g など g オ +プションを後に指定すると level 0 header が作成されます(このオリ +ジナル仕様はちょっとわかりにくいです)。 + +### level 3 header + +世の中には、level 3 header というものが存在するようですが、まだ仕様 +としてfix されてないようなので*読み込みのみ*サポートしました。追加の +拡張ヘッダは未対応です。(特に対応すべきヘッダが見当たらなかった) +largefile 対応する場合は、この level 3 header をサポートした方が良さ +そうです。 + +### ヘッダのダンプ + +まったくのおまけ機能としてヘッダのダンプ機能を追加しました。これは完 +全にデバッグ用です。 + +``` + lha vvv foo.lzh +``` + +とすると、アーカイブの内容一覧にまざってダンプが出力されます。 + +### デフォルトヘッダレベル + +アーカイブを作成するときのデフォルトのヘッダレベルを 2 にしました。 +(オリジナルの LHa for UNIX 1.14i ではレベル 1 がデフォルト) + +### 拡張ヘッダ + +拡張ヘッダ Windows timestamp (0x41) を解釈するようにしました。(level +1 header のみ)。level 2 以上では、基本ヘッダに time_t の情報があるの +で、拡張ヘッダの方は無視します。 +level 1 header のアーカイブに対して、Windows timestamp 拡張ヘッダ +を出力する LHA アーカイバが存在するかどうかは未確認です。あまり、 +役に立たない修正だった気がしますがせっかく作ったので残してます:-) + +### -x オプション + +圧縮対象のファイルから除外されるパターンを指定する -x オプションを追 +加しました。これに伴い、 + +``` + lha c -x '*.o' -x='*.a' -x'*.c' src.lzh src +``` + +といった指定ができるよう、オプション解析部は変更されました。 +本バージョンの usage は以下のようになります。 + +``` + usage: lha [-][] [- ...] archive_file [file...] +``` + +### Cygwin での解凍 + +MS-DOS タイプなど permission の情報を持たないアーカイブを Cygwin で +解凍する場合は、0777 & ~umask で展開するようにしました。これは、.exe +や .dll に実行属性を付けるためです。 + +### large files 対応 + +システムが対応していれば、2G over な large file を扱うことができます +(configure が適当なコンパイラオプションを指定してくれます) + +ただし、HP-UX 11.0 で large files に対応するには以下のように + +``` + CC="cc -Ae +DA2.0W" +``` + +と指示してあげる必要があるようです。 + +``` + ./configure --with-tmp-file=no CC="cc -Ae +DA2.0W" \ + ac_cv_have_mktime=yes \ + ac_cv_func_mktime=yes +``` + +--with-tmp-file=no は、中間ファイルを出力先と同じディレクトリに +作成します。テンポラリディレクトリが 2G over をサポートしていない +場合を考慮しています。 + +(largefiles 対応とは関係ありませんが ac_cv_*=yes は、HP-UX ではなぜ +か mktime の判定に失敗するため強制的に mktime を使うようにしています) + +もし large files 対応を「無効」にしたければ、 + +``` + ./configure --disable-largefile +``` + +のようにします。 + +なお、正規の LHA では、level 0, 1, 2 ヘッダの仕様上 4G 未満のファイル +しか書庫に格納できません(ファイルサイズを格納する領域が 4 bytes しかない)。 + +しかし、UNLHA32.DLL などは、拡張ヘッダ(0x42)により、4G over なファイ +ルも扱えるようになっています。autoconf 版では、今のところ展開のときの +みこの拡張ヘッダを参照して、4G overファイルをサポートします。 + +(作成に対応していないのは、安易にUNLHA32.DLLに従うことが正しいのかよ +くわからなかったからです。) + +### MacBinaryつきアーカイブのサポート + +MacLHAで「MacBinary」チェックボックスをONにして作成したアーカイブに +格納されているファイルは、MacBinaryエンコードされています。 +解凍時に -b オプションを指定すると、解凍後にMacBinaryデコードを行い、 +データフォークのみを取り出します(リソースフォークは無視されます)。 +また、普通のアーカイブに対して b オプションを指定して解凍した場合は +通常の解凍処理が行われます。 + +この機能を利用するにはapplefileライブラリが必要です。 +applefileライブラリは下記より入手できます。 + + http://sourceforge.net/projects/applefile/ + +3. バグ +----------- + +### 壊れたアーカイブの展開 (--extract-broken-archive) + +LHa for UNIX (autoconf版)は、バージョン 1.14i-ac20030713 (slide.c +revision 1.20) より壊れたアーカイブを作成してしまう致命的なバグがあ +りました。(このようなアーカイブが作成される現象に遭遇することはほと +んどないかも知れません。ただ、バグのある LHa for UNIX では正常に展開 +できてしまうので、壊れたアーカイブであることに気が付いてないだけかも +しれません) + +この壊れたアーカイブは他の正常な LHA (あるいは現在の LHa for UNIX) +では展開時に CRC エラーが発生してしまいます。 + +現在のバージョンでは、 + +``` + lha x --extract-broken-archive broken.lzh +``` + +とオプション --extract-broken-archive を指定することで、このバグによ +り作成された壊れたアーカイブを強制的に展開することができます。CRC エ +ラーが発生するアーカイブを見付けたときには(そして、それが過去の LHa +for UNIX (autoconf版)で作成されたものである場合には)このオプションを +試してみてください。 + +4. 再配布について +--------------------------- + +私は、src/header.c にはもはや元の lha 1.14i にあったコードは含まれて +いないと考えています。src/header.c は私の著作物です。ただし、lha +1.14i にあったコードが参考になったことは事実です。敬意を表する意味で +も src/header.c にあった歴代の改変者の名前はそのまま残しています。 + +これの意図する所は、man/lha.man にある再配布条件を src/header.c に適 +用しないことです。私は、より自由で使いやすいコードを LHa for UNIX の +構成物にしようと考えています。src/header.c に適用するライセンスはま +だ未定です(なので、現状は LHa for UNIX の再配布条件が適用されると考 +えてください)。 + + ライセンス候補(メモ): + + + +現在のところ、src/vsnprintf.c, src/fnmatch.[ch], src/getopt_long.[ch] +を除くその他の構成物や LHa for UNIX 全体には man/lha.man に含まれる +条項が適用されます。(vsnprintf.c, fnmatch.c を利用するように make し +た LHa for UNIX には、各ソースに記述された条項も適用されることに注意 +してください) diff --git a/header.doc.euc b/header.doc.euc deleted file mode 100644 index 9b28ec7..0000000 --- a/header.doc.euc +++ /dev/null @@ -1,189 +0,0 @@ -/* header.doc (In Japanese) */ -LHa for UNIX ¤Ç»ÈÍѤµ¤ì¤ë¥Ø¥Ã¥À¹½Â¤¤Ë¤Ä¤¤¤Æ Mar. 2, 1992, Masaru Oki. - ----------------------------------------------------------------- - ËܥС¼¥¸¥ç¥ó¤Ç¤Ï²áÅÏŪ¤ÊÁ¼Ã֤Ȥ·¤Æ3¼ïÎà¤Î¥Ø¥Ã¥À·Á¼°¤òÍÑ°Õ¤·¡¢level-1 - ¤ò´ûÄêÃͤȤ·¤Æ¤¤¤Þ¤¹¤¬¡¢¾­ÍèŪ¤Ë¤Ï¥Ñ¥¹Ì¾¤Îʸ»ú¿ô¤ËÀ©¸Â¤Î¤Ê¤¤ level-2¤Ë - Åý°ì¤¹¤ëÊý¿Ë¤Ç¤¹¡£ - - - A. ¥Ø¥Ã¥À¤Î»ÅÍÍ - ------------------------------------------------------------------------------ - level-0 level-1 level-2 ------------------------------------------------------------------------------ - 1 header size 1 header size 2 total header size - 1 header sum 1 header sum - 5 method ID 5 method ID 5 method ID -´ð 4 packed size 4 skip size 4 packed size - 4 original size 4 original size 4 original size -ËÜ 2 time 2 time 4 time(UNIX type) - 2 date 2 date -Éô 1 attribute 1 0x20 1 RESERVED - 1 level 0x00 1 level 0x01 1 level 0x02 -ʬ 1 name length 1 name length - ? pathname ? filename - 2 file crc 2 file crc 2 file crc - . ........ 1 OS ID 'U' 1 OS ID 'U' - . ........ - 2 next-header size 2 next-header size - ************************************************************************* - 24 + ? 27 + ? 26 ------------------------------------------------------------------------------ -³È 1 ext-type 1 ext-type -Ä¥ . ........ . ........ -Éô 2 next-header size 2 next-header size -ʬ ------------------------------------------------------------------------------ - - a. ¥Ø¥Ã¥À¤Î¼ïÎà - - level-0 ¥Ø¥Ã¥À - ½¾Íè¤Î LHarc, LArc ¤ÈƱ¤¸·Á¼°¤Ç¤¹¡£¥Ç¥£¥ì¥¯¥È¥ê̾¤Î¶èÀÚ¤ê¤Ï '\' - ¤òɸ½à¤È¤·¤Æ¤¤¤Þ¤¹¡£ - - level-1 ¥Ø¥Ã¥À - ´ûÄêÃͤǤ³¤Î¥Ø¥Ã¥À¤¬ºîÀ®¤µ¤ì¤Þ¤¹¡£-x0 ¤ÇºîÀ®¤µ¤ì¤¿ -lh0- ¤Î½ñ¸Ë - ¤Ï LHarc ¤Ç²òÅà²Äǽ¤Ç¤¹¤¬¡¢²òÅà»þ¤Ë CRC ¥Á¥§¥Ã¥¯¤Ï¹Ô¤ï¤ì¤Þ¤»¤ó¡£ - - level-2 ¥Ø¥Ã¥À - Ť¤¥Õ¥¡¥¤¥ë̾¤ò¥µ¥Ý¡¼¥È¤¹¤ë¤¿¤á¤Î¥Ø¥Ã¥À¤Ç¤¹¡£¾­ÍèŪ¤Ë¤ÏËܥإà - ¥À¤òɸ½à¤È¤·¤¿¤¤¤Î¤Ç¡¢LH ´ØÏ¢¤Î¥æ¡¼¥Æ¥£¥ê¥Æ¥£¤òºîÀ®¤µ¤ì¤ëÊý¤Ïº£¸å - ½àµò¤·¤Æ¤¯¤À¤µ¤ë¤è¤¦¤Ë¤ª´ê¤¤¤·¤Þ¤¹¡£ - - b. Åà·ë¡¦²òÅà²Äǽ¤Ê method ID ¤Ë¤Ä¤¤¤Æ - - * ¤ÏºîÀ®²Äǽ¤Ê method - - -lh0- * no compression - - -lh1- * 4k sliding dictionary(max 60 bytes) + dynamic Huffman - + fixed encoding of position - - -lh2- 8k sliding dictionary(max 256 bytes) + dynamic Huffman - - -lh3- 8k sliding dictionary(max 256 bytes) + static Huffman - - -lh4- * 4k sliding dictionary(max 256 bytes) + static Huffman - + improved encoding of position and trees - - -lh5- * 8k sliding dictionary(max 256 bytes) + static Huffman - + improved encoding of position and trees - - -lh6- 32k sliding dictionary(max 256 bytes) + static Huffman - + improved encoding of position and trees - - -lh7- 64k sliding dictionary(max 256 bytes) + static Huffman - + improved encoding of position and trees - - -lzs- 2k sliding dictionary(max 17 bytes) - - -lz4- no compression - - -lz5- 4k sliding dictionary(max 17 bytes) - - c. OS ID ¤Ë¤Ä¤¤¤Æ - - ¸½ºß¤Î¤È¤³¤í¡¢°Ê²¼¤Î ID ¤òͽÌ󤷤Ƥ¤¤Þ¤¹¡£ - - MS-DOS 'M' - OS/2 '2' - OS9 '9' - OS/68K 'K' - OS/386 '3' - HUMAN 'H' - UNIX 'U' - CP/M 'C' - FLEX 'F' - Mac 'm' - Runser 'R' - - B. ³ÈÄ¥Éôʬ - - a. OS Èó°Í¸ (0x00 - 0x3f) - - common header - 1 0x00 - 2 header crc - ( 1 information ) - 2 next-header size - - filename header - 1 0x01 - ? filename - 2 next-header size - - dirname header - 1 0x02 - ? dirname - 2 next-header size - - comment header - 1 0x3f - ? comments - 2 next-header size - -(°Ê¾å¡¢µÈºê»á¤Î lhx.doc ¤è¤ê) ----------------------------------------------------------------- -LHa for UNIX ³ÈÄ¥¥Ø¥Ã¥À»ÅÍÍ - -¡¦°Í¸¾ðÊó¤Î¼ïÎà(³ÊǼ¤¹¤Ù¤­¾ðÊó) - UNIX ¤Ë°Í¸¤¹¤ë¾ðÊó¤Çµ­Ï¿¤¹¤Ù¤­¤Ï°Ê²¼¤Î¤â¤Î¤Ç¤¢¤ë¡£ - (1) ¥Ñ¡¼¥ß¥Ã¥·¥ç¥ó - (2) GID,UID - (3) ¥°¥ë¡¼¥×̾¡¢¥æ¡¼¥¶Ì¾ - (4) ºÇ½ªÊѹ¹»þ¹ï(UNIX time) - -¡¦°Í¸¾ðÊó¤Î¥¿¥¤¥×(ext-type) - ³ÈÄ¥¥Ø¥Ã¥À¤ÎÀèƬ1¥Ð¥¤¥È¤Ë¤Ï¾ðÊ󤬲¿¤ò¼¨¤¹¤â¤Î - ¤«¼±Ê̤¹¤ë¤¿¤á¤ÎÃͤ¬³ÊǼ¤µ¤ì¤Æ¤¤¤ë¡£ - UNIX°Í¸¾ðÊó¤È¤·¤Æ 0x50 - 0x54 ¤ò»ÈÍѤ¹¤ë¡£ - -¡¦°Í¸¾ðÊó¤Î³ÊǼÊýË¡ - ¾åµ­ (1) - (4) ¤ò¤½¤ì¤¾¤ìÊ̤Υ¿¥¤¥×¤È¤¹¤ë¡£ - - (1)¥Ñ¡¼¥ß¥Ã¥·¥ç¥ó - size ÃÍ - 1 0x50 - 2 ¥Ñ¡¼¥ß¥Ã¥·¥ç¥óÃÍ - 2 next-header size - - (2)GID,UID - size ÃÍ - 1 0x51 - 2 GID - 2 UID - 2 next-header size - - (3)-1 ¥°¥ë¡¼¥×̾ - 1 0x52 - ? ¥°¥ë¡¼¥×̾ʸ»úÎó - 2 next-header size - - (3)-2 ¥æ¡¼¥¶Ì¾ - 1 0x53 - ? ¥æ¡¼¥¶Ì¾Ê¸»úÎó - 2 next-header size - - (4) ºÇ½ªÊѹ¹»þ¹ï (for header_level1) - 1 0x54 - 4 UNIX »þ´Ö - 2 next-header size - -¡¦LHa for UNIX ver 1.14 ¤Ç¤Î¼ÂÁõ¡ÊÌʺê¡Ë - ¾åµ­(3)¤ò¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤Ê¤¤¡£ - (3)¤Î¥°¥ë¡¼¥×̾¡¢¥æ¡¼¥¶Ì¾¤Î¾ðÊó¤ò´Þ¤à½ñ¸Ë¤òºîÀ®¤¹¤ë¤³¤È - ¤Ï¤Ê¤¯¡¢Å¸³«»þ¤Ë¤Ï̵»ë¤¹¤ë¡£ - ¤Þ¤¿¡¢(4)¤Ï¥Ø¥Ã¥À¥ì¥Ù¥ë1¤Î½ñ¸Ë¤Ë¤Î¤ß´Þ¤Þ¤ì¤ë¡£ - - (3) ¤òºîÀ®¤¹¤ë¤è¤¦¤Ë¤·¤Æ¤ß¤Þ¤·¤¿¡£Å¸³«¤È°ìÍ÷ɽ¼¨¤Î¤È¤­¤Ë (3) - ¤Î¾ðÊ󤬤¢¤ì¤Ð ID ¤ËÍ¥À褷¤Þ¤¹¡£ - ºîÀ®¤Ï configure ¥ª¥×¥·¥ç¥ó --enable-user-name-header ¤ò»ØÄꤷ¤Æ - build ¤·¤¿¤È¤­¤ËÍ­¸ú¤Ë¤Ê¤ê¤Þ¤¹¡£(2002-06-04 ¿·°æ) - ----------------------------------------------------------------- -°Ê¾å¡£ - -°ìÉô²þÊÑ¡§ -Ìʺꡡ½¤Î´ (Nobutaka Watazaki) -watazaki@shimadzu.co.jp diff --git a/header.doc.jp b/header.doc.jp new file mode 100644 index 0000000..847e356 --- /dev/null +++ b/header.doc.jp @@ -0,0 +1,189 @@ +/* header.doc (In Japanese) */ +LHa for UNIX で使用されるヘッダ構造について Mar. 2, 1992, Masaru Oki. + +---------------------------------------------------------------- + 本バージョンでは過渡的な措置として3種類のヘッダ形式を用意し、level-1 + を既定値としていますが、将来的にはパス名の文字数に制限のない level-2に + 統一する方針です。 + + + A. ヘッダの仕様 + +----------------------------------------------------------------------------- + level-0 level-1 level-2 +----------------------------------------------------------------------------- + 1 header size 1 header size 2 total header size + 1 header sum 1 header sum + 5 method ID 5 method ID 5 method ID +基 4 packed size 4 skip size 4 packed size + 4 original size 4 original size 4 original size +本 2 time 2 time 4 time(UNIX type) + 2 date 2 date +部 1 attribute 1 0x20 1 RESERVED + 1 level 0x00 1 level 0x01 1 level 0x02 +分 1 name length 1 name length + ? pathname ? filename + 2 file crc 2 file crc 2 file crc + . ........ 1 OS ID 'U' 1 OS ID 'U' + . ........ + 2 next-header size 2 next-header size + ************************************************************************* + 24 + ? 27 + ? 26 +----------------------------------------------------------------------------- +拡 1 ext-type 1 ext-type +å¼µ . ........ . ........ +部 2 next-header size 2 next-header size +分 +----------------------------------------------------------------------------- + + a. ヘッダの種類 + + level-0 ヘッダ + 従来の LHarc, LArc と同じ形式です。ディレクトリ名の区切りは '\' + を標準としています。 + + level-1 ヘッダ + 既定値でこのヘッダが作成されます。-x0 で作成された -lh0- の書庫 + は LHarc で解凍可能ですが、解凍時に CRC チェックは行われません。 + + level-2 ヘッダ + 長いファイル名をサポートするためのヘッダです。将来的には本ヘッ + ダを標準としたいので、LH 関連のユーティリティを作成される方は今後 + 準拠してくださるようにお願いします。 + + b. 凍結・解凍可能な method ID について + + * は作成可能な method + + -lh0- * no compression + + -lh1- * 4k sliding dictionary(max 60 bytes) + dynamic Huffman + + fixed encoding of position + + -lh2- 8k sliding dictionary(max 256 bytes) + dynamic Huffman + + -lh3- 8k sliding dictionary(max 256 bytes) + static Huffman + + -lh4- * 4k sliding dictionary(max 256 bytes) + static Huffman + + improved encoding of position and trees + + -lh5- * 8k sliding dictionary(max 256 bytes) + static Huffman + + improved encoding of position and trees + + -lh6- 32k sliding dictionary(max 256 bytes) + static Huffman + + improved encoding of position and trees + + -lh7- 64k sliding dictionary(max 256 bytes) + static Huffman + + improved encoding of position and trees + + -lzs- 2k sliding dictionary(max 17 bytes) + + -lz4- no compression + + -lz5- 4k sliding dictionary(max 17 bytes) + + c. OS ID について + + 現在のところ、以下の ID を予約しています。 + + MS-DOS 'M' + OS/2 '2' + OS9 '9' + OS/68K 'K' + OS/386 '3' + HUMAN 'H' + UNIX 'U' + CP/M 'C' + FLEX 'F' + Mac 'm' + Runser 'R' + + B. 拡張部分 + + a. OS 非依存 (0x00 - 0x3f) + + common header + 1 0x00 + 2 header crc + ( 1 information ) + 2 next-header size + + filename header + 1 0x01 + ? filename + 2 next-header size + + dirname header + 1 0x02 + ? dirname + 2 next-header size + + comment header + 1 0x3f + ? comments + 2 next-header size + +(以上、吉崎氏の lhx.doc より) +---------------------------------------------------------------- +LHa for UNIX 拡張ヘッダ仕様 + +・依存情報の種類(格納すべき情報) + UNIX に依存する情報で記録すべきは以下のものである。 + (1) パーミッション + (2) GID,UID + (3) グループ名、ユーザ名 + (4) 最終変更時刻(UNIX time) + +・依存情報のタイプ(ext-type) + 拡張ヘッダの先頭1バイトには情報が何を示すもの + か識別するための値が格納されている。 + UNIX依存情報として 0x50 - 0x54 を使用する。 + +・依存情報の格納方法 + 上記 (1) - (4) をそれぞれ別のタイプとする。 + + (1)パーミッション + size 値 + 1 0x50 + 2 パーミッション値 + 2 next-header size + + (2)GID,UID + size 値 + 1 0x51 + 2 GID + 2 UID + 2 next-header size + + (3)-1 グループ名 + 1 0x52 + ? グループ名文字列 + 2 next-header size + + (3)-2 ユーザ名 + 1 0x53 + ? ユーザ名文字列 + 2 next-header size + + (4) 最終変更時刻 (for header_level1) + 1 0x54 + 4 UNIX 時間 + 2 next-header size + +・LHa for UNIX ver 1.14 での実装(綿崎) + 上記(3)をサポートしていない。 + (3)のグループ名、ユーザ名の情報を含む書庫を作成すること + はなく、展開時には無視する。 + また、(4)はヘッダレベル1の書庫にのみ含まれる。 + + (3) を作成するようにしてみました。展開と一覧表示のときに (3) + の情報があれば ID に優先します。 + 作成は configure オプション --enable-user-name-header を指定して + build したときに有効になります。(2002-06-04 新井) + +---------------------------------------------------------------- +以上。 + +一部改変: +綿崎 修隆 (Nobutaka Watazaki) +watazaki@shimadzu.co.jp diff --git a/man/lha.man b/man/lha.man index 045148a..ff97a86 100644 --- a/man/lha.man +++ b/man/lha.man @@ -3,51 +3,51 @@ LHA(N) Unix Programmer's Manual LHA(N) -NNNAAAMMMEEE ̾̾̾¾Î¾Î¾Î - LHa - ¹â°µ½Ì¥¢¡¼¥«¥¤¥Ð +NNNAAAMMMEEE 名名名称称称 + LHa - 高圧縮アーカイバ -SSSYYYNNNOOOPPPSSSIIISSS ·Á·Á·Á¼°¼°¼° +SSSYYYNNNOOOPPPSSSIIISSS 形形形式式式 lllhhhaaa kkkeeeyyy [ mmmooodddiiifffiiieeerrrsss ] _a_r_c_h_i_v_e__f_i_l_e [ _f_l_i_e ... ] lllhhhaaa _a_r_c_h_i_v_e__f_i_l_e -DDDEEESSSCCCRRRIIIPPPTTTIIIOOONNN ²ò²ò²òÀâÀâÀâ - LLLHHHaaa ¤Ï¸úΨ¤Î¹â¤¤°µ½Ìµ¡Ç½¤ò»ý¤Ä¥Õ¥¡¥¤¥ë¥¢¡¼¥«¥¤¥Ð¤Ç¤¹¡£ - kkkeeeyyy ¤Ëµ¡Ç½Ê¸»ú¤ò»ØÄꤷ¡¢file ¤ÎÄɲᢹ¹¿·¡¢Ãê½Ð¡¢ºï½ü¡¢°ì - Í÷ɽ¼¨¤Ê¤É¤ò¹Ô¤Ê¤¤¤Þ¤¹¡£°ú¿ô¤Ë archive_file ¤Î¤ß¤ò»ØÄꤷ¤¿ - ¾ì¹ç¤Ë¤Ï¡¢µ¡Ç½Ê¸»ú¤Ë l ¤ò»ØÄꤷ¤¿¤Î¤ÈƱÅù¤ÎÆ°ºî¤ò¹Ô¤Ê¤¤¤Þ - ¤¹¡£ - µ¡Ç½Ê¸»ú¤Ï°Ê²¼¤ÎÄ̤ê¤Ç¤¹¡£ +DDDEEESSSCCCRRRIIIPPPTTTIIIOOONNN 解解解説説説 + LLLHHHaaa は効率の高い圧縮機能を持つファイルアーカイバです。 + kkkeeeyyy に機能文字を指定し、file の追加、更新、抽出、削除、一 + 覧表示などを行ないます。引数に archive_file のみを指定した + 場合には、機能文字に l を指定したのと同等の動作を行ないま + す。 + 機能文字は以下の通りです。 - aaa Äɲá£file ¤ò archive_file ¤ËÄɲä·¤Þ¤¹¡£file ¤¬¥Ç - ¥£¥ì¥¯¥È¥ê¤Ç¤¢¤ì¤Ð¡¢¤½¤Î¥Ç¥£¥ì¥¯¥È¥ê¤Ë´Þ¤Þ¤ì¤ë¥Õ¥¡ - ¥¤¥ë¤ò¤¹¤Ù¤ÆÄɲä·¤Þ¤¹¡£ + aaa 追加。file を archive_file に追加します。file がデ + ィレクトリであれば、そのディレクトリに含まれるファ + イルをすべて追加します。 - uuu ¹¹¿·¡£file ¤¬ archive_file ¤Ë³ÊǼ¤µ¤ì¤Æ¤¤¤Ê¤¤¤«¡¢ - ¤â¤·¤¯¤Ï³ÊǼ¤µ¤ì¤Æ¤¤¤ë¤â¤Î¤¬¸Å¤±¤ì¤Ð¡¢file ¤ò - archive_file ¤ËÄɲä·¤Þ¤¹¡£ + uuu 更新。file が archive_file に格納されていないか、 + もしくは格納されているものが古ければ、file を + archive_file に追加します。 - lll ¤Þ¤Þ¤Þ¤¿¤¿¤¿¤Ï¤Ï¤Ï vvv - °ìÍ÷ɽ¼¨¡£archive_file ¤Ë³ÊǼ¤µ¤ì¤Æ¤¤¤ë file ¤Î¾ð - Êó¤ò°ìÍ÷ɽ¼¨¤·¤Þ¤¹¡£file ¤Î»ØÄ꤬¤Ê¤±¤ì¤Ð - archive_file Æâ¤ÎÁ´¤Æ¤Î¥Õ¥¡¥¤¥ë¤Î¾ðÊó¤òɽ¼¨¤·¤Þ¤¹ - ¡£v ¤ò»ØÄꤹ¤ë¤È l ¤è¤ê¤â¾Ü¤·¤¤¾ðÊó¤òɽ¼¨¤·¤Þ¤¹¡£ + lll まままたたたははは vvv + 一覧表示。archive_file に格納されている file の情 + 報を一覧表示します。file の指定がなければ + archive_file 内の全てのファイルの情報を表示します + 。v を指定すると l よりも詳しい情報を表示します。 - xxx ¤Þ¤Þ¤Þ¤¿¤¿¤¿¤Ï¤Ï¤Ï eee - Ãê½Ð¡£archive_file ¤«¤é file ¤òÃê½Ð¤·¤Þ¤¹¡£file ¤Î - »ØÄ꤬¤Ê¤±¤ì¤Ð archive_file Æâ¤ÎÁ´¤Æ¤Î¥Õ¥¡¥¤¥ë¤òÃê - ½Ð¤·¤Þ¤¹¡£Ãê½Ð¤¹¤Ù¤­¥Õ¥¡¥¤¥ë¤¬¤¹¤Ç¤Ë¸ºß¤·¤Æ¤¤¤ë¾ì - ¹ç¤Ë¤Ï¡¢½Å¤Í½ñ¤­¤·¤Æ¤¤¤¤¤«¤ÎÌ䤤¹ç¤ï¤»¤ò¹Ô¤Ê¤¤¤Þ¤¹ - ¡£ + xxx まままたたたははは eee + 抽出。archive_file から file を抽出します。file の + 指定がなければ archive_file 内の全てのファイルを抽 + 出します。抽出すべきファイルがすでに存在している場 + 合には、重ね書きしていいかの問い合わせを行ないます + 。 - ppp ÆâÍÆɽ¼¨¡£archive_file ¤«¤é file ¤ÎÆâÍƤò¼è¤ê½Ð¤· - ¡¢É¸½à½ÐÎϤؤȽÐÎϤ·¤Þ¤¹¡£file ¤Î»ØÄ꤬¤Ê¤±¤ì¤Ð¡¢ - Á´¤Æ¤ÎÆâÍƤò½ÐÎϤ·¤Þ¤¹¡£ + ppp 内容表示。archive_file から file の内容を取り出し + 、標準出力へと出力します。file の指定がなければ、 + 全ての内容を出力します。 - ddd ºï½ü¡£archive_file ¤«¤é file ¤òºï½ü¤·¤Þ¤¹¡£ + ddd 削除。archive_file から file を削除します。 - mmm °ÜÆ°¡£file ¤ò archive_file ¤ËÄɲä·¤¿¸å¡¢file ¤òºï - ½ü¤·¤Þ¤¹¡£¤³¤ì¤Ï¡¢key ¤Ë a ¤ò¡¢modifiers ¤Ë d ¤ò»Ø - Äꤷ¤¿»þ¤ÈƱ¤¸Æ°ºî¤ò¹Ô¤Ê¤¤¤Þ¤¹¡£ + mmm 移動。file を archive_file に追加した後、file を削 + 除します。これは、key に a を、modifiers に d を指 + 定した時と同じ動作を行ないます。 January 14,1997 1 @@ -57,50 +57,50 @@ DDDEEESSSCCCRRRIIIPPPTTTIIIOOONNN LHA(N) Unix Programmer's Manual LHA(N) - ccc ¿·µ¬ºîÀ®¡£archive_file ¤ò¿·¤¿¤ËºîÀ®¤·¡¢file ¤òÄɲà - ¤·¤Þ¤¹¡£ + ccc 新規作成。archive_file を新たに作成し、file を追加 + します。 - µ¡Ç½Êѹ¹Ê¸»ú modifiers ¤ò»ØÄꤹ¤ë¤³¤È¤Ë¤è¤Ã¤Æ key ¤ÎÆ°ºî¤Î - ¾ÜºÙ¤òÊѹ¹¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£modifiers ¤Ï¡¢°Ê²¼¤ÎÃ椫¤éÊ£ - ¿ô»ØÄꤹ¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ + 機能変更文字 modifiers を指定することによって key の動作の + 詳細を変更することができます。modifiers は、以下の中から複 + 数指定することができます。 - qqq<<>> ɽ¼¨¤ÎÍÞÀ©¡£ÂÐÏÃŪ¤Ê¥á¥Ã¥»¡¼¥¸¤Îɽ¼¨¤òÍÞÀ©¤·¤Þ¤¹¡£ - ¤ÎÃͤˤè¤Ã¤Æ¡¢INDICATOR ¤Îɽ¼¨ÊýË¡¤òÊѹ¹¤Ç¤­ - ¤Þ¤¹¡£ + qqq<<>> 表示の抑制。対話的なメッセージの表示を抑制します。 + の値によって、INDICATOR の表示方法を変更でき + ます。 - qqq000 oooooo............ ¤Î¤Î¤Îɽɽɽ¼¨¼¨¼¨¤ò¤ò¤ò¹Ô¹Ô¹Ô¤Ê¤Ê¤Ê¤¦¤¦¤¦¡£¡£¡£ + qqq000 oooooo............ ののの表表表示示示ををを行行行なななううう。。。 - qqq111 ¥Õ¥Õ¥Õ¥¡¥¡¥¡¥¤¥¤¥¤¥ë¥ë¥ë̾̾̾ɽɽɽ¼¨¼¨¼¨¤Î¤Î¤Î¤ß¤ß¤ß¹Ô¹Ô¹Ô¤Ê¤Ê¤Ê¤¦¤¦¤¦¡£¡£¡£ + qqq111 フフファァァイイイルルル名名名表表表示示示のののみみみ行行行なななううう。。。 - qqq222 ²¿²¿²¿¤â¤â¤âɽɽɽ¼¨¼¨¼¨¤·¤·¤·¤Ê¤Ê¤Ê¤¤¤¤¤¤¡£¡£¡£ + qqq222 何何何ももも表表表示示示しししななないいい。。。 - ¤Ê¤ª¡¢ÃͤòÆþÎϤ·¤Ê¤«¤Ã¤¿ºÝ¤Ë¤Ï¡¢q2 ¤ÈƱÅù¤Ë¤Ê¤ê¤Þ - ¤¹¡£ + なお、値を入力しなかった際には、q2 と同等になりま + す。 - vvv ɽ¼¨¤Î¾éĹ²½¡£É½¼¨¤¹¤ë¥á¥Ã¥»¡¼¥¸¤ò¾éŤˤ·¤Þ¤¹¡£ + vvv 表示の冗長化。表示するメッセージを冗長にします。 - nnn ¼Â¹Ô¤·¤Ê¤¤¡£¼ÂºÝ¤Î¹¹¿·¤äÃê½Ð¤ÎÆ°ºî¤ò¹Ô¤Ê¤¤¤Þ¤»¤ó¡£ - ¼Â¹ÔÆâÍƤγÎǧ¤Î¤¿¤á¤ËÍÑ°Õ¤µ¤ì¤Æ¤¤¤Þ¤¹¡£ + nnn 実行しない。実際の更新や抽出の動作を行ないません。 + 実行内容の確認のために用意されています。 - fff ¶¯À©Åª¤Ê¼Â¹Ô¡£¥Õ¥¡¥¤¥ë¤ÎÃê½Ð»þ¤Ë¡¢Â¸ºß¤¹¤ë¥Õ¥¡¥¤¥ë - ¤Îºï½ü³Îǧ¤ò¹Ô¤Ê¤ï¤º¶¯À©Åª¤Ëºï½ü¤ò¹Ô¤Ê¤¤¤Þ¤¹¡£ + fff 強制的な実行。ファイルの抽出時に、存在するファイル + の削除確認を行なわず強制的に削除を行ないます。 - ttt ¥Æ¥­¥¹¥È¥â¡¼¥É¤Ë¤è¤ë³ÊǼ,Ãê½Ð¡£¥¢¡¼¥«¥¤¥Ö¥Õ¥¡¥¤¥ë - ¤Ø¤Î¥Õ¥¡¥¤¥ë¤Î³ÊǼ»þ¤Ë¤Ï¡¢UNIX ¤«¤é MS-DOS ¤Ø¤Î²þ - ¹Ô¥³¡¼¥ÉÊÑ´¹¤ò¹Ô¤Ê¤¤¤Þ¤¹¡£¥Õ¥¡¥¤¥ë¤ÎÃê½Ð»þ¤Ë¤ÏµÕ¤Ë - ¡¢MS-DOS ¤«¤é UNIX ¤Ø¤Î²þ¹Ô¥³¡¼¥ÉÊÑ´¹¤ò¹Ô¤Ê¤¤¤Þ¤¹ - ¡£ + ttt テキストモードによる格納,抽出。アーカイブファイル + へのファイルの格納時には、UNIX から MS-DOS への改 + 行コード変換を行ないます。ファイルの抽出時には逆に + 、MS-DOS から UNIX への改行コード変換を行ないます + 。 - ooo{{{nnnuuummm}}} LHarc ¸ß´¹¥¢¡¼¥«¥¤¥Ö¤ÎÀ¸À®¡¢¤Þ¤¿¤Ï°µ½Ì¥¢¥ë¥´¥ê¥º¥à - ¤ò»ØÄꤹ¤ë¡£µ¡Ç½Ê¸»ú a, u, m ¤È¶¦¤Ë»ØÄꤷ¡¢Ãê½Ð»þ - ¤Ë¤Ï̵»ë¤µ¤ì¤Þ¤¹¡£ + ooo{{{nnnuuummm}}} LHarc 互換アーカイブの生成、または圧縮アルゴリズム + を指定する。機能文字 a, u, m と共に指定し、抽出時 + には無視されます。 - ooo LLLHHHaaarrrccc ¸ß¸ß¸ß´¹´¹´¹¥¢¥¢¥¢¡¼¡¼¡¼¥«¥«¥«¥¤¥¤¥¤¥Ö¥Ö¥Ö¤Î¤Î¤ÎÀ¸À¸À¸À®À®À®¤ò¤ò¤ò¹Ô¹Ô¹Ô¤¦¤¦¤¦¡£¡£¡£ + ooo LLLHHHaaarrrccc 互互互換換換アアアーーーカカカイイイブブブののの生生生成成成ををを行行行ううう。。。 - ¸Å¤¤·Á¼°¤Î¥¢¡¼¥«¥¤¥Ö¤òÀ¸À®¤·¤Þ¤¹¡£°µ½Ì¥¢¥ë¥´ - ¥ê¥º¥à¤Ï-lh1-¤ò»ÈÍѤ·¤Þ¤¹¡£ + 古い形式のアーカイブを生成します。圧縮アルゴ + リズムは-lh1-を使用します。 @@ -111,51 +111,51 @@ LHA(N) Unix Programmer's Manual LHA(N) LHA(N) Unix Programmer's Manual LHA(N) - ooo555 °µ°µ°µ½Ì½Ì½Ì¥¢¥¢¥¢¥ë¥ë¥ë¥´¥´¥´¥ê¥ê¥ê¥º¥º¥º¥à¥à¥à¤Ë¤Ë¤Ë ---lllhhh555- ¤ò¤ò¤ò»È»È»ÈÍÑÍÑÍѤ¹¤¹¤¹¤ë¤ë¤ë¡£¡£¡£ + ooo555 圧圧圧縮縮縮アアアルルルゴゴゴリリリズズズムムムににに ---lllhhh555- ををを使使使用用用すすするるる。。。 - ooo666 °µ°µ°µ½Ì½Ì½Ì¥¢¥¢¥¢¥ë¥ë¥ë¥´¥´¥´¥ê¥ê¥ê¥º¥º¥º¥à¥à¥à¤Ë¤Ë¤Ë ---lllhhh666- ¤ò¤ò¤ò»È»È»ÈÍÑÍÑÍѤ¹¤¹¤¹¤ë¤ë¤ë¡£¡£¡£ + ooo666 圧圧圧縮縮縮アアアルルルゴゴゴリリリズズズムムムににに ---lllhhh666- ををを使使使用用用すすするるる。。。 - LHA¤Î¼ïÎà¤Ë¤è¤Ã¤Æ¤Ï¡¢-lh6-¤ÇÀ¸À®¤µ¤ì¤¿¥¢¡¼¥«¥¤¥Ö¤Ï - Ãê½Ð¤Ç¤­¤Ê¤¤¶²¤ì¤¬¤¢¤ë¤Î¤Ç¡¢¥¢¡¼¥«¥¤¥Ö¤òÇÛÉÛ¤¹¤ëºÝ - ¤Ë¤ÏÃí°Õ¤·¤Æ¤¯¤À¤µ¤¤¡£MSDOSÈǤÎÀµµ¬ÇÛÉÛÈÇ¤Ç¤Ï -lh6- - ¤ÎÀ¸À®¤Ï¹Ô¤¤¤Þ¤»¤ó¡£¤Þ¤¿¡¢MSDOSÈÇ LHA version 2.5x - °Ê¹ß¤Î¥Ð¡¼¥¸¥ç¥ó¤Ç¤Ï -lh6- ¤ÎÃê½Ð¤Î¤ß¤Ç¤­¤Þ¤¹¡£ + LHAの種類によっては、-lh6-で生成されたアーカイブは + 抽出できない恐れがあるので、アーカイブを配布する際 + には注意してください。MSDOS版の正規配布版では -lh6- + の生成は行いません。また、MSDOS版 LHA version 2.5x + 以降のバージョンでは -lh6- の抽出のみできます。 www===<<>> - ³ÊǼ¤ª¤è¤ÓÃê½Ð»þ¤Î¥ï¡¼¥¯Íѥǥ£¥ì¥¯¥È¥ê»ØÄê¡£¥Ç¥Õ¥© - ¥ë¥È¤Ç¤Ï¡¢/tmp ¤Ç¤¹¤¬¡¢/tmp ¤ÎÂ礭¤µ°Ê¾å¤Î¥Õ¥¡¥¤¥ë - ¤ò³ÊǼ¤¹¤ëºÝ¤Ë¤ÏɬÍפǤ¹¡£ + 格納および抽出時のワーク用ディレクトリ指定。デフォ + ルトでは、/tmp ですが、/tmp の大きさ以上のファイル + を格納する際には必要です。 - ddd ¥Õ¥¡¥¤¥ë³ÊǼ¸å¤Î¥Õ¥¡¥¤¥ë¤Îºï½ü¡£µ¡Ç½Ê¸»ú a, ¤â¤·¤¯ - ¤Ï u ¤È¶¦¤ËÍѤ¤¡¢¥¢¡¼¥«¥¤¥Ö¥Õ¥¡¥¤¥ë¤Ø¤Î¥Õ¥¡¥¤¥ë¤Î - ³ÊǼ¤Î¸å¤Ë¥Õ¥¡¥¤¥ë¤òºï½ü¤·¤Þ¤¹¡£u ¤ËÉղä·¤¿¾ì¹ç¡¢ - ¥¢¡¼¥«¥¤¥Ö¥Õ¥¡¥¤¥ë¤ÎÆâÍƤÎÊý¤¬¿·¤·¤¤»þ¤Ë¥Õ¥¡¥¤¥ë¤¬ - ³ÊǼ¤µ¤ì¤º¤Ëºï½ü¤µ¤ì¤ë¤³¤È¤Ë¤Ê¤ë¤Î¤ÇÃí°Õ¤·¤Æ²¼¤µ¤¤ - ¡£ + ddd ファイル格納後のファイルの削除。機能文字 a, もしく + は u と共に用い、アーカイブファイルへのファイルの + 格納の後にファイルを削除します。u に付加した場合、 + アーカイブファイルの内容の方が新しい時にファイルが + 格納されずに削除されることになるので注意して下さい + 。 - iii Ãê½Ð»þ¤Î¥Ç¥£¥ì¥¯¥È¥ê̾¤Î̵¸ú²½¡£Ãê½Ð»þ¤Ë¥Ç¥£¥ì¥¯¥È - ¥ê̾¤ò̵¸ú¤Ë¤·¤Þ¤¹¡£ + iii 抽出時のディレクトリ名の無効化。抽出時にディレクト + リ名を無効にします。 - zzz È󰵽̳ÊǼ¡£¥¢¡¼¥«¥¤¥Ö¤Ø¤Î¥Õ¥¡¥¤¥ë¤Î³ÊǼ»þ¤Ë°µ½Ì¤ò - ¹Ô¤Ê¤¤¤Þ¤»¤ó¡£¤¹¤Ç¤Ë°µ½Ì¤ò¹Ô¤Ê¤Ã¤Æ¤¤¤Æ°µ½Ì¸úΨ¤ò˾ - ¤á¤Ê¤¤¾ì¹ç¤Ê¤É¤Ë»ÈÍѤ·¤Þ¤¹¡£Ãê½Ð»þ¤Ë¤Ï̵»ë¤µ¤ì¤Þ¤¹ - ¡£ + zzz 非圧縮格納。アーカイブへのファイルの格納時に圧縮を + 行ないません。すでに圧縮を行なっていて圧縮効率を望 + めない場合などに使用します。抽出時には無視されます + 。 - ggg [generic]¥¢¡¼¥«¥¤¥Ö¤ÎºîÀ®¡£UNIX ÍѤÎÉղþðÊó¤ò»ý¤¿ - ¤Ê¤¤¥¢¡¼¥«¥¤¥Ö¥Õ¥¡¥¤¥ë¤òÀ¸À®¤·¤Þ¤¹¡£Ãê½Ð»þ¤Ë¤Ï̵»ë - ¤µ¤ì¤Þ¤¹¡£ + ggg [generic]アーカイブの作成。UNIX 用の付加情報を持た + ないアーカイブファイルを生成します。抽出時には無視 + されます。 - 000///111///222 ¥Ø¥Ã¥À¥ì¥Ù¥ë»ØÄê¡£¥¢¡¼¥«¥¤¥Ö¤ÎÆâÉôɽ¸½¤Î·Á¼°¤ò»ØÄê - ¤·¤Þ¤¹¡£¥Ç¥Õ¥©¥ë¥È¤Ç¤Ï 1 ¤Ç¤¹¡£0 ¤Ï LHarc ¤ÇÍÑ°Õ¤µ - ¤ì¤Æ¤¤¤¿¸Å¤¤·Á¼°¤Ç¤¹¡£¾­Íè¤Ï 2 ¤Î·Á¼°¤ËÅý°ì¤µ¤ì¤ë - ¤è¤¦¤Ç¤¹¡£¤³¤ì¤Ï¥Õ¥¡¥¤¥ë¤Î³ÊǼ»þ¤Î¤ßÍ­¸ú¤Ç¤¹¡£¥Õ¥¡ - ¥¤¥ë¤ÎÃê½Ð»þ¤Ë¤Ï¼«Æ°Åª¤Ë·Á¼°¤òȽÃǤ·½èÍý¤·¤Þ¤¹¡£ + 000///111///222 ヘッダレベル指定。アーカイブの内部表現の形式を指定 + します。デフォルトでは 1 です。0 は LHarc で用意さ + れていた古い形式です。将来は 2 の形式に統一される + ようです。これはファイルの格納時のみ有効です。ファ + イルの抽出時には自動的に形式を判断し処理します。 - _a_r_c_h_i_v_e__f_i_l_e ¤Ë¤Ï¥¢¡¼¥«¥¤¥Ö¥Õ¥¡¥¤¥ë¤ò»ØÄꤷ¤Þ¤¹¡£ + _a_r_c_h_i_v_e__f_i_l_e にはアーカイブファイルを指定します。 - _a_r_c_h_i_v_e__f_i_l_e ¤Ë- ¤È½ñ¤¯¤³¤È¤Ë¤è¤Ã¤Æ¡¢¥¢¡¼¥«¥¤¥Ö¥Õ¥¡¥¤¥ë¤ò - ɸ½àÆþ½ÐÎϤˤ¹¤ë¤³¤È¤¬²Äǽ¤Ç¤¹¡£tttaaarrr(1) ¤Î¤è¤¦¤Ë¡¢¥¢¡¼¥«¥¤ - ¥Ö¤ÎºîÀ®¡¢¹¹¿·»þ¤Ë¤Ïɸ½à½ÐÎϤˡ¢¥¢¡¼¥«¥¤¥Ö¤«¤é¤Î¥Õ¥¡¥¤¥ë¤Î + _a_r_c_h_i_v_e__f_i_l_e に- と書くことによって、アーカイブファイルを + 標準入出力にすることが可能です。tttaaarrr(1) のように、アーカイ + ブの作成、更新時には標準出力に、アーカイブからのファイルの January 14,1997 3 @@ -165,50 +165,50 @@ LHA(N) Unix Programmer's Manual LHA(N) LHA(N) Unix Programmer's Manual LHA(N) - Ãê½Ð»þ¤Ë¤Ïɸ½àÆþÎϤˤʤê¤Þ¤¹¡£ - - Ãê½Ð»þ¤Ë_a_r_c_h_i_v_e__f_i_l_e ¤¬Â¸ºß¤·¤Ê¤¤»þ¤Ë¤Ï¥µ¥Õ¥£¥Ã¥¯¥¹...lllzzzhhh ¤ò - ¥Õ¥¡¥¤¥ë̾¤Î¸å¤í¤ËÉղä·¤ÆÃê½Ð¤ò»î¤ß¤Þ¤¹¡£ - Æä˵¬Äê¤Ï¤¢¤ê¤Þ¤»¤ó¤¬¡¢±¿ÍѾ奵¥Õ¥£¥Ã¥¯¥¹¤È¤·¤Æ...lllzzzhhh ¤òÍÑ - ¤¤¤ë¤Î¤¬Ë¾¤Þ¤·¤¤¤È»×¤¤¤Þ¤¹¡£ÆüËܰʳ°¤Ç¤Ï ...lllhhhaaa ¤ò»ÈÍѤ·¤Æ¤¤ - ¤ë¥±¡¼¥¹¤¬¤¢¤ë¤½¤¦¤Ç¤¹¤Î¤Ç¡¢Å¬µ¹Ä´À°¤·¤Æ²¼¤µ¤¤¡£:-) - ¥µ¥Õ¥£¥Ã¥¯¥¹¤¬cccooommm ¤â¤·¤¯¤Ï...eeexxxeee ¤Î¾ì¹ç¤Ë¤Ï¡¢MS-DOS ÈǤǺîÀ® - ¤µ¤ì¤¿_S_F_X(¼«¸ÊŸ³«µ¡Ç½ÉÕ¤­¤Î°µ½Ì¥Õ¥¡¥¤¥ë) ·Á¼°¤«¤É¤¦¤«¤ò¥Á - ¥§¥Ã¥¯¤·¡¢Âбþ¤·¤Þ¤¹¡£ - ¥µ¥Õ¥£¥Ã¥¯¥¹¤¬...xxx ¤Î¾ì¹ç¤Ë¤Ï¡¢ÆüËÜÀ½ SHARP X68000 ¤Î OS, - Human68k ÈǤǺîÀ®¤µ¤ì¤¿ SFX ·Á¼°¤«¤É¤¦¤«¤ò¥Á¥§¥Ã¥¯¤·Âбþ¤· - ¤Þ¤¹¡£ - SFX ·Á¼°¤Î¥¢¡¼¥«¥¤¥Ö¥Õ¥¡¥¤¥ë¤ËÂФ·¤ÆÄɲääºï½ü¤ò¹Ô¤Ã¤¿¾ì¹ç - ¤Ë¤Ï¡¢¥µ¥Õ¥£¥Ã¥¯¥¹¤ò...lllzzzhhh ¤ËÊѹ¹¤·¡¢SFX ¤Î¾ðÊó¤òºï½ü¤·¤Þ¤¹ - ¡£ - -FFFIIILLLEEESSS ¥Õ¥Õ¥Õ¥¡¥¡¥¡¥¤¥¤¥¤¥ë¥ë¥ë - *.lzh - LHa/LHarc ¥¢¡¼¥«¥¤¥Ö¥Õ¥¡¥¤¥ë - *.bak - ¥Ð¥Ã¥¯¥¢¥Ã¥×¥Õ¥¡¥¤¥ë - /tmp/lh* - ¥Æ¥ó¥Ý¥é¥ê¥Õ¥¡¥¤¥ë - *.com *.exe - MS-DOS SFX ¥Õ¥¡¥¤¥ë - *.x - Human68k SFX ¥Õ¥¡¥¤¥ë - -SSSEEEEEE AAALLLSSSOOO ´Ø´Ø´ØϢϢϢ¹à¹à¹àÌÜÌÜÌÜ + 抽出時には標準入力になります。 + + 抽出時に_a_r_c_h_i_v_e__f_i_l_e が存在しない時にはサフィックス...lllzzzhhh を + ファイル名の後ろに付加して抽出を試みます。 + 特に規定はありませんが、運用上サフィックスとして...lllzzzhhh を用 + いるのが望ましいと思います。日本以外では ...lllhhhaaa を使用してい + るケースがあるそうですので、適宜調整して下さい。:-) + サフィックスがcccooommm もしくは...eeexxxeee の場合には、MS-DOS 版で作成 + された_S_F_X(自己展開機能付きの圧縮ファイル) 形式かどうかをチ + ェックし、対応します。 + サフィックスが...xxx の場合には、日本製 SHARP X68000 の OS, + Human68k 版で作成された SFX 形式かどうかをチェックし対応し + ます。 + SFX 形式のアーカイブファイルに対して追加や削除を行った場合 + には、サフィックスを...lllzzzhhh に変更し、SFX の情報を削除します + 。 + +FFFIIILLLEEESSS フフファァァイイイルルル + *.lzh - LHa/LHarc アーカイブファイル + *.bak - バックアップファイル + /tmp/lh* - テンポラリファイル + *.com *.exe - MS-DOS SFX ファイル + *.x - Human68k SFX ファイル + +SSSEEEEEE AAALLLSSSOOO 関関関連連連項項項目目目 tttaaarrr(1), aaarrr(1), cccooommmppprrreeessssss(1) -DDDIIISSSTTTRRRIIIBBBUUUTTTIIIOOONNN ºÆºÆºÆÇÛÇÛÇÛÉÛÉÛÉۤˤˤˤĤĤĤ¤¤¤¤¤¤Æ¤Æ¤Æ +DDDIIISSSTTTRRRIIIBBBUUUTTTIIIOOONNN 再再再配配配布布布にににつつついいいててて - °Ê²¼¤Î¾ò·ï¤Ç¡¢ºÆÇÛÉÛ¡¢Å¾ºÜ¡¢²þÊѤòµö²Ä¤·¤Þ¤¹¡£ + 以下の条件で、再配布、転載、改変を許可します。 - 1. Ãøºî¸¢É½¼¨¤òºï½ü¤·¤Ê¤¤¤³¤È¡£ + 1. 著作権表示を削除しないこと。 - 2. ÇÛÉÛÆâÍƤˤĤ¤¤Æ¤Ï¡¢ + 2. 配布内容については、 - a. ÇÛÉۤκݤ˸ºß¤¹¤ëÆâÍÆ(¤¹¤Ê¤ï¤Á¥½¡¼¥¹¥³¡¼¥É¡¢¥É¥­ - ¥å¥á¥ó¥È¡¢¥×¥í¥°¥é¥Þ¡¼¤Ø¤Î¼ê°ú¤­¤Ê¤É)¤¬ºÆÇÛÉÛ¤µ¤ì - ¤¿¤â¤Î¤ÎÃæ¤Ëɬ¤ºÂ¸ºß¤¹¤ë¤³¤È¡£²þÊѤµ¤ì¤Æ¤¤¤ë¤Ê¤é¤Ð - ¡¢¤½¤ì¤òÌÀ¼¨¤·¤¿¥É¥­¥å¥á¥ó¥È¤òÍÑ°Õ¤¹¤ë¤³¤È¡£ + a. 配布の際に存在する内容(すなわちソースコード、ドキ + ュメント、プログラマーへの手引きなど)が再配布され + たものの中に必ず存在すること。改変されているならば + 、それを明示したドキュメントを用意すること。 - b. LHa ¤ËÂФ¹¤ëÉղòÁÃͤ¬ÉÕ¤±¤é¤ì¤ÆºÆÇÛÉÛ¤µ¤ì¤ë¾ì¹ç¤Ë - ¤Ï¤½¤ì¤é¤â¤Ç¤­¤ë¤À¤±´Þ¤á¤ë¤è¤¦ÅØÎϤ¹¤ë¤³¤È¡£¤Þ¤¿¡¢ - ¤½¤ÎºÝ¤Ë¤ÏÉղòÁÃͤ¬ÉÕ¤±¤é¤ì¤Æ¤¤¤ë¤³¤È¤òÌÀ¼¨¤·¤¿¥É - ¥­¥å¥á¥ó¥È¤òÍÑ°Õ¤¹¤ë¤³¤È¡£ + b. LHa に対する付加価値が付けられて再配布される場合に + はそれらもできるだけ含めるよう努力すること。また、 + その際には付加価値が付けられていることを明示したド + キュメントを用意すること。 @@ -219,51 +219,51 @@ DDDIIISSSTTTRRRIIIBBBUUUTTTIIIOOONNN LHA(N) Unix Programmer's Manual LHA(N) - c. ¥Ð¥¤¥Ê¥ê¤Î¤ß¤ÎÇÛÉۤϵö¤µ¤ì¤Ê¤¤¡£(ÉղòÁÃͤΤâ¤Î¤â - ´Þ¤à) + c. バイナリのみの配布は許されない。(付加価値のものも + 含む) - 3. ºÇ¿·ÈǤÎÇÛÉÛ¤Ë̳¤á¤ë¤³¤È¡£(µÁ̳¤Ï¤Ê¤¤) + 3. 最新版の配布に務めること。(義務はない) - Ãí. ¤Ê¤ª¡¢¥Í¥Ã¥È¤Ç¤ÎÇÛÉդϼ«Í³¤Ç¤¢¤ë¤¬¡¢¥Í¥Ã¥È¤Ë¥¢¥¯¥» - ¥¹¤Ç¤­¤Ê¤¤Êý¡Ê»¨»ï¤ª¤è¤Ó¡¢CD-ROM ¤Ê¤É¤Ë¤è¤ë¡ËÇÛÉÕ - ¤Ï¡¢ÇÛÉÕÁ°¤Ë¤³¤Á¤é¤Ë E-Mail ¤ò¤ª´ê¤¤¤·¤Þ¤¹¡£ÇÛÉÕÁ° - ¤Ë½ÐÍè¤Ê¤¤ºÝ¤Ë¤Ï¡¢¸åÆüɬ¤º E-Mail ¤ò¤ª´ê¤¤¤·¤Þ¤¹¡£ + 注. なお、ネットでの配付は自由であるが、ネットにアクセ + スできない方(雑誌および、CD-ROM などによる)配付 + は、配付前にこちらに E-Mail をお願いします。配付前 + に出来ない際には、後日必ず E-Mail をお願いします。 - 4. ¤³¤Î¥×¥í¥°¥é¥à¤Î¸ºß¤ä»ÈÍѤ·¤¿¤³¤È¤Ë¤è¤Ã¤ÆÀ¸¤¸¤¿Â» - ³²¤ÏÁ´¤¯Êݾڤ·¤Ê¤¤¡£ + 4. このプログラムの存在や使用したことによって生じた損 + 害は全く保証しない。 - 5. ºî¼Ô¤Ï¡¢¤³¤Î¥×¥í¥°¥é¥à¤ËÉÔÈ÷¤¬¤¢¤Ã¤Æ¤â¡¢¤½¤ì¤òÄûÀµ - ¤¹¤ëµÁ̳¤òÉé¤ï¤Ê¤¤¡£ + 5. 作者は、このプログラムに不備があっても、それを訂正 + する義務を負わない。 - 6. ¤³¤Î¥×¥í¥°¥é¥à¤Î°ìÉô¡¢¤Þ¤¿¤ÏÁ´Éô¤ò¾¤Î¥×¥í¥°¥é¥à¤Ë - ÁȤ߹þ¤ó¤ÇÍøÍѤ·¤Æ¤â¤«¤Þ¤ï¤Ê¤¤¡£¤³¤Î¾ì¹ç¡¢¤½¤Î¥×¥í - ¥°¥é¥à¤Ï LHa ¤Ç¤Ï¤Ê¤¯¡¢LHa ¤È̾¾è¤Ã¤Æ¤Ï¤¤¤±¤Ê¤¤¡£ + 6. このプログラムの一部、または全部を他のプログラムに + 組み込んで利用してもかまわない。この場合、そのプロ + グラムは LHa ではなく、LHa と名乗ってはいけない。 - 7. ¾¦ÍøÍѤ˴ؤ·¤Æ¤Ï¡¢¾åµ­¤Î¾ò·ï¤Ë²Ã¤¨¡¢²¼µ­¤Î¾ò·ï¤Î¤â - ¤È¤Ë¤³¤ì¤òǧ¤á¤ë¡£ + 7. 商利用に関しては、上記の条件に加え、下記の条件のも + とにこれを認める。 - a. ¤³¤Î¥×¥í¥°¥é¥à¤ò¥á¥¤¥ó¤È¤¹¤ë¾¦ÍøÍѤ϶ػߤ¹¤ë¡£ + a. このプログラムをメインとする商利用は禁止する。 - b. ¾¦ÍøÍѤÎÁê¼ê¤¬¤³¤Î¥×¥í¥°¥é¥à¤Î»ÈÍѼԤȤ·¤ÆÉÔŬÀÚ¤È - ȽÃǤ·¤¿¾ì¹ç¤Ë¤ÏÇÛÉÛ¤·¤Ê¤¤¡£ + b. 商利用の相手がこのプログラムの使用者として不適切と + 判断した場合には配布しない。 - c. ¥¤¥ó¥¹¥È¡¼¥ë¤Î¼êÃʤȤ·¤Æ»ÈÍѤ¹¤ë¾ì¹ç¡¢¤³¤Î¥×¥í¥°¥é - ¥à¤ò»È¤¦¤³¤È¤òÁê¼ê¤Ë¶¯À©¤·¤Ê¤¤¡£¤³¤Î¾ì¹ç¡¢¾¦ÍøÍÑ¼Ô - ¤¬ºî¶È¤ò¹Ô¤¦¡£¤Þ¤¿¡¢¤½¤Î¤È¤­¤Î»³²¤Ï¡¢¾¦ÍøÍѼԤ¬Á´ - ÀÕǤ¤òÉ餦¡£ + c. インストールの手段として使用する場合、このプログラ + ムを使うことを相手に強制しない。この場合、商利用者 + が作業を行う。また、そのときの損害は、商利用者が全 + 責任を負う。 - d. ¾¦ÍøÍѤòÉղòÁÃͤȤ·¤Æ¹Ô¤¤¤³¤Î¥×¥í¥°¥é¥à¤ò»ÈÍѤ¹¤ë - ¾ì¹ç¡¢¾¦ÍøÍѼԤϡ¢¤½¤Î¥µ¥Ý¡¼¥È¤ò¹Ô¤¦¡£ + d. 商利用を付加価値として行いこのプログラムを使用する + 場合、商利用者は、そのサポートを行う。 -ºÇºÇºÇ¸å¸å¸å¤Ë¤Ë¤Ë(((FFFrrrooommm YYY...TTTaaagggaaawwwaaa))) - LZHUF Ë¡¤Î´ðÁäȤʤä¿ LZARI Ë¡¤òȯɽ¤·¤Æ¤¯¤À¤µ¤Ã¤¿±ü¼À² - ɧ»á¡¢¤½¤ì¤ò NIFTY-Serve ¤Ë¾Ò²ð¤·¡¢¤Þ¤¿¡¢LArc ¤Îºî¼Ô¤Ç¤â¤¢ - ¤ë»°ÌÚÏÂɧ»á¡¢¤Þ¤¿¡¢¤³¤ì¤é¤ÎÍ×°ø¤«¤é LZHUF Ë¡µÚ¤Ó¡¢MS-DOS - ÈÇ LHarc ¤òºîÀ®¤·¤¿µÈºê±ÉÂٻᡢLHarc UNIX ¤Î³«È¯¤Ë¶¨ÎϤ·¤¿ - ¿Íã¡¢¤³¤³¤í¤è¤¯¥á¥Ã¥»¡¼¥¸¤Î¶¶ÅϤ·¤ò¤·¤Æ¤¯¤ì¤¿Àкê°ìÌÀ»á(MIX - ID:k.ishi)¡¢¤Þ¤¿¡¢¤¤¤í¤¤¤í¤Ê¥ì¥Ý¡¼¥È¤ò¤¯¤ì¤¿¤ß¤Ê¤µ¤ó¡¢¤½¤ì - ¤òÃæ·Ñ¤·¤Æ¤¯¤À¤µ¤Ã¤¿¿¹¸ø°ìϺ»á (MIX ID:kmori)¤Ë´¶¼Õ¤·¤Þ¤¹ - ¡£ +最最最後後後ににに(((FFFrrrooommm YYY...TTTaaagggaaawwwaaa))) + LZHUF 法の基礎となった LZARI 法を発表してくださった奥村晴 + 彦氏、それを NIFTY-Serve に紹介し、また、LArc の作者でもあ + る三木和彦氏、また、これらの要因から LZHUF 法及び、MS-DOS + 版 LHarc を作成した吉崎栄泰氏、LHarc UNIX の開発に協力した + 人達、こころよくメッセージの橋渡しをしてくれた石崎一明氏(MIX + ID:k.ishi)、また、いろいろなレポートをくれたみなさん、それ + を中継してくださった森公一郎氏 (MIX ID:kmori)に感謝します + 。 January 14,1997 5 @@ -273,15 +273,15 @@ LHA(N) Unix Programmer's Manual LHA(N) LHA(N) Unix Programmer's Manual LHA(N) -ºÇºÇºÇ¸å¸å¸å¤Ë¤Ë¤Ë(((FFFrrrooommm MMMaaasssaaarrruuu OOOkkkiii))) - ¾åµ­¤ÎÊý¡¹¤Ï¤â¤Á¤í¤ó¡¢²Ã¤¨¤Æ LHarc UNIX ¤òºîÀ®¤·¤¿ Y.Tagawa - »á¡¢¤½¤ì¤ò OSK ¤Ë°Ü¿¢¤·µÈºê»á¤Î LHx ¤Î¥¢¥ë¥´¥ê¥º¥à¤òÁȤ߹þ - ¤ó¤À H.S »á¡¢JUNET ¤Î LHa for UNIX MailingList ¤Ë¤Æ¶¨ÎϤ· - ¤Æ¤¯¤À¤µ¤Ã¤¿¤ß¤Ê¤µ¤ó¤Ë´¶¼ÕÃפ·¤Þ¤¹¡£ +最最最後後後ににに(((FFFrrrooommm MMMaaasssaaarrruuu OOOkkkiii))) + 上記の方々はもちろん、加えて LHarc UNIX を作成した Y.Tagawa + 氏、それを OSK に移植し吉崎氏の LHx のアルゴリズムを組み込 + んだ H.S 氏、JUNET の LHa for UNIX MailingList にて協力し + てくださったみなさんに感謝致します。 -ºÇºÇºÇ¸å¸å¸å¤Ë¤Ë¤Ë(((FFFrrrooommm NNNooobbbuuutttaaakkkaaa WWWaaatttaaazzzaaakkkiii))) - ¤³¤Î¥×¥í¥°¥é¥à¤ÎºîÀ®¤Ë¤«¤«¤ï¤Ã¤¿Êý¡¹¡¢¤µ¤é¤Ë¤³¤³¤Þ¤Ç LHa - ¤ò»Å¾å¤²¤Æ¤¯¤À¤µ¤Ã¤¿ ²­¾¡»á¤Ë´¶¼ÕÃפ·¤Þ¤¹¡£ +最最最後後後ににに(((FFFrrrooommm NNNooobbbuuutttaaakkkaaa WWWaaatttaaazzzaaakkkiii))) + このプログラムの作成にかかわった方々、さらにここまで LHa + を仕上げてくださった 沖勝氏に感謝致します。 diff --git a/man/lha.n b/man/lha.n index 4af75a8..fefcbb6 100644 --- a/man/lha.n +++ b/man/lha.n @@ -1,7 +1,7 @@ .TH LHA N "January 14,1997" "" "LHa for UNIX Users Manual" -.SH "NAME ̾¾Î" -LHa \- ¹â°µ½Ì¥¢¡¼¥«¥¤¥Ð -.SH "SYNOPSIS ·Á¼°" +.SH "NAME 名称" +LHa \- 高圧縮アーカイバ +.SH "SYNOPSIS 形式" .B lha .B key [ @@ -14,286 +14,286 @@ LHa \- .br .B lha .I archive_file -.SH "DESCRIPTION ²òÀâ" +.SH "DESCRIPTION 解説" .B LHa -¤Ï¸úΨ¤Î¹â¤¤°µ½Ìµ¡Ç½¤ò»ý¤Ä¥Õ¥¡¥¤¥ë¥¢¡¼¥«¥¤¥Ð¤Ç¤¹¡£ +は効率の高い圧縮機能を持つファイルアーカイバです。 .br .B key -¤Ëµ¡Ç½Ê¸»ú¤ò»ØÄꤷ¡¢file ¤ÎÄɲᢹ¹¿·¡¢Ãê½Ð¡¢ºï½ü¡¢°ìÍ÷ɽ¼¨ -¤Ê¤É¤ò¹Ô¤Ê¤¤¤Þ¤¹¡£°ú¿ô¤Ë archive_file ¤Î¤ß¤ò»ØÄꤷ¤¿¾ì¹ç¤Ë¤Ï¡¢ -µ¡Ç½Ê¸»ú¤Ë l ¤ò»ØÄꤷ¤¿¤Î¤ÈƱÅù¤ÎÆ°ºî¤ò¹Ô¤Ê¤¤¤Þ¤¹¡£ +に機能文字を指定し、file の追加、更新、抽出、削除、一覧表示 +などを行ないます。引数に archive_file のみを指定した場合には、 +機能文字に l を指定したのと同等の動作を行ないます。 .br -µ¡Ç½Ê¸»ú¤Ï°Ê²¼¤ÎÄ̤ê¤Ç¤¹¡£ +機能文字は以下の通りです。 .TP 8 .B a -Äɲá£file ¤ò archive_file ¤ËÄɲä·¤Þ¤¹¡£ -file ¤¬¥Ç¥£¥ì¥¯¥È¥ê¤Ç¤¢¤ì¤Ð¡¢¤½¤Î¥Ç¥£¥ì¥¯¥È¥ê¤Ë´Þ¤Þ¤ì¤ë -¥Õ¥¡¥¤¥ë¤ò¤¹¤Ù¤ÆÄɲä·¤Þ¤¹¡£ +追加。file を archive_file に追加します。 +file がディレクトリであれば、そのディレクトリに含まれる +ファイルをすべて追加します。 .TP 8 .B u -¹¹¿·¡£file ¤¬ archive_file ¤Ë³ÊǼ¤µ¤ì¤Æ¤¤¤Ê¤¤¤«¡¢¤â¤·¤¯¤Ï -³ÊǼ¤µ¤ì¤Æ¤¤¤ë¤â¤Î¤¬¸Å¤±¤ì¤Ð¡¢file ¤ò archive_file ¤ËÄɲà -¤·¤Þ¤¹¡£ +更新。file が archive_file に格納されていないか、もしくは +格納されているものが古ければ、file を archive_file に追加 +します。 .TP 8 -.B "l ¤Þ¤¿¤Ï v" -°ìÍ÷ɽ¼¨¡£archive_file ¤Ë³ÊǼ¤µ¤ì¤Æ¤¤¤ë file ¤Î¾ðÊó¤ò°ìÍ÷ -ɽ¼¨¤·¤Þ¤¹¡£file ¤Î»ØÄ꤬¤Ê¤±¤ì¤Ð archive_file Æâ¤ÎÁ´¤Æ¤Î -¥Õ¥¡¥¤¥ë¤Î¾ðÊó¤òɽ¼¨¤·¤Þ¤¹¡£ -v ¤ò»ØÄꤹ¤ë¤È l ¤è¤ê¤â¾Ü¤·¤¤¾ðÊó¤òɽ¼¨¤·¤Þ¤¹¡£ +.B "l または v" +一覧表示。archive_file に格納されている file の情報を一覧 +表示します。file の指定がなければ archive_file 内の全ての +ファイルの情報を表示します。 +v を指定すると l よりも詳しい情報を表示します。 .TP 8 -.B "x ¤Þ¤¿¤Ï e" -Ãê½Ð¡£archive_file ¤«¤é file ¤òÃê½Ð¤·¤Þ¤¹¡£file ¤Î»ØÄ꤬ -¤Ê¤±¤ì¤Ð archive_file Æâ¤ÎÁ´¤Æ¤Î¥Õ¥¡¥¤¥ë¤òÃê½Ð¤·¤Þ¤¹¡£ -Ãê½Ð¤¹¤Ù¤­¥Õ¥¡¥¤¥ë¤¬¤¹¤Ç¤Ë¸ºß¤·¤Æ¤¤¤ë¾ì¹ç¤Ë¤Ï¡¢½Å¤Í½ñ¤­ -¤·¤Æ¤¤¤¤¤«¤ÎÌ䤤¹ç¤ï¤»¤ò¹Ô¤Ê¤¤¤Þ¤¹¡£ +.B "x または e" +抽出。archive_file から file を抽出します。file の指定が +なければ archive_file 内の全てのファイルを抽出します。 +抽出すべきファイルがすでに存在している場合には、重ね書き +していいかの問い合わせを行ないます。 .TP 8 .B p -ÆâÍÆɽ¼¨¡£archive_file ¤«¤é file ¤ÎÆâÍƤò¼è¤ê½Ð¤·¡¢É¸½à -½ÐÎϤؤȽÐÎϤ·¤Þ¤¹¡£file ¤Î»ØÄ꤬¤Ê¤±¤ì¤Ð¡¢Á´¤Æ¤ÎÆâÍƤò -½ÐÎϤ·¤Þ¤¹¡£ +内容表示。archive_file から file の内容を取り出し、標準 +出力へと出力します。file の指定がなければ、全ての内容を +出力します。 .TP 8 .B d -ºï½ü¡£archive_file ¤«¤é file ¤òºï½ü¤·¤Þ¤¹¡£ +削除。archive_file から file を削除します。 .TP 8 .B m -°ÜÆ°¡£file ¤ò archive_file ¤ËÄɲä·¤¿¸å¡¢file ¤òºï½ü¤·¤Þ¤¹¡£ -¤³¤ì¤Ï¡¢key ¤Ë a ¤ò¡¢modifiers ¤Ë d ¤ò»ØÄꤷ¤¿»þ¤ÈƱ¤¸Æ°ºî -¤ò¹Ô¤Ê¤¤¤Þ¤¹¡£ +移動。file を archive_file に追加した後、file を削除します。 +これは、key に a を、modifiers に d を指定した時と同じ動作 +を行ないます。 .TP 8 .B c -¿·µ¬ºîÀ®¡£archive_file ¤ò¿·¤¿¤ËºîÀ®¤·¡¢file ¤òÄɲä·¤Þ¤¹¡£ +新規作成。archive_file を新たに作成し、file を追加します。 .PP -µ¡Ç½Êѹ¹Ê¸»ú modifiers ¤ò»ØÄꤹ¤ë¤³¤È¤Ë¤è¤Ã¤Æ key ¤ÎÆ°ºî¤Î -¾ÜºÙ¤òÊѹ¹¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£modifiers ¤Ï¡¢°Ê²¼¤ÎÃ椫¤éÊ£ -¿ô»ØÄꤹ¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ +機能変更文字 modifiers を指定することによって key の動作の +詳細を変更することができます。modifiers は、以下の中から複 +数指定することができます。 .TP 8 .B q -ɽ¼¨¤ÎÍÞÀ©¡£ -ÂÐÏÃŪ¤Ê¥á¥Ã¥»¡¼¥¸¤Îɽ¼¨¤òÍÞÀ©¤·¤Þ¤¹¡£ - ¤ÎÃͤˤè¤Ã¤Æ¡¢INDICATOR ¤Îɽ¼¨ÊýË¡¤òÊѹ¹¤Ç¤­¤Þ¤¹¡£ +表示の抑制。 +対話的なメッセージの表示を抑制します。 + の値によって、INDICATOR の表示方法を変更できます。 .RS .IP -.B q0 oo.... ¤Îɽ¼¨¤ò¹Ô¤Ê¤¦¡£ +.B q0 oo.... の表示を行なう。 -.B q1 ¥Õ¥¡¥¤¥ë̾ɽ¼¨¤Î¤ß¹Ô¤Ê¤¦¡£ +.B q1 ファイル名表示のみ行なう。 -.B q2 ²¿¤âɽ¼¨¤·¤Ê¤¤¡£ +.B q2 何も表示しない。 .RE .IP -¤Ê¤ª¡¢ÃͤòÆþÎϤ·¤Ê¤«¤Ã¤¿ºÝ¤Ë¤Ï¡¢q2 ¤ÈƱÅù¤Ë¤Ê¤ê¤Þ¤¹¡£ +なお、値を入力しなかった際には、q2 と同等になります。 .TP 8 .B v -ɽ¼¨¤Î¾éĹ²½¡£ -ɽ¼¨¤¹¤ë¥á¥Ã¥»¡¼¥¸¤ò¾éŤˤ·¤Þ¤¹¡£ +表示の冗長化。 +表示するメッセージを冗長にします。 .TP 8 .B n -¼Â¹Ô¤·¤Ê¤¤¡£ -¼ÂºÝ¤Î¹¹¿·¤äÃê½Ð¤ÎÆ°ºî¤ò¹Ô¤Ê¤¤¤Þ¤»¤ó¡£ -¼Â¹ÔÆâÍƤγÎǧ¤Î¤¿¤á¤ËÍÑ°Õ¤µ¤ì¤Æ¤¤¤Þ¤¹¡£ +実行しない。 +実際の更新や抽出の動作を行ないません。 +実行内容の確認のために用意されています。 .TP 8 .B f -¶¯À©Åª¤Ê¼Â¹Ô¡£ -¥Õ¥¡¥¤¥ë¤ÎÃê½Ð»þ¤Ë¡¢Â¸ºß¤¹¤ë¥Õ¥¡¥¤¥ë¤Îºï½ü³Îǧ¤ò -¹Ô¤Ê¤ï¤º¶¯À©Åª¤Ëºï½ü¤ò¹Ô¤Ê¤¤¤Þ¤¹¡£ +強制的な実行。 +ファイルの抽出時に、存在するファイルの削除確認を +行なわず強制的に削除を行ないます。 .TP 8 .B t -¥Æ¥­¥¹¥È¥â¡¼¥É¤Ë¤è¤ë³ÊǼ,Ãê½Ð¡£ -¥¢¡¼¥«¥¤¥Ö¥Õ¥¡¥¤¥ë¤Ø¤Î¥Õ¥¡¥¤¥ë¤Î³ÊǼ»þ¤Ë¤Ï¡¢ -UNIX ¤«¤é MS-DOS ¤Ø¤Î²þ¹Ô¥³¡¼¥ÉÊÑ´¹¤ò¹Ô¤Ê¤¤¤Þ¤¹¡£ -¥Õ¥¡¥¤¥ë¤ÎÃê½Ð»þ¤Ë¤ÏµÕ¤Ë¡¢MS-DOS ¤«¤é UNIX ¤Ø¤Î -²þ¹Ô¥³¡¼¥ÉÊÑ´¹¤ò¹Ô¤Ê¤¤¤Þ¤¹¡£ +テキストモードによる格納,抽出。 +アーカイブファイルへのファイルの格納時には、 +UNIX から MS-DOS への改行コード変換を行ないます。 +ファイルの抽出時には逆に、MS-DOS から UNIX への +改行コード変換を行ないます。 .TP 8 .B o{num} -LHarc ¸ß´¹¥¢¡¼¥«¥¤¥Ö¤ÎÀ¸À®¡¢¤Þ¤¿¤Ï°µ½Ì¥¢¥ë¥´¥ê¥º¥à¤ò»ØÄꤹ¤ë¡£ -µ¡Ç½Ê¸»ú a, u, m ¤È¶¦¤Ë»ØÄꤷ¡¢Ãê½Ð»þ¤Ë¤Ï̵»ë¤µ¤ì¤Þ¤¹¡£ +LHarc 互換アーカイブの生成、または圧縮アルゴリズムを指定する。 +機能文字 a, u, m と共に指定し、抽出時には無視されます。 .RS .IP -.B o LHarc ¸ß´¹¥¢¡¼¥«¥¤¥Ö¤ÎÀ¸À®¤ò¹Ô¤¦¡£ +.B o LHarc 互換アーカイブの生成を行う。 .RS -¸Å¤¤·Á¼°¤Î¥¢¡¼¥«¥¤¥Ö¤òÀ¸À®¤·¤Þ¤¹¡£°µ½Ì¥¢¥ë¥´¥ê¥º¥à¤Ï-lh1-¤ò»ÈÍѤ·¤Þ¤¹¡£ +古い形式のアーカイブを生成します。圧縮アルゴリズムは-lh1-を使用します。 .RE -.B o5 °µ½Ì¥¢¥ë¥´¥ê¥º¥à¤Ë -lh5- ¤ò»ÈÍѤ¹¤ë¡£ +.B o5 圧縮アルゴリズムに -lh5- を使用する。 -.B o6 °µ½Ì¥¢¥ë¥´¥ê¥º¥à¤Ë -lh6- ¤ò»ÈÍѤ¹¤ë¡£ +.B o6 圧縮アルゴリズムに -lh6- を使用する。 -LHA¤Î¼ïÎà¤Ë¤è¤Ã¤Æ¤Ï¡¢-lh6-¤ÇÀ¸À®¤µ¤ì¤¿¥¢¡¼¥«¥¤¥Ö¤ÏÃê½Ð¤Ç¤­¤Ê¤¤ -¶²¤ì¤¬¤¢¤ë¤Î¤Ç¡¢¥¢¡¼¥«¥¤¥Ö¤òÇÛÉÛ¤¹¤ëºÝ¤Ë¤ÏÃí°Õ¤·¤Æ¤¯¤À¤µ¤¤¡£ -MSDOSÈǤÎÀµµ¬ÇÛÉÛÈÇ¤Ç¤Ï -lh6- ¤ÎÀ¸À®¤Ï¹Ô¤¤¤Þ¤»¤ó¡£ -¤Þ¤¿¡¢MSDOSÈÇ LHA version 2.5x °Ê¹ß¤Î¥Ð¡¼¥¸¥ç¥ó¤Ç¤Ï -lh6- ¤Î -Ãê½Ð¤Î¤ß¤Ç¤­¤Þ¤¹¡£ +LHAの種類によっては、-lh6-で生成されたアーカイブは抽出できない +恐れがあるので、アーカイブを配布する際には注意してください。 +MSDOS版の正規配布版では -lh6- の生成は行いません。 +また、MSDOS版 LHA version 2.5x 以降のバージョンでは -lh6- の +抽出のみできます。 .RE .TP 8 .B w= -³ÊǼ¤ª¤è¤ÓÃê½Ð»þ¤Î¥ï¡¼¥¯Íѥǥ£¥ì¥¯¥È¥ê»ØÄê¡£ -¥Ç¥Õ¥©¥ë¥È¤Ç¤Ï¡¢/tmp ¤Ç¤¹¤¬¡¢/tmp ¤ÎÂ礭¤µ°Ê¾å¤Î -¥Õ¥¡¥¤¥ë¤ò³ÊǼ¤¹¤ëºÝ¤Ë¤ÏɬÍפǤ¹¡£ +格納および抽出時のワーク用ディレクトリ指定。 +デフォルトでは、/tmp ですが、/tmp の大きさ以上の +ファイルを格納する際には必要です。 .TP 8 .B d -¥Õ¥¡¥¤¥ë³ÊǼ¸å¤Î¥Õ¥¡¥¤¥ë¤Îºï½ü¡£ -µ¡Ç½Ê¸»ú a, ¤â¤·¤¯¤Ï u ¤È¶¦¤ËÍѤ¤¡¢¥¢¡¼¥«¥¤¥Ö -¥Õ¥¡¥¤¥ë¤Ø¤Î¥Õ¥¡¥¤¥ë¤Î³ÊǼ¤Î¸å¤Ë¥Õ¥¡¥¤¥ë¤òºï -½ü¤·¤Þ¤¹¡£ -u ¤ËÉղä·¤¿¾ì¹ç¡¢¥¢¡¼¥«¥¤¥Ö¥Õ¥¡¥¤¥ë¤ÎÆâÍƤÎÊý¤¬ -¿·¤·¤¤»þ¤Ë¥Õ¥¡¥¤¥ë¤¬³ÊǼ¤µ¤ì¤º¤Ëºï½ü¤µ¤ì¤ë¤³¤È¤Ë -¤Ê¤ë¤Î¤ÇÃí°Õ¤·¤Æ²¼¤µ¤¤¡£ +ファイル格納後のファイルの削除。 +機能文字 a, もしくは u と共に用い、アーカイブ +ファイルへのファイルの格納の後にファイルを削 +除します。 +u に付加した場合、アーカイブファイルの内容の方が +新しい時にファイルが格納されずに削除されることに +なるので注意して下さい。 .TP 8 .B i -Ãê½Ð»þ¤Î¥Ç¥£¥ì¥¯¥È¥ê̾¤Î̵¸ú²½¡£ -Ãê½Ð»þ¤Ë¥Ç¥£¥ì¥¯¥È¥ê̾¤ò̵¸ú¤Ë¤·¤Þ¤¹¡£ +抽出時のディレクトリ名の無効化。 +抽出時にディレクトリ名を無効にします。 .TP 8 .B z -È󰵽̳ÊǼ¡£ -¥¢¡¼¥«¥¤¥Ö¤Ø¤Î¥Õ¥¡¥¤¥ë¤Î³ÊǼ»þ¤Ë°µ½Ì¤ò¹Ô¤Ê¤¤¤Þ¤»¤ó¡£ -¤¹¤Ç¤Ë°µ½Ì¤ò¹Ô¤Ê¤Ã¤Æ¤¤¤Æ°µ½Ì¸úΨ¤ò˾¤á¤Ê¤¤¾ì¹ç¤Ê¤É¤Ë»ÈÍѤ·¤Þ¤¹¡£ -Ãê½Ð»þ¤Ë¤Ï̵»ë¤µ¤ì¤Þ¤¹¡£ +非圧縮格納。 +アーカイブへのファイルの格納時に圧縮を行ないません。 +すでに圧縮を行なっていて圧縮効率を望めない場合などに使用します。 +抽出時には無視されます。 .TP 8 .B g -[generic]¥¢¡¼¥«¥¤¥Ö¤ÎºîÀ®¡£ -UNIX ÍѤÎÉղþðÊó¤ò»ý¤¿¤Ê¤¤¥¢¡¼¥«¥¤¥Ö¥Õ¥¡¥¤¥ë¤òÀ¸À®¤·¤Þ¤¹¡£ -Ãê½Ð»þ¤Ë¤Ï̵»ë¤µ¤ì¤Þ¤¹¡£ +[generic]アーカイブの作成。 +UNIX 用の付加情報を持たないアーカイブファイルを生成します。 +抽出時には無視されます。 .TP 8 .B 0/1/2 -¥Ø¥Ã¥À¥ì¥Ù¥ë»ØÄê¡£ -¥¢¡¼¥«¥¤¥Ö¤ÎÆâÉôɽ¸½¤Î·Á¼°¤ò»ØÄꤷ¤Þ¤¹¡£ -¥Ç¥Õ¥©¥ë¥È¤Ç¤Ï 1 ¤Ç¤¹¡£0 ¤Ï LHarc ¤ÇÍÑ°Õ¤µ¤ì¤Æ¤¤ -¤¿¸Å¤¤·Á¼°¤Ç¤¹¡£¾­Íè¤Ï 2 ¤Î·Á¼°¤ËÅý°ì¤µ¤ì¤ë¤è¤¦¤Ç¤¹¡£ -¤³¤ì¤Ï¥Õ¥¡¥¤¥ë¤Î³ÊǼ»þ¤Î¤ßÍ­¸ú¤Ç¤¹¡£¥Õ¥¡¥¤¥ë¤ÎÃê½Ð»þ -¤Ë¤Ï¼«Æ°Åª¤Ë·Á¼°¤òȽÃǤ·½èÍý¤·¤Þ¤¹¡£ +ヘッダレベル指定。 +アーカイブの内部表現の形式を指定します。 +デフォルトでは 1 です。0 は LHarc で用意されてい +た古い形式です。将来は 2 の形式に統一されるようです。 +これはファイルの格納時のみ有効です。ファイルの抽出時 +には自動的に形式を判断し処理します。 .PP .I archive_file -¤Ë¤Ï¥¢¡¼¥«¥¤¥Ö¥Õ¥¡¥¤¥ë¤ò»ØÄꤷ¤Þ¤¹¡£ +にはアーカイブファイルを指定します。 .PP .I archive_file -¤Ë +に .I - -¤È½ñ¤¯¤³¤È¤Ë¤è¤Ã¤Æ¡¢¥¢¡¼¥«¥¤¥Ö¥Õ¥¡¥¤¥ë¤òɸ½àÆþ½ÐÎÏ -¤Ë¤¹¤ë¤³¤È¤¬²Äǽ¤Ç¤¹¡£ +と書くことによって、アーカイブファイルを標準入出力 +にすることが可能です。 .BR tar (1) -¤Î¤è¤¦¤Ë¡¢¥¢¡¼¥«¥¤¥Ö¤ÎºîÀ®¡¢¹¹¿·»þ¤Ë¤Ïɸ½à½ÐÎϤˡ¢ -¥¢¡¼¥«¥¤¥Ö¤«¤é¤Î¥Õ¥¡¥¤¥ë¤ÎÃê½Ð»þ¤Ë¤Ïɸ½àÆþÎϤˤʤê¤Þ¤¹¡£ +のように、アーカイブの作成、更新時には標準出力に、 +アーカイブからのファイルの抽出時には標準入力になります。 .PP -Ãê½Ð»þ¤Ë +抽出時に .I archive_file -¤¬Â¸ºß¤·¤Ê¤¤»þ¤Ë¤Ï¥µ¥Õ¥£¥Ã¥¯¥¹ +が存在しない時にはサフィックス .B ".lzh" -¤ò¥Õ¥¡¥¤¥ë̾¤Î¸å¤í¤ËÉղä·¤ÆÃê½Ð¤ò»î¤ß¤Þ¤¹¡£ +をファイル名の後ろに付加して抽出を試みます。 .br -Æä˵¬Äê¤Ï¤¢¤ê¤Þ¤»¤ó¤¬¡¢±¿ÍѾ奵¥Õ¥£¥Ã¥¯¥¹¤È¤·¤Æ +特に規定はありませんが、運用上サフィックスとして .B ".lzh" -¤òÍѤ¤¤ë¤Î¤¬Ë¾¤Þ¤·¤¤¤È»×¤¤¤Þ¤¹¡£ -ÆüËܰʳ°¤Ç¤Ï +を用いるのが望ましいと思います。 +日本以外では .B ".lha" -¤ò»ÈÍѤ·¤Æ¤¤¤ë¥±¡¼¥¹¤¬¤¢¤ë¤½¤¦¤Ç¤¹¤Î¤Ç¡¢Å¬µ¹Ä´À° -¤·¤Æ²¼¤µ¤¤¡£:-) +を使用しているケースがあるそうですので、適宜調整 +して下さい。:-) .br -¥µ¥Õ¥£¥Ã¥¯¥¹¤¬ +サフィックスが .B "com" -¤â¤·¤¯¤Ï +もしくは .B ".exe" -¤Î¾ì¹ç¤Ë¤Ï¡¢MS-DOS ÈǤǺîÀ®¤µ¤ì¤¿ -.IR SFX (¼«¸ÊŸ³«µ¡Ç½ÉÕ¤­¤Î°µ½Ì¥Õ¥¡¥¤¥ë) -·Á¼°¤«¤É¤¦¤«¤ò¥Á¥§¥Ã¥¯¤·¡¢Âбþ¤·¤Þ¤¹¡£ +の場合には、MS-DOS 版で作成された +.IR SFX (自己展開機能付きの圧縮ファイル) +形式かどうかをチェックし、対応します。 .br -¥µ¥Õ¥£¥Ã¥¯¥¹¤¬ +サフィックスが .B ".x" -¤Î¾ì¹ç¤Ë¤Ï¡¢ÆüËÜÀ½ SHARP X68000 ¤Î OS, Human68k ÈÇ¤Ç -ºîÀ®¤µ¤ì¤¿ SFX ·Á¼°¤«¤É¤¦¤«¤ò¥Á¥§¥Ã¥¯¤·Âбþ¤·¤Þ¤¹¡£ +の場合には、日本製 SHARP X68000 の OS, Human68k 版で +作成された SFX 形式かどうかをチェックし対応します。 .br -SFX ·Á¼°¤Î¥¢¡¼¥«¥¤¥Ö¥Õ¥¡¥¤¥ë¤ËÂФ·¤ÆÄɲääºï½ü¤ò -¹Ô¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢¥µ¥Õ¥£¥Ã¥¯¥¹¤ò +SFX 形式のアーカイブファイルに対して追加や削除を +行った場合には、サフィックスを .B ".lzh" -¤ËÊѹ¹¤·¡¢SFX ¤Î¾ðÊó¤òºï½ü¤·¤Þ¤¹¡£ +に変更し、SFX の情報を削除します。 .PP -.SH "FILES ¥Õ¥¡¥¤¥ë" +.SH "FILES ファイル" .ta \w'*.com *.exe 'u -*.lzh - LHa/LHarc ¥¢¡¼¥«¥¤¥Ö¥Õ¥¡¥¤¥ë +*.lzh - LHa/LHarc アーカイブファイル .br -*.bak - ¥Ð¥Ã¥¯¥¢¥Ã¥×¥Õ¥¡¥¤¥ë +*.bak - バックアップファイル .br -/tmp/lh* - ¥Æ¥ó¥Ý¥é¥ê¥Õ¥¡¥¤¥ë +/tmp/lh* - テンポラリファイル .br -*.com *.exe - MS-DOS SFX ¥Õ¥¡¥¤¥ë +*.com *.exe - MS-DOS SFX ファイル .br -*.x - Human68k SFX ¥Õ¥¡¥¤¥ë +*.x - Human68k SFX ファイル .PP -.SH "SEE ALSO ´ØÏ¢¹àÌÜ" +.SH "SEE ALSO 関連項目" .BR tar (1), .BR ar (1), .BR compress (1) -.SH "DISTRIBUTION ºÆÇÛÉۤˤĤ¤¤Æ" +.SH "DISTRIBUTION 再配布について" .PP -°Ê²¼¤Î¾ò·ï¤Ç¡¢ºÆÇÛÉÛ¡¢Å¾ºÜ¡¢²þÊѤòµö²Ä¤·¤Þ¤¹¡£ +以下の条件で、再配布、転載、改変を許可します。 .IP 1. -Ãøºî¸¢É½¼¨¤òºï½ü¤·¤Ê¤¤¤³¤È¡£ +著作権表示を削除しないこと。 .IP 2. -ÇÛÉÛÆâÍƤˤĤ¤¤Æ¤Ï¡¢ +配布内容については、 .RS .IP a. -ÇÛÉۤκݤ˸ºß¤¹¤ëÆâÍÆ(¤¹¤Ê¤ï¤Á¥½¡¼¥¹¥³¡¼¥É¡¢¥É¥­¥å¥á¥ó¥È¡¢ -¥×¥í¥°¥é¥Þ¡¼¤Ø¤Î¼ê°ú¤­¤Ê¤É)¤¬ºÆÇÛÉÛ¤µ¤ì¤¿¤â¤Î¤ÎÃæ¤Ëɬ¤ºÂ¸ -ºß¤¹¤ë¤³¤È¡£²þÊѤµ¤ì¤Æ¤¤¤ë¤Ê¤é¤Ð¡¢¤½¤ì¤òÌÀ¼¨¤·¤¿¥É¥­¥å¥á -¥ó¥È¤òÍÑ°Õ¤¹¤ë¤³¤È¡£ +配布の際に存在する内容(すなわちソースコード、ドキュメント、 +プログラマーへの手引きなど)が再配布されたものの中に必ず存 +在すること。改変されているならば、それを明示したドキュメ +ントを用意すること。 .IP b. -LHa ¤ËÂФ¹¤ëÉղòÁÃͤ¬ÉÕ¤±¤é¤ì¤ÆºÆÇÛÉÛ¤µ¤ì¤ë¾ì¹ç¤Ë¤Ï¤½¤ì¤é -¤â¤Ç¤­¤ë¤À¤±´Þ¤á¤ë¤è¤¦ÅØÎϤ¹¤ë¤³¤È¡£¤Þ¤¿¡¢¤½¤ÎºÝ¤Ë¤ÏÉղòÁ -Ãͤ¬ÉÕ¤±¤é¤ì¤Æ¤¤¤ë¤³¤È¤òÌÀ¼¨¤·¤¿¥É¥­¥å¥á¥ó¥È¤òÍÑ°Õ¤¹¤ë¤³¤È¡£ +LHa に対する付加価値が付けられて再配布される場合にはそれら +もできるだけ含めるよう努力すること。また、その際には付加価 +値が付けられていることを明示したドキュメントを用意すること。 .IP c. -¥Ð¥¤¥Ê¥ê¤Î¤ß¤ÎÇÛÉۤϵö¤µ¤ì¤Ê¤¤¡£(ÉղòÁÃͤΤâ¤Î¤â´Þ¤à) +バイナリのみの配布は許されない。(付加価値のものも含む) .RE .IP 3. -ºÇ¿·ÈǤÎÇÛÉÛ¤Ë̳¤á¤ë¤³¤È¡£(µÁ̳¤Ï¤Ê¤¤) +最新版の配布に務めること。(義務はない) .RS -.IP Ãí. -¤Ê¤ª¡¢¥Í¥Ã¥È¤Ç¤ÎÇÛÉդϼ«Í³¤Ç¤¢¤ë¤¬¡¢¥Í¥Ã¥È¤Ë¥¢¥¯¥»¥¹¤Ç¤­¤Ê¤¤Êý¡Ê»¨»ï¤ª¤è¤Ó¡¢ -CD-ROM ¤Ê¤É¤Ë¤è¤ë¡ËÇÛÉդϡ¢ÇÛÉÕÁ°¤Ë¤³¤Á¤é¤Ë E-Mail ¤ò¤ª´ê¤¤¤·¤Þ¤¹¡£ -ÇÛÉÕÁ°¤Ë½ÐÍè¤Ê¤¤ºÝ¤Ë¤Ï¡¢¸åÆüɬ¤º E-Mail ¤ò¤ª´ê¤¤¤·¤Þ¤¹¡£ +.IP 注. +なお、ネットでの配付は自由であるが、ネットにアクセスできない方(雑誌および、 +CD-ROM などによる)配付は、配付前にこちらに E-Mail をお願いします。 +配付前に出来ない際には、後日必ず E-Mail をお願いします。 .RE .IP 4. -¤³¤Î¥×¥í¥°¥é¥à¤Î¸ºß¤ä»ÈÍѤ·¤¿¤³¤È¤Ë¤è¤Ã¤ÆÀ¸¤¸¤¿Â»³²¤ÏÁ´¤¯ÊÝ -¾Ú¤·¤Ê¤¤¡£ +このプログラムの存在や使用したことによって生じた損害は全く保 +証しない。 .IP 5. -ºî¼Ô¤Ï¡¢¤³¤Î¥×¥í¥°¥é¥à¤ËÉÔÈ÷¤¬¤¢¤Ã¤Æ¤â¡¢¤½¤ì¤òÄûÀµ¤¹¤ëµÁ̳¤ò -Éé¤ï¤Ê¤¤¡£ +作者は、このプログラムに不備があっても、それを訂正する義務を +負わない。 .IP 6. -¤³¤Î¥×¥í¥°¥é¥à¤Î°ìÉô¡¢¤Þ¤¿¤ÏÁ´Éô¤ò¾¤Î¥×¥í¥°¥é¥à¤ËÁȤ߹þ¤ó¤Ç -ÍøÍѤ·¤Æ¤â¤«¤Þ¤ï¤Ê¤¤¡£¤³¤Î¾ì¹ç¡¢¤½¤Î¥×¥í¥°¥é¥à¤Ï LHa ¤Ç¤Ï¤Ê¤¯¡¢ -LHa ¤È̾¾è¤Ã¤Æ¤Ï¤¤¤±¤Ê¤¤¡£ +このプログラムの一部、または全部を他のプログラムに組み込んで +利用してもかまわない。この場合、そのプログラムは LHa ではなく、 +LHa と名乗ってはいけない。 .IP 7. -¾¦ÍøÍѤ˴ؤ·¤Æ¤Ï¡¢¾åµ­¤Î¾ò·ï¤Ë²Ã¤¨¡¢²¼µ­¤Î¾ò·ï¤Î¤â¤È¤Ë¤³¤ì¤ò -ǧ¤á¤ë¡£ +商利用に関しては、上記の条件に加え、下記の条件のもとにこれを +認める。 .RS .IP a. -¤³¤Î¥×¥í¥°¥é¥à¤ò¥á¥¤¥ó¤È¤¹¤ë¾¦ÍøÍѤ϶ػߤ¹¤ë¡£ +このプログラムをメインとする商利用は禁止する。 .IP b. -¾¦ÍøÍѤÎÁê¼ê¤¬¤³¤Î¥×¥í¥°¥é¥à¤Î»ÈÍѼԤȤ·¤ÆÉÔŬÀÚ¤ÈȽÃǤ· -¤¿¾ì¹ç¤Ë¤ÏÇÛÉÛ¤·¤Ê¤¤¡£ +商利用の相手がこのプログラムの使用者として不適切と判断し +た場合には配布しない。 .IP c. -¥¤¥ó¥¹¥È¡¼¥ë¤Î¼êÃʤȤ·¤Æ»ÈÍѤ¹¤ë¾ì¹ç¡¢¤³¤Î¥×¥í¥°¥é¥à¤ò»È -¤¦¤³¤È¤òÁê¼ê¤Ë¶¯À©¤·¤Ê¤¤¡£¤³¤Î¾ì¹ç¡¢¾¦ÍøÍѼԤ¬ºî¶È¤ò¹Ô¤¦¡£ -¤Þ¤¿¡¢¤½¤Î¤È¤­¤Î»³²¤Ï¡¢¾¦ÍøÍѼԤ¬Á´ÀÕǤ¤òÉ餦¡£ +インストールの手段として使用する場合、このプログラムを使 +うことを相手に強制しない。この場合、商利用者が作業を行う。 +また、そのときの損害は、商利用者が全責任を負う。 .IP d. -¾¦ÍøÍѤòÉղòÁÃͤȤ·¤Æ¹Ô¤¤¤³¤Î¥×¥í¥°¥é¥à¤ò»ÈÍѤ¹¤ë¾ì¹ç¡¢ -¾¦ÍøÍѼԤϡ¢¤½¤Î¥µ¥Ý¡¼¥È¤ò¹Ô¤¦¡£ +商利用を付加価値として行いこのプログラムを使用する場合、 +商利用者は、そのサポートを行う。 .RE .PP -.SH "ºÇ¸å¤Ë(From Y.Tagawa)" -LZHUF Ë¡¤Î´ðÁäȤʤä¿ LZARI Ë¡¤òȯɽ¤·¤Æ¤¯¤À¤µ¤Ã¤¿±ü¼À²É§»á¡¢ -¤½¤ì¤ò NIFTY-Serve ¤Ë¾Ò²ð¤·¡¢¤Þ¤¿¡¢LArc ¤Îºî¼Ô¤Ç¤â¤¢¤ë»°ÌÚÏÂɧ»á¡¢ -¤Þ¤¿¡¢¤³¤ì¤é¤ÎÍ×°ø¤«¤é LZHUF Ë¡µÚ¤Ó¡¢MS-DOS ÈÇ LHarc ¤òºîÀ®¤·¤¿ -µÈºê±ÉÂٻᡢLHarc UNIX ¤Î³«È¯¤Ë¶¨ÎϤ·¤¿¿Íã¡¢¤³¤³¤í¤è¤¯¥á¥Ã¥»¡¼¥¸ -¤Î¶¶ÅϤ·¤ò¤·¤Æ¤¯¤ì¤¿Àкê°ìÌÀ»á(MIX ID:k.ishi)¡¢¤Þ¤¿¡¢¤¤¤í¤¤¤í¤Ê -¥ì¥Ý¡¼¥È¤ò¤¯¤ì¤¿¤ß¤Ê¤µ¤ó¡¢¤½¤ì¤òÃæ·Ñ¤·¤Æ¤¯¤À¤µ¤Ã¤¿¿¹¸ø°ìϺ -»á (MIX ID:kmori)¤Ë´¶¼Õ¤·¤Þ¤¹¡£ +.SH "最後に(From Y.Tagawa)" +LZHUF 法の基礎となった LZARI 法を発表してくださった奥村晴彦氏、 +それを NIFTY-Serve に紹介し、また、LArc の作者でもある三木和彦氏、 +また、これらの要因から LZHUF 法及び、MS-DOS 版 LHarc を作成した +吉崎栄泰氏、LHarc UNIX の開発に協力した人達、こころよくメッセージ +の橋渡しをしてくれた石崎一明氏(MIX ID:k.ishi)、また、いろいろな +レポートをくれたみなさん、それを中継してくださった森公一郎 +氏 (MIX ID:kmori)に感謝します。 .PP -.SH "ºÇ¸å¤Ë(From Masaru Oki)" -¾åµ­¤ÎÊý¡¹¤Ï¤â¤Á¤í¤ó¡¢²Ã¤¨¤Æ LHarc UNIX ¤òºîÀ®¤·¤¿ Y.Tagawa »á¡¢ -¤½¤ì¤ò OSK ¤Ë°Ü¿¢¤·µÈºê»á¤Î LHx ¤Î¥¢¥ë¥´¥ê¥º¥à¤òÁȤ߹þ¤ó¤À H.S »á¡¢ -JUNET ¤Î LHa for UNIX MailingList ¤Ë¤Æ¶¨ÎϤ·¤Æ¤¯¤À¤µ¤Ã¤¿¤ß¤Ê¤µ¤ó¤Ë -´¶¼ÕÃפ·¤Þ¤¹¡£ -.SH "ºÇ¸å¤Ë(From Nobutaka Watazaki)" -¤³¤Î¥×¥í¥°¥é¥à¤ÎºîÀ®¤Ë¤«¤«¤ï¤Ã¤¿Êý¡¹¡¢¤µ¤é¤Ë¤³¤³¤Þ¤Ç LHa ¤ò»Å -¾å¤²¤Æ¤¯¤À¤µ¤Ã¤¿ ²­¾¡»á¤Ë´¶¼ÕÃפ·¤Þ¤¹¡£ +.SH "最後に(From Masaru Oki)" +上記の方々はもちろん、加えて LHarc UNIX を作成した Y.Tagawa 氏、 +それを OSK に移植し吉崎氏の LHx のアルゴリズムを組み込んだ H.S 氏、 +JUNET の LHa for UNIX MailingList にて協力してくださったみなさんに +感謝致します。 +.SH "最後に(From Nobutaka Watazaki)" +このプログラムの作成にかかわった方々、さらにここまで LHa を仕 +上げてくださった 沖勝氏に感謝致します。