Efficient way to manipulate bits in byte array representing a bitfield - java

So, I have a byte array that represents a bitfield. A bit 1 at any index of a byte array implies that I have the corresponding piece and vice versa for a 0. Now, I need to change the bit value of 0 to 1, whenever I have the corresponding piece.
My question is, is it better if I convert the byte array to an int array and then change the corresponding value of the array index or is it simpler to do it in a byte array?
If it's the former, how can I convert the byte array to an integer array? If it's the latter, how do I change the value of the corresponding byte array?

To check if bit n is true
boolean get(int n, byte[] bitField)
{
return (bitField[n >> 3] & 1 << (n & 0x7)) != 0; //or use n / 8 and n % 8
}
To set bit n
void set(int n, byte[] bitField, boolean value)
{
if(value)
bitField[n >> 3] |= 1 << (n & 0x7);
else
bitField[n >> 3] &= ~(1 << (n & 0x7));
}
If you use a BitSet, it's a bit simpler
To instantiate
BitSet bitField = new BitSet(); //can specify size
To check if bit n is true
bitField.get(n);
To set bit n
bitField.set(n, value); //can also use set(int) and clear(int) instead

Related

Java: manipulate bits in a number

I was trying to implement a bit manipulation tool in Java.
I do not want to work with the Bitsetclass.
I am working with 64-bit Long values and implemented the following methods:
static final long toggle_bit(long number, int index) {
return (number ^= (1 << index));
}
static final long set_bit(long number, int index) {
return (number |= (1 << index));
}
static final long unset_bit(long number, int index) {
return (number &= ~(1 << index));
}
static final void print_bits(long number) {
String s = Long.toBinaryString(number);
String zeros = "0000000000000000000000000000000000000000000000000000000000000000"; //String of 64 zeros
s = zeros.substring(s.length()) + s;
System.out.println(s);
}
I think the best way of showing my problem is the result of this code:
>>> print_bits(set_bit(0L, 30));
0000000000000000000000000000000001000000000000000000000000000000
>>> print_bits(set_bit(0L, 31));
1111111111111111111111111111111110000000000000000000000000000000
I assume I am reaching negative values or something like that. I would
be very happy if someone could show me a very efficient way of
manipulating all 64 bits in a long-number.
Greetings,
Finn
Make index and the1 a long (declare as 1L) so that JVM doesn’t treat it as an integer.
I also have to note that in set_bits, |= (or equals) doesn’t account for when the bit is 1 but the desired value at the bit is 0
You are shifting an int value of 1. Try shifting a long value of 1, i.e. 1L << index

swapping the order of a multibyte

I have the following multi-byte represented in hex format:
0xdc, 0xd3
I can process the bytes in little endian () format to get the decimal value 54236:
List<Integer> packet = new LinkedList<Integer>(Arrays.asList(0xdc, 0xd3 ));
int idx=0;
int rpm = (int)readBytes(packet, idx, 2);
private long readBytes(List<Integer> packet, int idx, int size){
long val=0;
int element;
for(int i=0;i<size;i++, idx++){
element = packet.get(idx);
val |= element << (8 * i);
}
return val;
}
The above method produces the value as expected. However, now I want to get the value in the reverse order (big endian format), but it gives me some crazy value of 14471936:
private long bigEndianReadBytes(List<Integer> packet, int idx, int size){
long val=0;
int element;
for(int i=size;i>0;i--, idx++){
element = packet.get(idx);
val |= element << (8 * i);
}
return val;
}
What might be wrong with this method?
With #TimBiegeleisen answer (+1), you have good "manual" code to for converting bytes to values.
Have you considered using a ByteBuffer? It does all this for you and works with both Big Endian and Little Endian.
public static void main(String[] args) throws Exception {
ByteBuffer bb = ByteBuffer.allocate(2);
bb.put(new byte[] {(byte)0xdc, (byte)0xd3});
System.out.println(bb.order(ByteOrder.BIG_ENDIAN).getShort(0) & 0xFFFF);
System.out.println(bb.order(ByteOrder.LITTLE_ENDIAN).getShort(0) & 0xFFFF);
}
ANDing the result against 0xFFFF so the short result is promoted to an int, otherwise the result is a negative number because java works with signed data types.
Results:
56531
54236
The for loop in your bigEndianReadBytes() method should start by shifting size - 1 bytes, and end by shifting zero bytes (in the last iteration). Currently, you are shifting size bytes in the first iteration, and one byte in the last iteration. Try this code instead:
for (int i=size-1; i >= 0; i--, idx++) {
element = packet.get(idx);
val |= element << (8 * i);
}

