libz

Table of Contents

a2pdf

物は試しに入れてみた。textをPDFに変換してくれるperlさんね。 デフォルトの設定が、リレーするには(PDFをグルルで翻訳させる為のバトン) 不向きになってる。ちゃんとヘッダーとか行番号を追加したりだ。 簡単に変更できないかしら。開けソースしてみる。

# Set default options
my %options = (
  header        => 1,          # Include header on all pages
  footer        => 1,          # Include footer on all pages
  line_numbers  => 1,          # Print line numbers
  page_width    => 595,        # A4
  page_height   => 842,        # A4
  left_margin   => 48,         # 0.75"
  right_margin  => 48,         # 0.75"
  top_margin    => 60,         #
  bottom_margin => 60,         #
  font_face     => 'Courier',  # Monospaged text
  font_size     => 9,          # Text size = 9 points
  perl_syntax   => 1,          # Perform Perl syntax highlighting
  icon_scale    => 0.5,        # Icon scaling (%age)
  );

どうやらperlのソースを美文書でPDFしましょってのが、趣旨みたいだな。テ ケトーに変更してしまえ!

上記変更を施したa2pdfを使用して、zlib.hのPDF作成。それを翻訳。Machine Translated by Google なんて言う肩書のPDFが返却された。全30ページの作品。 永久保存版にしよう。原文は何時でも読めますから。

manの原稿

manも場合によっては非常に長い場合がある。たとえば、deflateって関数は、 まとめてcomplessに載ってる。zlib.hの別な解説になるかな。そんな時は、下 記の様にして、man原稿をテキストに変換すれば良い。後は、a2pdf へとバト ンタッチだな。

vbox$ man -T ascii -O width=72 deflate | col -b > compress.txt

ratio vs speed

前回からやってるlibzがらみで、gzipのマニュアルを見ていたんだ。そしたら、

-1...9  Use the deflate scheme, with compression factor of -1 to -9.
        Compression factor -1 is the fastest, but provides a poorer level
        of compression.  Compression factor -9 provides the best level of
        compression, but is relatively slow.  The default is -6.

こんな説明が目にとまった。圧縮率と速度は両立しませんよ、だからユーザー が目的に合わせて選択してください。ならばベンチしたいぞ。

その材料はどうする? で、目につけたのが各種コマンドという実バイナリー・ ファイル。一番大きいのはだれ?

vbox$ cd /usr/local/bin
vbox$ ls -s | sort -nr | head -5
20576 egdb*
12544 rsvg-convert*
 7872 git-upload-archive*
 7872 git*
 4928 emacs-29.1*

追加エリアの横綱は、gdbさんか。

vbox$ cd /usr/bin
vbox$ ls -s | sort -nr | head -5
159232 clang-cpp*
159232 clang++*
159232 clang*
159232 cc*
159232 c++*

元々居る人でジャイアンツさんはclang-cppとな。よくサイズを見ると同一だ。 実体は一つって言う得意な手法を使っているな。

vbox$ time gzip -1 clang-cpp
    0m04.26s real     0m01.73s user     0m02.38s system
vbox$ gzip -lv clang-cpp.gz
method  crc      date   time  compressed  uncompressed  ratio  uncompressed_name
deflate 9c9f3cec Nov 07 14:02   33021067      81463340  59.5%  clang-cpp
vbox$ time gunzip clang-cpp.gz
    0m02.34s real     0m00.61s user     0m01.74s system
vbox$ time gzip -9 clang-cpp
    0m14.81s real     0m12.57s user     0m02.20s system
vbox$ gzip -lv clang-cpp.gz
method  crc      date   time  compressed  uncompressed  ratio  uncompressed_name
deflate 9c9f3cec Nov 07 14:02   29762364      81463340  63.5%  clang-cpp
vbox$ time gunzip clang-cpp.gz
    0m02.17s real     0m00.51s user     0m01.66s system

今度はソース・ファイルで最大な物を探してみましょ。

vbox$ find . -type f -name '*.c' -exec ls -s {} + | sort -nr | head -5
8448 ./gnu/usr.bin/binutils-2.17/opcodes/m32c-desc.c
7968 ./gnu/usr.bin/binutils-2.17/opcodes/m32c-opc.c
2144 ./gnu/usr.bin/perl/regcomp.c
1472 ./gnu/usr.bin/gcc/gcc/f/stb.c
1408 ./gnu/usr.bin/gcc/gcc/java/parse.c
vbox$ wc m32c-desc.c
   63176  424849 4295722 m32c-desc.c

