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

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

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

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

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

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したものが置いてあります.

blog元のmemo

環境
$ 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

Raspbian jessieでもhiptextを試す

さっき

というのを書きましたが,なんとなくRaspberry Pi B+ の Raspbian jessie lite でも`hiptext`を試してみました.

$ sudo apt install build-essential libpng12-dev libjpeg-dev     libfreetype6-dev libgif-dev ragel libavformat-dev libavcodec-dev     libswscale-dev libgflags-dev libgoogle-glog-dev git
$ git clone https://github.com/jart/hiptext.git
$ cd hiptext
$ time make
   :
real    22m7.443s
user    21m53.190s
sys     0m8.910s

て感じでほぼREADMEのままmake一発でした.違ったのは導入パッケージに`git`が必要だったくらいです.
思いの外makeに時間が掛かりましたがRaspberry Pi B+で試しているので2Bや3Bだともっと速いでしょう.
Balls.pngやObama.jpgはうまく行き,さっきうまく行かなかったTux.pngは同じようにコケたので同じ動きのようです.

恐らく Debian jessie でも同様だと思います.

27293516472 deedbe97a4

画像や動画をtextに変換するhiptextを試す

類似のものでlibcacaとかがありますが,

Turn images into text better than caca/aalib

とのことでもっといいものらしいです.
Texttop[1]で使われているので興味を持ったのですが,そのままではBuild出来なかったのでメモです.

build

READMEではこの辺が必要とのことです.

sudo apt-get install build-essential libpng12-dev libjpeg-dev \
    libfreetype6-dev libgif-dev ragel libavformat-dev libavcodec-dev \
    libswscale-dev libgflags-dev libgoogle-glog-dev

Debian stretch testingではこんな感じ?

$ sudo apt-get install build-essential libpng-dev libjpeg-dev \
      libfreetype6-dev libgif-dev ragel libavformat-dev libavcodec-dev \
      libswscale-dev libgflags-dev libgoogle-glog-dev

後はmakeするだけなんですが,こんなエラーが.新しいlibavに追従できてない感じ?

g++ -g -O3 -std=c++11 -Wall -Wextra -fno-exceptions -fno-rtti -MD  -march=native -c -o movie.o movie.cc
movie.cc: In member function ‘void Movie::PrepareRGB(int, int)’:
movie.cc:56:42: error: ‘PIX_FMT_RGB24’ was not declared in this scope
                       width_, height_, PIX_FMT_RGB24, SWS_FAST_BILINEAR,
                                          ^
In file included from graphic.h:10:0,
                 from movie.h:9,
                 from movie.cc:4:
movie.cc:64:38: error: ‘avcodec_alloc_frame’ was not declared in this scope
 CHECK(frame_ = avcodec_alloc_frame());
                                      ^
movie.cc:66:19: warning: ‘int avpicture_get_size(AVPixelFormat, int, int)’ is deprecated [-Wdeprecated-declarations]
   int rgb_bytes = avpicture_get_size(PIX_FMT_RGB24, width_, height_);
                   ^
In file included from /usr/include/x86_64-linux-gnu/libavformat/avformat.h:318:0,
                 from movie.cc:10:
/usr/include/x86_64-linux-gnu/libavcodec/avcodec.h:4898:5: note: declared here
 int avpicture_get_size(enum AVPixelFormat pix_fmt, int width, int height);
     ^
movie.cc:66:19: warning: ‘int avpicture_get_size(AVPixelFormat, int, int)’ is deprecated [-Wdeprecated-declarations]
   int rgb_bytes = avpicture_get_size(PIX_FMT_RGB24, width_, height_);
                   ^
In file included from /usr/include/x86_64-linux-gnu/libavformat/avformat.h:318:0,
                 from movie.cc:10:
/usr/include/x86_64-linux-gnu/libavcodec/avcodec.h:4898:5: note: declared here
 int avpicture_get_size(enum AVPixelFormat pix_fmt, int width, int height);
     ^
