ネットワークで取り扱うデータ形式には、バイナリ形式のものが多く、さらにビット単位に割り付けられているものがあります。
例えば、時刻同期でおなじみのNTPでは、RFC2030で規定されているフォーマットがあります。
先頭部分を抜粋したのが次です。
1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |LI | VN |Mode | Stratum | Poll | Precision | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Root Delay | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
例えば先頭のLIは2ビットで次の意味になります。
ビット値 | 数値 | 内容 |
---|---|---|
00 | 0 | 警告なし |
01 | 1 | 最後の1分が61秒 |
10 | 2 | 最後の1分が59秒 |
11 | 3 | 警告状態(時計が同期していない) |
もし、Javaでbyte[]からこのLI値を取り出すとするなら、次のようなコードになるでしょう。
byte[] message = ... int leapIndicator = (message[0] & 0xc0) >> 6; int versionNumber = (message[0] & 0x38) >> 3; int stratum = message[1] & 0xff;
こんなのがたくさん書かないとしたら、どこが間違っているか分かりにくいしテストも大変だし、かなり苦痛なプログラミングが待っています。
そこで、バイト列であるデータの任意の場所からデータを切り出すライブラリが欲しくなります。
java.nio.ByteBufferがこの目的に比較的近いのですが、任意の場所(バイト境界ではなく任意のビット位置)から、任意のビット幅を切り出すことができません。結局バイトを取り出してビット操作をする必要があります。
ByteBufferをラップしてbit切り出しできるように使用かと考え、BitBufferを作ろうとしましたが、世の中には既に同じことを解決しているかもと探してみたら、Preonなるライブラリが見つかりました。
Preonは、bit表現のエンコード/デコードを行うオープンソースのライブラリです。
Preon
sourceforgeやcodehausなどを転々として現在はgithubでソースコードを管理しています。
https://github.com/preon/preon
Preonのプレゼンテーション資料(OOPSLA 2009で作者のSpringer氏が発表)
http://www.slideshare.net/springerw/oopsla-talk-on-preon
この資料によると、アノテーションを用いてバイナリデータの構造を表現するクラスを作成します。
class Color { @Bound int red; @Bound int green; @Bound int red; }
これは、32bit符号付き整数が3つ並んだバイナリデータを表現します。
1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | red | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | green | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | red | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
ファイルから読み込みデコードする例が資料にあります。(すこし修正しました。)
File file = new File... Codec<Color> codec = Codecs.create(Color.class); Color color = Codecs.decode(codec, file); :
ビット幅の定義もできるので、最初にあげたNTPの例をアノテーションで定義すると
class NtpMessage { @BoundNumber(size = "2") int leapIndicator; @BoundNumber(size = "3") int versionNumber; @BoundNumber(size = "3") int mode; @BoundNumber(size = "8") int stratum; @Bound byte poll; @Bound byte precision; @Bound int rootDelay; : }
となるかと思います。
可変長リストの定義や、値によってフィールドの有無が決まるなどのよくあるフォーマットにも対応しています。入れ子構造もできるので、かなり広範囲なフォーマットに対応できそうです。
アノテーションをうまく活用したAPIの設計例としても興味深いものがあります。
ライセンスは、GPLv2 with Classpath Exception なので、Preonを利用するアプリケーションはGPLにする必要はなくてすみます。