ソースと言うよりデータが充填された化け物です。

vbox$ gzip -1 m32c-desc.c
vbox$ gzip -l m32c-desc.c.gz
compressed  uncompressed  ratio  uncompressed_name
    279731       4295722  93.5%  m32c-desc.c

同じ様なデータが並んでいるので、高効率で圧縮ができるとな。 もっと一般的って事で、usr/binのソースを対象にしてみる。

vbox$ find usr.bin -type f -name '*.c' -exec ls -s {} + | sort -nr | head -5
448 usr.bin/ssh/ed25519.c
296 usr.bin/tmux/window-copy.c
284 usr.bin/ssh/channels.c
228 usr.bin/tmux/format.c
216 usr.bin/ssh/ssh-keygen.c

ssh,tmuxなんてのが上位に喰い込んでいるとな。

vbox$ gzip -l ed25519.c.gz
compressed  uncompressed  ratio  uncompressed_name
     45236        201545  77.6%  ed25519.c

これぐらいが、標準なのか。

言うまでもないけど、ratioって大きい程嬉しい。それだけエリアが節約でき ましたって意味だから。

もうすぐワイン好きには堪らない日がやってくる。おフランスあたりからやっ て来る、新酒の解禁日。空輸されてくるんだな。水分が90%ぐらいで無駄だろ う。成分だけ、圧縮して空輸したら、随分と運送費が安くなるだろう。

圧縮液が日本に到着したら、水を使って展開すればオーケー。それだと、ボト ル イン ジャパン ってなって、有り難みが無くなるか。ひょっとして安いワ インは、そんな裏技が使われていたりして。

ヨーロッパあたりの水は、硬水で飲めたものではない。だから、ドイツあたり では、水の代わりにビールを呑むのさ。初めてドイツに駐留した時、出社した 頃を見計らって下痢してた。どうにも症状が長びいたので、周りの人に相談し たら、水道からの水を直接使ってませんかと質問された。それで、硬水は、そ のままじゃ駄目って知った次第。