Java 16bit CRC packet validation

I have an Avl Packet that I'm recieving through GPRS from a Device.
The protocol manual says the packet has a 16bit CRC on the last 4 bytes and a source code for CRC calculation is given:
public static int getCrc16(Byte[] buffer) {
return getCrc16(buffer, 0, buffer.length, 0xA001, 0);
}
public synchronized static int getCrc16(Byte[] buffer, int offset, int bufLen, int polynom, int preset) {
preset &= 0xFFFF;
polynom &= 0xFFFF;
int crc = preset;
for (int i = 0; i < bufLen; i++) {
int data = buffer[i + offset] & 0xFF;
crc ^= data;
for (int j = 0; j < 8; j++) {
if ((crc & 0x0001) != 0) {
crc = (crc >> 1) ^ polynom;
} else {
crc = crc >> 1;
}
}
}
return crc & 0xFFFF;
}
So I get the CRC number the packet sends me,then I call getCrc16 for the Byte array in which I have stored the packet and then compare the two numbers right??
Here is the code I use inside my program:
public static String toBinaryString(byte n) {
StringBuilder sb = new StringBuilder("00000000");
for (int bit = 0; bit < 8; bit++) {
if (((n >> bit) & 1) > 0) {
sb.setCharAt(7 - bit, '1');
}
}
return sb.toString();
}
int CalculatedCRC = getCrc16(AvlPacket);
System.out.println("Calculated CRC= " + CalculatedCRC);
int index = (AvlPacket.length)-4;
String BinaryRecievedCRC = "";
for (int j = 0; j < 4; j++) {
BinaryRecievedCRC+= toBinaryString(AvlPacket[index]);
index+=1;
}
int RecievedCRC = Integer.parseInt(BinaryRecievedCRC, 2);
System.out.println("Recieved CRC= " + RecievedCRC);
toBinaryString() converts a byte to it's binary from and puts it into a string!
So I calculate the CRC through getCrc16() given to me from the manual.Then take an index 4 bytes before the end of the packet so I can read the last 4 bytes and get the CRC sent with the packet!
The for loop takes each of the last bytes and with toBinaryString() combines all them in binary form and into a String!So I got something like 0000000000000000101011011101001 (The manual states that first two bytes are always zeroes because its a 16bit CRC)
So I just parse the Binary String into a signed int and Compare the two CRCs...!
Yet I get Results like :
Calculated CRC= 21395
-----Recieved CRC= 30416
or
Calculated CRC= 56084
-----Recieved CRC= 10504
I've tested with many packets and they can't all have loss of data..And I'm parsing the data too so I know that the data I get is correct!
What am I missing in all this??
There is probably something wrong with the documentation wording (or your understanding of it). If there is a 16-Bit CRC in a packet, its most likely occupying two bytes, not four (in binary form). If it were a decimal, even four bytes wouldn't suffice (you would need 5 didgts to store it as unsigned decimal string).
Your code shows you do conversions (but I can't see what kind of conversion its supposed to do):
BinaryRecievedCRC+= toBinaryString(AvlPacket[index]);
I would expect the CRC to be store in the data in binary form, so I assume the only thing you need to figure out are which endianess is used and where the CRC is stored in the data.
Edit: Judging from your comment you would need to extract the CRC like this:
public int getCRC(byte[] data, int index) {
return ((data[index] & 0xFF) << 8)) | (data[index + 1] & 0xFF);
}
So I get the CRC number the packet sends me,then I call getCrc16 for
the Byte array in which I have stored the packet and then compare the
two numbers right??
Wrong. You calculate the CRC over the entire message, including the CRC bytes, and the result should be zero.
Problem Solved!
The problem was that the Packet had 8 other bytes before getting into the Data part!
So I had to exclude those first 8 bytes along with the last 4 bytes of the sent CRC before calculating the CRC!
Now the numbers agree and the above code is correct with the exception that the for loop in the getCrc16 function starts from i=8 (so as to skip the first 8 bytes of the packet which do no belong to the Data part!)
Thank you all for your time!