movie.cc:69:14: warning: ‘int avpicture_fill(AVPicture*, const uint8_t*, AVPixelFormat, int, int)’ is deprecated [-Wdeprecated-declarations]
   int prep = avpicture_fill(reinterpret_cast<AVPicture*>(frame_rgb_),
              ^
In file included from /usr/include/x86_64-linux-gnu/libavformat/avformat.h:318:0,
                 from movie.cc:10:
/usr/include/x86_64-linux-gnu/libavcodec/avcodec.h:4883:5: note: declared here
 int avpicture_fill(AVPicture *picture, const uint8_t *ptr,
     ^
movie.cc:69:14: warning: ‘int avpicture_fill(AVPicture*, const uint8_t*, AVPixelFormat, int, int)’ is deprecated [-Wdeprecated-declarations]
   int prep = avpicture_fill(reinterpret_cast<AVPicture*>(frame_rgb_),
              ^
In file included from /usr/include/x86_64-linux-gnu/libavformat/avformat.h:318:0,
                 from movie.cc:10:
/usr/include/x86_64-linux-gnu/libavcodec/avcodec.h:4883:5: note: declared here
 int avpicture_fill(AVPicture *picture, const uint8_t *ptr,
     ^
movie.cc: In member function ‘Graphic Movie::Next()’:
movie.cc:94:5: warning: ‘void av_free_packet(AVPacket*)’ is deprecated [-Wdeprecated-declarations]
     av_free_packet(&packet);
     ^
In file included from /usr/include/x86_64-linux-gnu/libavformat/avformat.h:318:0,
                 from movie.cc:10:
/usr/include/x86_64-linux-gnu/libavcodec/avcodec.h:4040:6: note: declared here
 void av_free_packet(AVPacket *pkt);
      ^
movie.cc:94:5: warning: ‘void av_free_packet(AVPacket*)’ is deprecated [-Wdeprecated-declarations]
     av_free_packet(&packet);
     ^
In file included from /usr/include/x86_64-linux-gnu/libavformat/avformat.h:318:0,
                 from movie.cc:10:
/usr/include/x86_64-linux-gnu/libavcodec/avcodec.h:4040:6: note: declared here
 void av_free_packet(AVPacket *pkt);
      ^
movie.cc:94:27: warning: ‘void av_free_packet(AVPacket*)’ is deprecated [-Wdeprecated-declarations]
     av_free_packet(&packet);
                           ^
In file included from /usr/include/x86_64-linux-gnu/libavformat/avformat.h:318:0,
                 from movie.cc:10:
/usr/include/x86_64-linux-gnu/libavcodec/avcodec.h:4040:6: note: declared here
 void av_free_packet(AVPacket *pkt);
      ^
<ビルトイン>: ターゲット 'movie.o' のレシピで失敗しました
make: *** [movie.o] エラー 1

でも以下のPRを反映したら通りました :)

試してみる

サンプルとして,balls.jpg, obama.jpg があるのでこれを試してみます.
うまく動いていそうです.

47418

でも.pngはうまく動かない?

$ ./hiptext ~/Downloads/Tux.png
F0601 11:01:16.713266 30721 png.cc:35] Check failed: type == PNG_COLOR_TYPE_RGB || type == PNG_COLOR_TYPE_RGBA png bad type: /home/mk/Downloads/Tux.png
*** Check failure stack trace: ***
    @     0x7fa48d3c013d  google::LogMessage::Fail()
    @     0x7fa48d3c1fa3  google::LogMessage::SendToLog()
    @     0x7fa48d3bfccb  google::LogMessage::Flush()
    @     0x7fa48d3c298e  google::LogMessageFatal::~LogMessageFatal()
    @           0x41859f  LoadPNG()
    @           0x403fa8  main
    @     0x7fa489e59610  __libc_start_main
    @           0x40a229  _start
    @              (nil)  (unknown)
中止
$ convert ~/Downloads/Tux.png ./Tux.jpg
$ ./hiptext Tux.jpg

`.jpg`に変換したらいけてる感じですがなんか欠けてます.

26780171563 8d301b27a4

動画再生もいけます.再生のみで一時定位などの操作は出来ない感じ?

今のところlibcacaや動画ではvlcやmplayerなんかのほうがパッケージで導入できるのでお手軽そう.

とりあえず動くようになったので次はTexttopを試してみようと思います.