xdelta でバイナリファイルへのパッチをあてる


バイナリ用の差分生成・適用コマンド [xdelta](http://xdelta.org/) を試してみた。
バイナリファイルとしては手元に入っていた TokyoCabinet の hdb ファイルを使った。

## インストール

CentOS 6.5 では `yum` で 1.1.4 が入った。

```shell-session
# yum install -y xdelta
# xdelta -v
xdelta: version 1.1.4
```

## 使ってみる


取り敢えず TokyoCabinet のデータベースで試してみる。
適当なデータベースを作成しデータを入れる。

```shell-session
$ tchmgr create ./example.hdb
$ tchmgr put ./example.hdb foo bar
```

データを入れた hdb ファイルをコピーし、片方にだけ追加でデータを入れる。

```shell-session
$ cp ./example.hdb ./example_old.hdb
$ tchmgr put ./example.hdb baz qux
```

以下のような状態になった。

```shell-session
$ tchmgr list ./example.hdb
foo
baz
$ tchmgr list ./example_old.hdb
foo
```

パッチを作成。

```shell-session
$ xdelta delta example_old.hdb example.hdb hdb.xdelta
```

差分ファイルが作成された。

```shell-session
$ ll
合計 1044
-rw-r--r-- 1 akishin akishin 528768 10月  5 00:20 2015 example.hdb
-rw-r--r-- 1 akishin akishin 528736 10月  5 00:20 2015 example_old.hdb
-rw-r--r-- 1 akishin akishin    266 10月  5 00:21 2015 hdb.xdelta
```

差分ファイルの情報は以下で確認できる。

```shell-session
$ xdelta info hdb.xdelta
xdelta: version 1.1.4 found patch version 1.1 in hdb.xdelta (compressed)
xdelta: output name:   example.hdb
xdelta: output length: 528768
xdelta: output md5:    e8678368af4cf3f4401998c2aafe2bda
xdelta: patch from segments: 2
xdelta: MD5                                     Length  Copies  Used    Seq?    Name
xdelta: 7ffda015e0093a3ec3e33f1752b068d0        43      3       43      yes     (patch data)
xdelta: 09c2b02ab2b4c1c0a5ec2c344a2095e9        528736  14      528725  no      example_old.hdb
```

パッチを適用する。
以下で example_old.hdb にパッチが適用され、example_patched.hdb というファイルが出力される。

```shell-session
$ xdelta patch hdb.xdelta example_old.hdb example_patched.hdb
```

パッチ適用後のファイルサイズがオリジナルと同じになっている。

```shell-session
$ ll
合計 1564
-rw-r--r-- 1 akishin akishin 528768 10月  5 00:20 2015 example.hdb
-rw-r--r-- 1 akishin akishin 528736 10月  5 00:20 2015 example_old.hdb
-rw-r--r-- 1 akishin akishin 528768 10月  5 00:24 2015 example_patched.hdb
-rw-r--r-- 1 akishin akishin    266 10月  5 00:21 2015 hdb.xdelta
```

内容も同じになっている事が確認できる。

```shell-session
$ tchmgr list ./example_patched.hdb
foo
baz
```

使った事無かったけどなかなか便利そう。

## 圧縮したファイルで試してみる

今度は

1. hdb ファイルを gzip 圧縮
2. 1. の hdb ファイルに更に値を追加してから圧縮

とした場合に、gzip ファイルに差分を適用して正常に展開・動作するかを試してみる。

まずは hdb ファイルを gzip 圧縮。

```shell-session
$ gzip -c example.hdb > example_old.hdb.gz
```

hdb ファイルに値を追加。

```shell-session
$ tchmgr put ./example.hdb hoge fuga
$ tchmgr list ./example.hdb
foo
baz
hoge
```

値を追加した hdb ファイルを圧縮。

```shell-session
$ gzip -c example.hdb > example.hdb.gz
```

ファイルサイズは以下のようになった。

```shell-session
$ ll
合計 528
-rw-r--r-- 1 akishin akishin 528800 10月  8 21:55 2015 example.hdb
-rw-r--r-- 1 akishin akishin    695 10月  8 21:56 2015 example.hdb.gz
-rw-r--r-- 1 akishin akishin    693 10月  8 21:55 2015 example_old.hdb.gz
```

差分ファイルを作成。

```shell-session
$ xdelta delta example_old.hdb.gz example.hdb.gz hdb.gz.xdelta
$ ll hdb.gz.xdelta
-rw-r--r-- 1 akishin akishin 271 10月  8 22:02 2015 hdb.gz.xdelta
```

特に問題などは無さそう。

```shell-session
$ xdelta info hdb.gz.xdelta
xdelta: version 1.1.4 found patch version 1.1 in hdb.gz.xdelta (compressed)
xdelta: generated with a gzipped FROM file
xdelta: generated with a gzipped TO file
xdelta: output name:   example.hdb.gz
xdelta: output length: 528800
xdelta: output md5:    bacb0158f881a451804bf9894b08f265
xdelta: patch from segments: 2
xdelta: MD5                                     Length  Copies  Used    Seq?    Name
xdelta: 65f18a14b640468f88080d650e7b3ebb        45      4       45      yes     (patch data)
xdelta: e8678368af4cf3f4401998c2aafe2bda        528768  21      528755  no      example_old.hdb.gz
```

パッチを適用してみる。

```shell-session
$ xdelta patch hdb.gz.xdelta example_old.hdb.gz example_patched.hdb.gz
```

ファイルサイズはこんな感じ。
パッチ適用後のサイズがオリジナルと異なるのが若干気になるが・・・。

```shell-session
$ ll
合計 536
-rw-r--r-- 1 akishin akishin 528800 10月  8 21:55 2015 example.hdb
-rw-r--r-- 1 akishin akishin    695 10月  8 21:56 2015 example.hdb.gz
-rw-r--r-- 1 akishin akishin    693 10月  8 21:55 2015 example_old.hdb.gz
-rw-r--r-- 1 akishin akishin    683 10月  8 22:03 2015 example_patched.hdb.gz
-rw-r--r-- 1 akishin akishin    271 10月  8 22:02 2015 hdb.gz.xdelta
```

展開してみると問題なく展開された。
展開後の hdb ファイルのサイズは同じになっている。

```shell-session
$ gunzip example_patched.hdb.gz
$ ll
合計 1052
-rw-r--r-- 1 akishin akishin 528800 10月  8 21:55 2015 example.hdb
-rw-r--r-- 1 akishin akishin    695 10月  8 21:56 2015 example.hdb.gz
-rw-r--r-- 1 akishin akishin    693 10月  8 21:55 2015 example_old.hdb.gz
-rw-r--r-- 1 akishin akishin 528800 10月  8 22:03 2015 example_patched.hdb
-rw-r--r-- 1 akishin akishin    271 10月  8 22:02 2015 hdb.gz.xdelta
```

中身のデータも問題なく参照できる模様。

```shell-session
$ tchmgr list ./example_patched.hdb
foo
baz
hoge
$ tchmgr get ./example_patched.hdb hoge
fuga
```

## 参考サイト

xdelta - Wikipedia 
https://ja.wikipedia.org/wiki/Xdelta

UNIXの部屋 コマンド検索:xdelta (*BSD/Linux) 
http://x68000.q-e-d.net/~68user/unix/pickup?xdelta