goalngのarch/zipをカスタマイズした

golang には標準でライブラリが沢山入っており、例えば zip圧縮/解凍 を行う arch/zip なんてものもある。
もちろん、ソースコードは全て公開されている。 (github)

しかし、このzipライブラリ、使ってく上で2点問題があった。

  • zipファイルの編集に向いてない
  • Data Descriptor を書き込まない形式に対応していない

この問題を解決するために、はるか昔に arch/zip を fork した物がこちら。
だけど、もろもろ互換性に問題があってサポートしづらかったので、1からforkしなおした。

github.com

以下、余談

zipファイルの形式

詳細は こちら を参照で。

単純に書くと、次の形式になる。

+-----------------+
| file header     |
+-----------------+
| file data       |
+-----------------+
| data descriptor | *option
+-----------------+
  ...
+-----------------+
| dir header      |
+-----------------+
  ...
+-----------------+
| zip end block   |
+-----------------+

最初に各ファイルに関するブロック (file headerdata descriptor) が繰り返され、その次に dir header がファイルの数だけ繰り返される。
dir header と書いてあるけど、内容はほぼ file header と同じである。ディレクトリの情報ブロックでは無いのは注意するところ。
そして、最後にzipファイルそのものの情報を格納する zip end block が存在する。

なお、これら各ブロックは、名前やコメント、または特別なフィールド (extra) を含んでいるため、全て不定長である。

なんで Data Descriptor が標準なのか

zipは誤解満載で大別すると、以下の2つの形式に分けられる。

  • 1パスで読み込めるzipファイル形式
  • 1パスで書き込めるzipファイル形式

file header には通常 file data のデータサイズが格納されている。
そのため、zipファイルを1パスで読み込むことができる。

しかし、ファイルを作成する場合、ここで問題が発生する。
それは「file header を書き込む時点で、file data のサイズ = データの圧縮サイズが決定していないといけない」という問題である。
別に言えば、1パスでzipファイルは書き込めないという問題にもなる。

そのため、1パスでzipファイルを書き込めるように「file header に書き込めない情報は、file dataの後ろに置こう」としたファイル形式として、data descriptor が採用できる。

data descriptor を採用すると、1パスでzipファイルを作成できる (golang だと io.Writer で十分になる) 。
これは、ネット経由のzip転送など、ランダムアクセスが困難な出力形式に対応できる大きな利点となる。

そのため、golang だと data descriptor がある形式が標準になっている・・・・・と妄想してる。*1

結局、どっちがいいのか

だいたいのライブラリは data descriptor があっても正常に解析できるから、data descriptor がある形式でいいと思う。
どうしても古いライブラリじゃないと駄目、とか、data descriptorさえ要らないほどファイルサイズを減らしたい、とかの場合のみ、data descriptor が無い形式を採用すればいい。

*1:MacOSだと data descriptor 形式が標準だという話もあるかも