torutkのブログ

ソフトウェア・エンジニアのブログ

Javaでビットフィールド

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;