話がそれた。 gzipはcompress一族になっている。-1 から -9 は、こんな風に処理されてい る(main.c)。

                switch (ch) {
                case '1':
                  :
                case '9':
                        method = M_DEFLATE;
                        strlcpy(suffix, method->suffix, sizeof(suffix));
                        bits = ch - '0';
                        break;

static int
docompress(const char *in, char *out, const struct compressor *method,
    int bits, struct stat *sb)
{
          :
        if ((cookie = method->wopen(ofd, name, bits, mtime)) == NULL) {
(gdb) b docompress
Breakpoint 1 at 0x536a: file main.c, line 561.
(gdb) r -3 gzopen.o
Starting program: /tmp/compress/gzip -3 gzopen.o

Breakpoint 1, docompress (in=0x6b4ec500 "gzopen.o",
    out=0xcf7f8278 "gzopen.o.gz", method=0x3bdf4018 <c_table>, bits=3,
    sb=0x6b4fabb8) at main.c:561

-3 とかの指定が、bits=3 に反映されてる。 gzopen.c/ gz_wopen() の中で 呼んでる

if (deflateInit2(&(s->z_stream), bits, Z_DEFLATED,
-MAX_WBITS, DEF_MEM_LEVEL, 0) != Z_OK) {

に渡っていくのか。このままgdbを使うと、 釣瓶落としの日が暮れそうなので、先回りして関係部署にガサ入れしとく。 ひょっとして、これかな? deflate.c

local const config configuration_table[10] = {
/*      good lazy nice chain */
/* 0 */ {0,    0,  0,    0, deflate_stored},  /* store only */
/* 1 */ {4,    4,  8,    4, deflate_fast}, /* max speed, no lazy matches */
/* 2 */ {4,    5, 16,    8, deflate_fast},
/* 3 */ {4,    6, 32,   32, deflate_fast},

/* 4 */ {4,    4, 16,   16, deflate_slow},  /* lazy matches */
/* 5 */ {8,   16, 32,   32, deflate_slow},
/* 6 */ {8,   16, 128, 128, deflate_slow},
/* 7 */ {8,   32, 128, 256, deflate_slow},
/* 8 */ {32, 128, 258, 1024, deflate_slow},
/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */

local void lm_init(deflate_state *s) {
    s->max_lazy_match   = configuration_table[s->level].max_lazy;
    s->good_match       = configuration_table[s->level].good_length;
    s->nice_match       = configuration_table[s->level].nice_length;
    s->max_chain_length = configuration_table[s->level].max_chain;

炙り出しモードに突入。

(gdb) r -5 zforce
Starting program: /tmp/compress/gzip -5 zforce

Breakpoint 1, lm_init (s=0x65ad1000) at /usr/src/lib/libz/deflate.c:664
664         s->window_size = (ulg)2L*s->w_size;
(gdb) p s->level
$3 = 5
(gdb) bt
#0  lm_init (s=0x65ad1000) at /usr/src/lib/libz/deflate.c:664
#1  deflateReset (strm=<optimized out>) at /usr/src/lib/libz/deflate.c:690
#2  deflateInit2_ (strm=0x408cd008, level=<optimized out>, method=8,
    windowBits=15, memLevel=8, strategy=0, version=0x182c116e "1.3",
    stream_size=56) at /usr/src/lib/libz/deflate.c:513
#3  0x182c8fdd in gz_wopen (fd=4, name=0x2e457704 <basename.bname> "zforce",
    bits=5, mtime=1699506007) at gzopen.c:408
#4  0x182c555c in docompress (in=0x63641a00 "zforce",
    out=0xcf7e74e8 "zforce.gz", method=0x382c0018 <c_table>, bits=5,
    sb=0x6363b674) at main.c:592
#5  0x182c446c in main (argc=1, argv=0xcf7e795c) at main.c:468

ビンゴ。後は、それぞれのパラメータの利用方法を調査。いや、それより、大 局の deflate_fastdeflate_slow の違いを見るのが先だろうに。

/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */

/* Values for max_lazy_match, good_match and max_chain_length, depending on
 * the desired pack level (0..9). The values given below have been tuned to
 * exclude worst case performance for pathological files. Better values may be
 * found for specific files.
 */
typedef struct config_s {
   ush good_length; /* reduce lazy search above this match length */
   ush max_lazy;    /* do not perform lazy search above this match length */
   ush nice_length; /* quit search above this match length */
   ush max_chain;
   compress_func func;
} config;

こういう情報を得てからかな。

compress type

main.cに定義されてた、アレ、何を意味するか想像シレ

#define M_UNZIP (&c_table[2]) {
                "unzip",
                ".zip",
                "PK",
#define M_COMPRESS (&c_table[1]) {
                "compress",
                ".Z",
                "\037\235",
#define M_DEFLATE (&c_table[0]) {
                "deflate",
                ".gz",
                "\037\213",

馬鹿とエラーの使い道

libzは機能豊富。でも全部は使っていないっぽい。どんなの使ってる。 libzをリンクしなかったら、リンカーが文句を言ってくる。それを見れば、一 目瞭然さ。

cc   -o compress main.o zopen.o gzopen.o zipopen.o nullopen.o
ld: error: undefined symbol: crc32
>>> referenced by gzopen.c
>>>               gzopen.o:(gz_ropen)
 :
ld: error: undefined symbol: inflateInit2_
>>> referenced by gzopen.c
>>>               gzopen.o:(gz_ropen)
 :
ld: error: undefined symbol: deflate
>>> referenced by gzopen.c
>>>               gzopen.o:(gz_close)
 :

これを上手に処理したら、楽しいスクリプトが出来そうだな。

tar z…

OpenBSDのtarの圧縮オプションには、下記が有ると説明されてた。

-j      Compress archive using bzip2.  The bzip2 utility must be
        installed separately.
-Z      Compress archive using compress(1).
-z      Compress archive using gzip(1).

いざソースを閲覧しようとしたら、tarはなかった。上で見たように、コマン ドの合体かと思って探したら、pax族になってた。cpip,tarが同居してる。

pax.cのmainで振り分けしてるかと思ったら、options.cにまとめてあった。

#define NM_TAR  "tar"
#define NM_CPIO "cpio"
#define NM_PAX  "pax"

#define GZIP_CMD        "gzip"          /* command to run as gzip */
#define COMPRESS_CMD    "compress"      /* command to run as compress */
#define BZIP2_CMD       "bzip2"         /* command to run as bzip2 */

options(int argc, char **argv)
{
        argv0 = __progname;

        if (strcmp(NM_TAR, argv0) == 0) {
                op_mode = OP_TAR;
                tar_options(argc, argv);
                return;
        }

tar_options(int argc, char **argv)
{
                case 'z':
                        /*
                         * use gzip.  Non standard option.
                         */
                        gzip_program = GZIP_CMD;
                        break;
                case 'Z':
                        /*
                         * use compress.
                         */
                        gzip_program = COMPRESS_CMD;
                        break;

zオプションが標準じゃないって、随分昔のコードだな。

で、実際にgzipをハンドリングしてるのは、 ar_io.c に有り。tarが親、 gzipが子供という親子のパイプを形成してた。ユーザーがshellを駆使してパ イプを使うのと同じ事をtar内でやってるのか。 だから

vbox$ ldd /bin/tar
/bin/tar:
        Start    End      Type  Open Ref GrpRef Name
        04f47000 24f60000 dlib  1    0   0      /bin/tar

この様にtarコマンドの成分分析しても、libz を同梱していないのさ。これが 由緒正しいunixってもんです。

gtar

いわゆるGNU tarを称してBSD界隈では、gtarと呼び在来種と区別してる。同様 なものにgmakeが有ったりする。これらは便利さ優先になってる。gtarは各種 の圧縮を統一的に扱うサービスになっている。

一番新しい奴で確認。

tar-latest.tar.gz            2023-07-18 16:16 4.4M
tar-latest.tar.bz2           2023-07-18 16:16 3.1M
tar-latest.tar.xz            2023-07-18 16:16 2.2M

圧縮性能の陳列台です。

[sakae@deb z]$ file tar-latest.tar.xz
tar-latest.tar.xz: XZ compressed data
[sakae@deb z]$ tar xf tar-latest.tar.xz

展開も面倒な圧縮タイプ指定が不要だ。manから圧縮オプションを拾ってみる と

-j, --bzip2
       Filter the archive through bzip2(1).
-J, --xz
       Filter the archive through xz(1).
--lzip Filter the archive through lzip(1).
--lzma Filter the archive through lzma(1).
--lzop Filter the archive through lzop(1).
-z, --gzip, --gunzip, --ungzip
       Filter the archive through gzip(1).
-Z, --compress, --uncompress
       Filter the archive through compress(1).
--zstd Filter the archive through zstd(1).

もう、世の中の圧縮機構を全部集めてみましたって趣。

ざっと見、内部でパイプを作成して、圧縮機械と接続してるね。まあ、考える 事はBSDの人と一緒だわな。大きなくくりでunixだもの。

emacs

以前にやったけど、gz化されたファイルを、そのまま開いて閲覧した。そして、 今確認したけど、それを編集して保存できた。もちろん結果はgzのまま。

この状況証拠からlibzを利用してるんだろうけど、更に証拠を積み重ねてみた い。

vbox$ ldd /usr/local/bin/emacs-29.1 | grep libz
        0522e000 25230000 rlib  0    4   0      /usr/lib/libz.so.7.0
        00416000 20418000 rlib  0    1   0      /usr/local/lib/libzstd.so.6.3

zstdなんてのもサポートしてるんか。RFCになってるんで、標準を取り入れし ましたって事だな。開発社のフェースブックさんの願いが叶ったってわけだ。

どんな風に使ってる? zlib.hをインクルードしてるのを探せばいいんだな。

vbox$ grep zlib.h *
comp.c:#include "zlib.h"
decompress.c:#include <zlib.h>
decompress.c:     zlib formats" according to zlib.h.  */

example of zlib

例が幾つか添付されている。迷わずに一番短かいのを、やり玉に挙げてみる。

[sakae@fb /tmp/zlib-1.3/examples]$ cc -g zpipe.c
ld: error: undefined symbol: deflateInit_
>>> referenced by zpipe.c:48
>>>               /tmp/zpipe-07bbd0.o:(def)
 :
[sakae@fb /tmp/zlib-1.3/examples]$ cc -g zpipe.c -lz
[sakae@fb /tmp/zlib-1.3/examples]$ echo hello | ./a.out | ./a.out -d
hello

libzの指定を忘れてエラーの洗礼を受けたので、往復ビンタの返礼。圧縮して、 それを解凍すれば、元に戻るでしょ。

[sakae@fb /tmp/zlib-1.3/examples]$ echo hello | ./a.out > hello.gz
[sakae@fb /tmp/zlib-1.3/examples]$ gzip -l hello.gz
gzip: hello.gz: not in gzip format
[sakae@fb /tmp/zlib-1.3/examples]$ file hello.gz
hello.gz: zlib compressed data
[sakae@fb /tmp/zlib-1.3/examples]$ hd hello.gz
00000000  78 9c cb 48 cd c9 c9 e7  02 00 08 4b 02 1f        |x..H.......K..|
0000000e

このダンプリストを見ると、余計なデータは含まれていない。パイプに流すた めに用意してるんだな。Webのデータなら、往々にしてファイル名なんて不要 の場合が多い。そんな時に使ってください、だな。

こちらにも説明があった。 LZ77 符号 (LZSS 符号)

libz in kernel

OpenBSDのカーネル・ソースにlibzが含まれている。どんな用のために、使っ てるの?

[sakae@deb sys]$ find . -name '*.c' | xargs grep -l inflate
./dev/microcode/myx/build.c
./dev/pci/drm/i915/i915_perf.c
./dev/pci/drm/drm_file.c
./dev/pv/viomb.c
./ddb/db_ctf.c
./kern/subr_hibernate.c
./netinet/tcp_input.c
./net/ppp-deflate.c
./arch/i386/i386/hibernate_machdep.c
./arch/amd64/amd64/hibernate_machdep.c
./arch/loongson/loongson/hibernate_machdep.c
./lib/libsa/cread.c
./crypto/xform_ipcomp.c

解凍機構だから、やむにやまれず、迎合せざるを得ない場合もあるん だな。

[sakae@deb sys]$ find . -name '*.c' | xargs grep -l deflate
./dev/pci/drm/i915/i915_gpu_error.c
./dev/pv/viomb.c
./kern/subr_hibernate.c
./netinet/tcp_input.c
./netinet/ip_ipcomp.c
./net/if_ppp.c
./net/ppp-deflate.c
./net/pfkeyv2.c
 :
./crypto/xform_ipcomp.c
./crypto/cryptosoft.c
./crypto/xform.c

圧縮機構だから、カーネルが能動的に使うんだな。

libz in Linux kernel

直交性の観点から、同じ事をリナでもやってみた。

[sakae@deb linux-5.10.144]$ find . -name '*.c' | xargs grep -l deflate
./drivers/virt/vboxguest/vboxguest_core.c
./drivers/net/ppp/ppp_generic.c
./drivers/net/ppp/ppp_deflate.c
./drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c
./drivers/net/ethernet/chelsio/cxgb4/cudbg_zlib.c
./drivers/net/ethernet/chelsio/cxgb4/cxgb4_cudbg.c
./drivers/virtio/virtio_balloon.c
./drivers/gpu/drm/i915/i915_gpu_error.c
./drivers/misc/vmw_balloon.c
./drivers/crypto/hisilicon/zip/zip_crypto.c
./drivers/crypto/cavium/zip/zip_crypto.c
./drivers/crypto/cavium/zip/zip_deflate.c
./drivers/crypto/cavium/zip/zip_device.c
./drivers/crypto/cavium/zip/zip_main.c
./security/apparmor/apparmorfs.c
./security/apparmor/policy_unpack.c
./net/xfrm/xfrm_algo.c
./net/ipv4/tcp_timer.c
./net/dccp/timer.c
./fs/isofs/compress.c
./fs/pstore/platform.c
./fs/jffs2/compr_zlib.c
./fs/jffs2/compr_lzo.c
./fs/btrfs/zlib.c
./fs/ubifs/compress.c
./mm/vmstat.c
./arch/s390/tools/gen_facilities.c
./arch/powerpc/platforms/pseries/cmm.c
./arch/powerpc/kernel/nvram_64.c
./tools/testing/selftests/net/ipsec.c
./arch/s390/tools/gen_facilities.c
./arch/powerpc/platforms/pseries/cmm.c
./arch/powerpc/kernel/nvram_64.c
./tools/testing/selftests/net/ipsec.c
./tools/testing/selftests/powerpc/nx-gzip/gunz_test.c
./tools/testing/selftests/powerpc/nx-gzip/gzfht_test.c
./lib/zlib_inflate/inftrees.c
./lib/zlib_inflate/inflate.c
./lib/zlib_dfltcc/dfltcc_deflate.c
./lib/zlib_deflate/deftree.c
./lib/zlib_deflate/deflate_syms.c
./lib/zlib_deflate/deflate.c
./lib/decompress_bunzip2.c
./lib/inflate.c
./lib/decompress_unlzma.c
./crypto/testmgr.c
./crypto/deflate.c
./crypto/tcrypt.c

This year's Index

Home