バイト配列の一部bit列を数値に変換 : java

bit列で数値を抜き出す必要性

一部のプロトコルではデータがbyteやinteger単位ではなく、bit単位で設定されています。

特定bit列を数値に変換するには先頭bitから最終bitまでシフトしつつ足していくことで取得することができますが、バイト単位にキャストしたほうが早いように思うので試してみます。

バイト単位にキャストしたサンプルコード

public long bitToLong1(final byte[] data, int startBit, int length) {
    int firstByte = startBit / 8;
    int firstByteStartBit = startBit % 8;
    int firstByteLength = length + firstByteStartBit > 8 ? 8 - firstByteStartBit : length;
    int firstByteSpareBit = 8 - (firstByteStartBit + firstByteLength);

    // 1バイトで終了するケース
    if(firstByteLength >= length) return
        (data[firstByte] >>> firstByteSpareBit) & (0xff >>> (8 - firstByteLength));

    // 先頭バイトキャスト
    long sum = data[firstByte] & (0xff >>> (8 - firstByteLength));

    // 2バイト目から8bitすべて含むバイトをキャスト
    int numberOfFullByte = (length - firstByteLength) / 8;
    int count;
    for(count = 1; count <= numberOfFullByte; count++) {
        sum = (sum << 8) + (data[firstByte + count] & 0xff);
    }

    // 最後に8ビットに満たないバイトがない場合
    int lastByteLength = length - (firstByteLength + numberOfFullByte * 8);
    if(lastByteLength == 0) return sum;

    // 最後の8ビット未満のバイトをキャスト
    return (sum << lastByteLength) + ((data[firstByte + count] & 0xff) >>> (8 - lastByteLength));
}

ByteBufferを使用したサンプルコード

public long bitToLong2(byte[] data, int startBit, int length) {
    int firstByte = startBit / 8;
    int firstByteStartBit = startBit % 8;

    // 抽出するビットを含むバイト列をコピー
    byte[] dst = new byte[8];
    System.arraycopy(data, firstByte, dst, 0, (startBit + length + 7) / 8 - firstByte);
    
    //先頭バイトを先頭ビットまで0でマスク
    dst[0] &= 0xff >>> firstByteStartBit;    
    // BytBufferでlongに変換
    long sum = ByteBuffer.wrap(dst).getLong();
    // 後ろの余分なビット分シフト
    sum >>>= 64 - (startBit + length) + firstByte * 8;

    return sum;
}

処理能力

7バイトのバイト配列を等差数列的に総なめする動作を100,000回ループさせた結果は次のとおりでした(単位はmsec)。

1回目 2回目 3回目
bitToLong1 1122 1172 1109
bitToLong2 2269 2251 2253
bit単位のループ 8464 8530 8495

まとめ

ByteBufferのソースコードを確認すると最終的にはキャストしていたので性能としてあまり差はでないはずです。 処理速度を求めない人には可読性を重視したほうがよいかもしれません。