Cではよくバイナリデータのフォーマットでビットフィールドを使いました。身近な例では、TCP/IPのヘッダーなんかで使用されています。
Javaでも、こうしたデータ構造を扱うときにはビットフィールドの処理を記述する必要があります。しかし、Javaはこのビットフィールド処理が少々苦手です。直接ビットフィールドを扱う言語機能がない点と、整数型がすべて符号ありという点の2つが原因です。(といっても言語として駄目ということではなく、単にビットフィールドを扱うのが不得手というだけです。)
ビットフィールドの処理について、書籍・Webともに有益な情報が見つからなかったので、自分でまとめてみました。
http://www.02.246.ne.jp/~torutk/javahow2/integer.html
Apache CommonsのLangパッケージにBitFieldというユーティリティクラスが含まれているのですが、これは32bit幅限定の上、扱うフィールド毎にインスタンスを作る必要があり、あまり便利ではないと思います。
以下にIPv4ヘッダーの1ワード目を作成するコードの例を、Java標準の範囲およびCommonsのBitFieldクラスを使ったものと2つ紹介します。
IPv4ヘッダーの1ワード目(32bit)のデータを作成するコード
標準Javaの範囲で記述
public static final int setIpHeader1( final int aVersion, final int aHeaderLength, final int aServiceType, final int aLength ) { int header = 0; header = (header & ~VERSION_MASK) | (aVersion << VERSION_SHIFT & VERSION_MASK); header = (header & ~HEADER_LENGTH_MASK) | (aHeaderLength << HEADER_LENGTH_SHIFT & HEADER_LENGTH_MASK); header = (header & ~SERVICE_TYPE_MASK) | (aServiceType << SERVICE_TYPE_SHIFT & SERVICE_TYPE_MASK); header = (header & ~LENGTH_MASK) | (aLength << LENGTH_SHIFT & LENGTH_MASK); return header; }
Apache Commons LangのBitFieldを使って記述
public static final int setIpHeader2( final int aVersion, final int aHeaderLength, final int aServiceType, final int aLength ) { int header = 0; BitField version = new BitField(VERSION_MASK); header = version.setValue(header, aVersion); BitField headerLength = new BitField(HEADER_LENGTH_MASK); header = headerLength.setValue(header, aHeaderLength); BitField serviceType = new BitField(SERVICE_TYPE_MASK); header = serviceType.setValue(header, aServiceType); BitField length = new BitField(LENGTH_MASK); header = length.setValue(header, aLength); return header; }
使用している定数の記述
private static final int VERSION_MASK = 0xf0000000; private static final int VERSION_SHIFT = 28; private static final int HEADER_LENGTH_MASK = 0x0f000000; private static final int HEADER_LENGTH_SHIFT = 24; private static final int SERVICE_TYPE_MASK = 0x00ff0000; private static final int SERVICE_TYPE_SHIFT = 16; private static final int LENGTH_MASK = 0x0000ffff; private static final int LENGTH_SHIFT = 0;