class BitBufferHelper {

    public static byte[] getBits(byte[] data, int startOffset, int numBits) throws BufferException {

        int startByteOffset = 0;
        int extranumBits = numBits % NetUtils.NUM_BITS_IN_A_BYTE;
        final int extraOffsetBits = startOffset % NetUtils.NUM_BITS_IN_A_BYTE;
        int numBytes = numBits % NetUtils.NUM_BITS_IN_A_BYTE != 0 ? 1 + numBits / NetUtils.NUM_BITS_IN_A_BYTE
                : numBits / NetUtils.NUM_BITS_IN_A_BYTE;
        startByteOffset = startOffset / NetUtils.NUM_BITS_IN_A_BYTE;
        byte[] bytes = new byte[numBytes];
        if (numBits == 0) {
            return bytes;
        }

        checkExceptions(data, startOffset, numBits);

        if (extraOffsetBits == 0) {
            if (extranumBits == 0) {
                System.arraycopy(data, startByteOffset, bytes, 0, numBytes);
                return bytes;
            } else {
                System.arraycopy(data, startByteOffset, bytes, 0, numBytes - 1);
                bytes[numBytes - 1] = (byte) (data[startByteOffset + numBytes - 1] & getMSBMask(extranumBits));
            }
        } else {
            int index;
            int valfromnext;
            for (index = 0; index < numBits / NetUtils.NUM_BITS_IN_A_BYTE; index++) {
                // Reading numBytes starting from offset
                int valfromcurr = data[startByteOffset + index]
                        & getLSBMask(NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits);
                valfromnext = data[startByteOffset + index + 1] & getMSBMask(extraOffsetBits);
                bytes[index] = (byte) (valfromcurr << extraOffsetBits
                        | valfromnext >> NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits);
            }
            // Now adding the rest of the bits if any
            if (extranumBits != 0) {
                if (extranumBits < NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits) {
                    valfromnext = (byte) (data[startByteOffset + index]
                            & getMSBMask(extranumBits) >> extraOffsetBits);
                    bytes[index] = (byte) (valfromnext << extraOffsetBits);
                } else if (extranumBits == NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits) {
                    int valfromcurr = data[startByteOffset + index]
                            & getLSBMask(NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits);
                    bytes[index] = (byte) (valfromcurr << extraOffsetBits);
                } else {
                    int valfromcurr = data[startByteOffset + index]
                            & getLSBMask(NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits);
                    valfromnext = data[startByteOffset + index + 1]
                            & getMSBMask(extranumBits - (NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits));
                    bytes[index] = (byte) (valfromcurr << extraOffsetBits
                            | valfromnext >> NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits);
                }

            }
        }
        // Aligns the bits to LSB
        return shiftBitsToLSB(bytes, numBits);
    }

    private  BitBufferHelper();

}