Split Array Cells in Java

outp is of unsigned short* type. Some manipulations need to be done depending on the mode where mode is a global variable. In the if-block val is assigned to outp at the calculated index. In if-else block first the address pointed by outc is calculated and then the value is assigned at that location which in turn leads to change in a paricular cell of outp.
void lds_ld(int val, int x, int y, int value, unsigned short* outp)
{
unsigned char *outc;
if(mode==1)
{
outp[y*width +x] = val;
}
else if (mode==2)
{
outc = (unsigned char *)outp +y*width + x;
*outc= value;
}
else
{
printf("Wrong mode");
}
}
I need to write a java code that performs the same function. So i have written:
void lds_ld(int val, int x, int y, int value, int outp[])
{
short outc[];
if(mode==1)
{
outp[y*width +x] = val;
}
else if (mode==2)
{
//what to write here
}
else
{
System.out.printf("Wrong mode");
}
}
To write the if-else block i some how need to first split each array cell into two and then calculate the index and assign value at that index and then convert this array back to int[] type. whole How can i do this?
Mode 2 seems to allow you to only edit the first byte of the short, so you might try this:
int arrayValue = outp[y * width + x];
int firstByte = (arrayValue >> 8) & 0xFF;
int secondByte = arrayValue & 0xFF;
firstByte = value;
//if you want to edit the second byte:
//secondByte = value;
outp[y * width + x] = (int)(firstByte << 8) + secondByte;
There are no pointers in java, so you can either work on the same array and remember index to start from or create new array and copy part of source array to the target:
// check array size calculations: I am not sure I completely 
// your logic but I am sure you do.
int outc = new int[outp - (y*width + x)]; understand
System.arraycopy(outp, y*width + x, outc, 0, outc.length);
Depending on how width is set, you are
1) either setting two shorts from an int
2) or you are setting one half of a short, another short, and another half of a short from the int. Which halves of the shorts you are changing (most or least significant) will depend on your CPU architecture (and maybe your compiler)
Since (2) is quite painful, let's assume (1), i.e. width % 2 == 0. In Java, you have to split the int for yourself instead of assuming casting magic:
// determine the least significant 16 bits of your int
short lower = (short)(value & 0xffff);
// determine the most significant 16 bits of your int
short upper = (short)(value >>> 16);
// same calculation as in C-code, but for a short-array instead of for char-array
int pos = (y * width + x) / 2;
// assuming little endian order in your array (similar to running the C-code on x86
outp[pos] = lower;
outp[pos + 1] = upper;
For option (2) you need to split your int into three values:
byte left = (byte)(value & 0xff);
short middle = (short)(value >>> 8);
byte right = (byte)(value >> 24);
After doing so, you can assign middle directly to outp[pos], but you must combine the left and right bytes with the existing values in the cell using bit-manipulation-operations:
if ((y * width + x) % 2) != 0) {
// still assuming little endianness
outp[pos - 1] = (short)(outp[pos - 1] & 0xff00 | left);
outp[pos + 1] = (short)(((right << 8) & 0xff00) | (outp[pos + 1] & 0xff));
}
Another way of doing the given task would be using java.nio.ByteBuffer classes:
import java.nio.*;
// Wrap outp into a ByteBuffer
ByteBuffer outpBuf = ByteBuffer.wrap(outp);
outpBuf.putInt(y * width + x, value);
Please note that ByteBuffer is initially using Big Endian ordering, so you need to use outpBuf.order(ByteOrder.LITTLE_ENDIAN) to get results equivalent to the direct bit-operations suggested by the other posters.

