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_fast
や deflate_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