basE91で少し効率よくバイナリをテキストに変換

バイナリを利用できない環境でのデータ転送時にUNIX環境では古くは uudecode, uuencode が，日本のパソコン通信では ish などが使われていました．近頃は base64 がよく使われているように感じます．

これらのツールではバイナリをASCIIで表現するためサイズが大きくなってしまいます．でも全てのASCIIを使っていません．

今回見つけたbasE91はASCIIの0x21-0x7Eのうち -, \, ' を除いた91文字を使ってデコードすることで効率よくするもののようです．

basE91 – binary to ASCII text encoding

早速試してみます．Debianパッケージがないかなと探しましたがありませんでした．

(以前要望されたことはあるよう #706078 – RFP: base91 — base91 encoder/decoder – Debian Bug report logs )

sourceを貰ってきてmakeします．
※hashは「 basE91 – Browse /basE91/0.6.0 at SourceForge.net 」で確認できる．

$ wget http://downloads.sourceforge.net/base91/base91-0.6.0.tar.gz
$ md5sum base91-0.6.0.tar.gz
e227841d900cc463a162bd79775aeb54 base91-0.6.0.tar.gz
$ sha1sum base91-0.6.0.tar.gz
00cfd573ec8b3d0160dbf53e2c7a49b99a1aa720 base91-0.6.0.tar.gz
$ tar tvf base91-0.6.0.tar.gz
-rw-r--r-- 0/0 575 2005-06-25 00:00 base91-0.6.0/AWK/README
-rwxr-xr-x 0/0 727 2006-11-02 05:13 base91-0.6.0/AWK/b91dec.awk
-rw-r--r-- 0/0 932 2006-09-04 03:00 base91-0.6.0/PHP4/README
-rw-r--r-- 0/0 1513 2006-11-02 05:13 base91-0.6.0/PHP4/base91.php
-rw-r--r-- 0/0 149 2006-09-04 03:00 base91-0.6.0/test/Makefile
-rw-r--r-- 0/0 4265 2006-11-02 05:13 base91-0.6.0/test/test.sh
-rw-r--r-- 0/0 332 2006-08-25 17:00 base91-0.6.0/DOS-asm/readme.txt
-rw-r--r-- 0/0 3487 2006-11-02 05:13 base91-0.6.0/DOS-asm/b91enc.asm
-rw-r--r-- 0/0 5034 2006-11-02 05:13 base91-0.6.0/Java/b91cli.java
-rw-r--r-- 0/0 3297 2006-11-02 05:13 base91-0.6.0/Java/basE91.java
-rw-r--r-- 0/0 1526 2006-11-02 05:13 base91-0.6.0/Java/license.txt
-rw-r--r-- 0/0 793 2006-11-02 05:13 base91-0.6.0/Java/readme.txt
-rw-r--r-- 0/0 112 2006-09-04 03:00 base91-0.6.0/Java/manifest.mf
-rwxr-xr-x 0/0 178 2006-11-02 05:13 base91-0.6.0/Java/build_jar.sh
-rw-r--r-- 0/0 7502 2006-11-02 05:13 base91-0.6.0/cli.c
-rw-r--r-- 0/0 5066 2006-11-02 05:13 base91-0.6.0/base91.c
-rw-r--r-- 0/0 1501 2006-11-02 05:13 base91-0.6.0/LICENSE
-rw-r--r-- 0/0 561 2006-11-02 05:13 base91-0.6.0/base91.h
-rw-r--r-- 0/0 2360 2006-11-02 05:13 base91-0.6.0/README
-rw-r--r-- 0/0 1762 2006-11-02 05:13 base91-0.6.0/base91.1
-rw-r--r-- 0/0 903 2006-09-04 03:00 base91-0.6.0/Makefile
-rw-r--r-- 0/0 2330 2006-11-02 05:13 base91-0.6.0/NEWS
$ tar xf base91-0.6.0.tar.gz
$ cd base91-0.6.0
$ make
$ ./base91 -h
Usage: base91 [OPTION]... [FILE]
basE91 encode or decode FILE, or standard input, to standard output.

-d, --decode decode data
-m SIZE use SIZE bytes of memory for buffers (suffixes b, K, M)
-o, --output=FILE write to FILE instead of standard output
-v, --verbose verbose mode
-w, --wrap=COLS wrap encoded lines after COLS characters (default 76)
--help display this help and exit
--version output version information and exit

With no FILE, or when FILE is -, read standard input.

encodeしてdecodeしてみます．
当たり前ですが，encode, decodeしてもdiffもhashも同じです．

$ ./base91 ./base91 -o ./base91.base91
$ ./base91 -d ./base91.base91 -o ./base91.tmp
$ diff ./base91 ./base91.tmp
$ sha512sum ./base91
bc903be7c5b694841a9d0303351846f80f4798ad8848e8f298cf2c4818c68a2270b065db495b969503b25cdf672632e1cced18094f935fed47b75718c3c3e976 ./base91
$ ./base91 ./base91 | ./base91 -d | sha512sum
bc903be7c5b694841a9d0303351846f80f4798ad8848e8f298cf2c4818c68a2270b065db495b969503b25cdf672632e1cced18094f935fed47b75718c3c3e976 -

basE91, base64, base32, uudecode で変換してみます．
basE91 が一番小さいですね

$ ls --block-size=1 -s ./base91
16384 ./base91
$ ./base91 ./base91 | wc -c
17340
$ base64 ./base91 | wc -c
19615
$ uuencode ./base91 - | wc -c
20024
$ base32 ./base91 | wc -c
23538

圧縮すると大分小さくなります．圧縮のほうがずっと効きますね．

$ xz -c ./base91 | wc -c
4528
$ xz -c ./base91 | ./base91 | wc -c
5640
$ xz -c ./base91 | base64 | wc -c
6120
$ xz -c ./base91 | uuencode - | wc -c
6260
$ xz -c ./base91 | base32 | wc -c
7344

他のファイルも試してみます．/usr/bin 以下のファイルを適当に見繕って比較x5です．

#!/bin/bash

XZ=`mktemp`
for i in `seq 1 5`
do
CMD=`find /usr/bin -type f | shuf -n 1`
echo $CMD
echo -n "raw "
stat -c %s $CMD
xz -9 -c $CMD > $XZ
echo -n "xz "
stat -c %s $XZ
echo -n "xz.uu "
uuencode $XZ - | wc -c
echo -n "xz.base64 "
base64 $XZ | wc -c
echo -n "xz.base91 "
base91 $XZ | wc -c
done
rm $XZ

$ bash ./bin2ascii.bash | column -t
/usr/bin/lxc-snapshot
raw 27632
xz 6756
xz.uu 9328
xz.base64 9127
xz.base91 8418
/usr/bin/dh_installtmpfiles
raw 3263
xz 1540
xz.uu 2144
xz.base64 2084
xz.base91 1917
/usr/bin/pbmtogo
raw 10384
xz 3164
xz.uu 4380
xz.base64 4276
xz.base91 3942
/usr/bin/chartread
raw 3966528
xz 772956
xz.uu 1064980
xz.base64 1044169
xz.base91 963047
/usr/bin/spamassassin
raw 29898
xz 9608
xz.uu 13258
xz.base64 12981
xz.base91 11971

おまけ?armelでstatic linkなバイナリを作ってみました．Sipeed Lichee Nano で動かないかな?と．

$ git diff HEAD~~ Makefile
diff --git a/Makefile b/Makefile
index 246aede..129acff 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
-CFLAGS = -Wall -W -O2
-LDFLAGS = -s
+CFLAGS = -static -Wall -W -O2
+LDFLAGS = -static -s

-CC = gcc
+CC = arm-linux-gnueabi-gcc
INSTALL = install
INSTALL_DATA = $(INSTALL) -m 444
INSTALL_PROGRAM = $(INSTALL) -m 555

qemu-arm-static では動くのを確認したけど実機では未確認です．

$ file ./base91
./base91: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, BuildID[sha1]=88ec4ccbf69c4bb41640b5de63b6b5373b7e5365, for GNU/Linux 3.2.0, stripped
$ ldd ./base91
not a dynamic executable
$ echo 😺 | qemu-arm-static ./base91 | pee cat "qemu-arm-static ./base91 -d"
=~m6xHA
😺

以下にバイナリとuuencodeしたものが置いてあります．

https://github.com/matoken/basE91/releases/tag/0.6.0

https://gist.github.com/matoken/c2bc8dadfdcb00da99fd1b4c2f12ce1c

blog元のmemo

unix:commands:base91 [wiki.matoken.org]

環境

$ dpkg-query -W gcc-10-arm-linux-gnueabi qemu-user-static gcc coreutils sharutils
coreutils 8.32-3
gcc 4:10.1.0-1
gcc-10-arm-linux-gnueabi 10.2.0-3cross2
qemu-user-static 1:5.0-13
sharutils 1:4.15.2-5
$ lsb_release -dr
Description: Debian GNU/Linux bullseye/sid
Release: unstable
$ uname -m
x86_64