Java byte arrays and copying ints into them

What's the best way to put an int at a certain point in a byte[] array?
Say you have a byte array:
byte[] bytes = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
int someInt = 12355; //0x43, 0x30
How can I do like bytes[4] = someInt; so that now bytes[4] will equal 0x43 and bytes[5] will be equal to 0x30?
I'm used to just using memcpy with C++ and don't know the alternatives in Java.
Thanks
If you want also high 0-bytes of the int put into the byte[]:
void place(int num, byte[] store, int where){
for(int i = 0; i < 4; ++i){
store[where+i] = (byte)(num & 0xFF);
num >>= 8;
}
}
If you only want the bytes to the highest nonzero byte:
void place(int num, byte[] store, int where){
while(num != 0){
store[where++] = (byte)(num & 0xFF);
num >>>= 8;
}
}
If you want the bytes big-endian (highest byte at lowest index), the version storing all four bytes is very easy, the other one slightly more difficult:
void placeBigEndian(int num , byte[] store, int where){
for(int i = 3; i >= 0; --i){
store[where+i] = (byte)(num & 0xFF);
num >>= 8;
}
}
void placeBigEndian(int num, byte[] store, int where){
in mask = 0xFF000000, shift = 24;
while((mask & num) == 0){
mask >>>= 8;
shift -= 8;
}
while(shift > 0){
store[where++] = (byte)((num & mask) >>> shift);
mask >>>= 8;
shift -= 8;
}
}
Note, you assume a big endian ordering! x86 is little endian... What's more, your int is 32bits long, hence 0x00004330 in big endian.
If this is what you want, use a ByteBuffer (which uses big endian ordering by default):
ByteBuffer buf = ByteBuffer.allocate(8);
// then use buf.putInt(yourint, index)
// buf.get(index) will read byte at index index, starting from 0
I don't see the problem, it looks like you solved it your own way:
public static void putShort(bytes[] array, int position, short value)
{
byte leftByte = (byte) (value >>> 8);
byte rightByte = (byte) (value & 0xFF);
array[position] = leftByte;
array[position + 1] = rightByte;
}
Note that an int is 4 bytes and a short is 2 bytes.
First of all, in Java you don't need to initialize byte arrays to zeroes. All arrays are initialized on construction time to 0/false/null.
Second, ints are signed 32-bit big-endian integers, so 12355 is actually 0x00003043. If you want to use 16-bit integers, use the short type.
Then, to get the individual bytes in your integer, you could do:
bytes[ i ] = (byte) (someInt >> 24);
bytes[ i+1 ] = (byte) (someInt >> 16);
bytes[ i+2 ] = (byte) (someInt >> 8);
bytes[ i+3 ] = (byte) (someInt);
The conversion to byte truncates the remaining bits, so no & 0xFF mask is needed. I'm assuming i is the index of the array. To change the endianness, swap the offsets at the indices.
One approach would be to use a DataOutputStream and it's writeInt() method, wrapped around a ByteArrayOutputStream. e.g. (with no error-handling)
public byte[] writeIntAtPositionX(int position, int iVal) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
// now, advancing to a specific spot is awkward.
// presumably you are actually writing other stuff out before the integer
// but if you really want to advance to a specific position
for (int i = 0; i < position; i++)
dos.writeByte(0);
dos.writeInt(iVal);
dos.flush();
dos.close();
return baos.toByteArray();
}
The big advantage of this method is that the guys who wrote Java figured out the byte ordering and the masking with 0xFF and all that stuff. Plus, if you ever envision writing doubles, shorts, longs or Strings etc. to your buffer you won't need to add all those methods, the work is already done.

Categories

Resources