Tibo
Tibo

Reputation: 533

Extract bits from byte array

I would like to extract information encoded at a bit level from a byte array. Assuming that my model is something like

Model
Field 1 - 3 bits
Field 2 - 7 bits
Field 3 - 9 bits
Field 4 - 13 bits

And i've got an array containing 4 bytes, how could i match my model with my array ?

I read some posts about bit manipulation but i'm not really sure to have understood everything.

Upvotes: 0

Views: 3540

Answers (1)

Tibo
Tibo

Reputation: 533

I found out BitSet object, which seems to be really useful for what i want to do.

public class BytesDecoder {

    private BitSet bitSetToDecode;

    /**
     * BytesDecoder constructor
     * Init the BitSet used for decoding
     * @param bytes the byte array to decode
     */
    public BytesDecoder(byte[] bytes) {
        Log.i("BytesDecoder", "==== FRAME TO DECODE ====");
        Log.i("BytesDecoder", Arrays.toString(bytes));
        // Translate bytes array as binary
        String frameBinary ="";
        for (byte b : bytes) {
            frameBinary  += toBinary(b,8);
        }
        Log.i("BytesDecoder", frameBinary);

        // Init the BitSet with an array of reversed bytes to handle the little endian representation expected
        bitSetToDecode = BitSet.valueOf(reverse(bytes));
    }

    /**
     * Decode a part of the byte array between the startIndex and the endIndex
     * @param startIndex index of the first bit to include
     * @param endIndex index after the last bit to include
     * @return the int value of the decoded bits
     */
    public int decode(int startIndex, int endIndex) {
        int length = endIndex - startIndex;

        int decodedInt = convert(bitSetToDecode.get(startIndex, endIndex), length);

        Log.i("BytesDecoder","--- Decoded parameter --- ");
        Log.i("BytesDecoder",toBinary(decodedInt, length) + " interpreted as " + decodedInt);

        return decodedInt;
    }

    /**
     * Reverse bit order of each byte of the array
     * @param data the bytes array
     * @return the bytes array with bit order reversed for each byte
     */
    private byte[] reverse(byte [] data) {
        byte [] bytes = data.clone();

        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte) (Integer.reverse(bytes[i]) >>> 24);
        }

        return bytes;
    }

    /**
     * Get the binary form of an int
     * @param num the int number
     * @param length the bit length
     * @return the string value of the binary form of the int
     */
    private String toBinary(int num, int length) {
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < length; i++) {
            sb.append(((num & 1) == 1) ? '1' : '0');
            num >>= 1;
        }

        return sb.reverse().toString();
    }

    /**
     * Convert a BitSet into an int
     * @param bits the BitSet
     * @param length the BitSet theorical lenght
     * @return the int value corresponding
     */
    private int convert(BitSet bits, int length) {
        int value = 0;
        // Set the increment to the difference between the therocial length and the effective lenght of the bitset
        // to take into account the fact that the BitSet just represent significative bits
        // (i.e instead of 110, the bitset while contains 11 since the 0 is irrelevant in his representation)
        int increment = length - bits.length();

        // Browse the BitSet from the end to the begining to handle the little endian representation
        for (int i = bits.length() - 1; i >= 0; --i) {
            value += bits.get(i) ? (1L << increment) : 0L;
            increment++;
        }

        return value;
    }
}

With this class, I can decode a byte array bit by bit.

I don't know if it's the best solution, but this do what i wanted.

Upvotes: 2

Related Questions