swapping the order of a multibyte - java

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);
}

Related

Convert a array having bits into one byte

I m trying to convert 8 bits into one byte. The way the bits are represented are by using a byte object that only contains a 1 or a 0.
If i have a 8 length byte array with these bits, how can i convert them into one byte.
public byte bitsToByte(byte[] bits) {
//Something in here. Each byte inside bits is either a 1 or a 0.
}
Can anyone help?
Thanks
public static byte bitsToByte(byte[] bits){
byte b = 0;
for (int i = 0; i < 8; i++)
b |= (bits[i]&1) << i;
return b;
}
//as an added bonus, the reverse.
public static byte[] bitsToByte(byte bits){
byte[] b = new byte[8];
for (int i = 0; i < 8; i++)
b[i] = (byte) ((bits&(1 << i)) >>> i);
return b;
}
left shift by 1 first for each bit in the array and then add the bit to the byte.
The implementation is based on the assumption that first bit in the array is sign bit and following bits are the magnitude in higher to lower positions of the byte.
public byte bitsToByte(byte[] bits) {
byte value = 0;
for (byte b : bits) {
value <<=1;
value += b;
}
return value;
}
Test the method:
public static void main(String[] args) {
BitsToByte bitsToByte = new BitsToByte();
byte bits[] = new byte[]{0,0,1,0,1,1,0,1}; // 1 + 0 + 4 + 8 + 0 + 32 + 0 + 0
byte value = bitsToByte.bitsToByte(bits);
System.out.println(value);
}
output:
45
Covert the byte array into a byte value (in the same order):
public static byte bitsToByte1(byte[] bits){
byte result = 0;
for (byte i = 0; i < bits.length; i++) {
byte tmp = bits[i];
tmp <<= i; // Perform the left shift by "i" times. "i" position of the bit
result |= tmp; // perform the bit-wise OR
}
return result;
}
input: (same array in reverse)
byte bits1[] = new byte[]{1,0,1,1,0,1,0,0};
value = bitsToByte1(bits1);
System.out.println(value);
output:
45

I need to send an 8-byte string to an SNMP agent, but it's receiving the wrong values

I need to send an 8-byte string to an SNMP agent.
My number can be a big integer as a string.
Due to the java limitation of signed bytes, I'm having a problem with some numbers.
For example, if num is "555", the SNMP agent receives the right value.
if num is "666", the SNMP agent receives the wrong value, because, one of the byte in the
array has a -ve value.
I did a bit & with 0xFF, still it doesn't work.
How can I fix this? Thanks for your help!
public static String stringNumToOctetString(String num) {
BigInteger bi = new BigInteger(num);
byte[] b = bi.toByteArray();
int n = 8 - b.length;
byte[] bVal = new byte[8]; //return must be 8 bytes
for(int i=0; i<8; i++) {
bVal[i] = (byte) 0;
}
int k = 0;
for(int j=n; j<8; j++) {
bVal[j] = (byte) (b[k++] & 0xFF);
}
return new String(bVal);
}
Use an array of int to store your octet values, not an array of byte. byte is signed, and has a range of -128 to +127, so it's not going to work here, where you need values to go to 255.
Further Reading
http://www.jguru.com/faq/view.jsp?EID=13647

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!

How to convert java BigDecimal to normal byte array (not 2's complement)

How do I convert from big integer to a byte array which is not in 2's complement format. Bascially I only need to convert positive numbers and do not need the sign bit.
So something like 10 would become a byte 0x0a i.e-> 00001010
[Update]
As per comment I tried this
public void testBinary()
{
BigDecimal test = new BigDecimal(35116031);
BigInteger theInt = test.unscaledValue();
byte[] arr = theInt.toByteArray();
System.out.println(getCounterVal(arr, new BigInteger("256")));
}
public BigInteger getCounterVal(byte[] arr, BigInteger multiplier)
{
BigInteger counter = BigInteger.ZERO;
for(int i = (arr.length - 1); i >=0; i--)
{
int b = arr[i];
//int val = (int) b & 0xFF;
BigInteger augend = BigInteger.valueOf(b);
counter = counter.add(augend.multiply(multiplier.pow(i)));
}
return counter;
}
The out put value I got was -19720446 And with the //int val = (int) b & 0xFF; uncommented and used as augend, I got the value 4292024066
[Update2]
Here is a test I ran which works. Not sure if it is bug free but looks fine.
#Test
public void bigIntegerToArray()
{
BigInteger bigInt = new BigInteger("35116444");
byte[] array = bigInt.toByteArray();
if (array[0] == 0)
{
byte[] tmp = new byte[array.length - 1];
System.arraycopy(array, 1, tmp, 0, tmp.length);
array = tmp;
}
BigInteger derived = BigInteger.ZERO;
BigInteger twofiftysix = new BigInteger("256");
int j = 0;
for (int i = array.length - 1; i >= 0; i--)
{
int val = (int) array[i] & 0xFF;
BigInteger addend = BigInteger.valueOf(val);
BigInteger multiplier = twofiftysix.pow(j);
addend = addend.multiply(multiplier);
derived = derived.add(addend);
j++;
}
Assert.assertEquals(bigInt, derived);
}
The difference is largely conceptual. Unsigned numbers are the same in 2's compliment. 2's compliment just describes how to represent negative numbers which you say you don't have.
i.e. 10 is 00001010 in signed and unsigned representation.
To get the bytes from a BigDecimal or BigInteger you can use the methods it provides.
BigDecimal test = new BigDecimal(35116031);
BigInteger theInt = test.unscaledValue();
byte[] arr = theInt.toByteArray();
System.out.println(Arrays.toString(arr));
BigInteger bi2 = new BigInteger(arr);
BigDecimal bd2 = new BigDecimal(bi2, 0);
System.out.println(bd2);
prints
[2, 23, -45, -1]
35116031
The bytes are correct and reproduce the same value.
There is a bug in the way you rebuild your BigInteger. You assume the byte serialization is little endian when Java typically uses big endian http://en.wikipedia.org/wiki/Endianness
Try to split the number in bytes, by dividing by 256 in each iteration and using the remainder, and place all these bytes into an array.
the sign bit in 2-compliment for positive numbers is 0
so signed or unsigned doesn't make a difference for positive numbers
If the value is less than the size of a long then use longValue, then chop the long into bytes. If the value is bigger than a long then probably you need to use an iterative approach, repeatedly dividing the number by 256, taking the remainder as the next byte, then repeating until you get zero. The bytes would be generated right to left. Signed numbers require thought (to generate 2s-complement results) but aren't much more complicated.

A question in java.lang.Integer internal code

While looking in the code of the method:
Integer.toHexString
I found the following code :
public static String toHexString(int i) {
return toUnsignedString(i, 4);
}
private static String toUnsignedString(int i, int shift) {
char[] buf = new char[32];
int charPos = 32;
int radix = 1 << shift;
int mask = radix - 1;
do {
buf[--charPos] = digits[i & mask];
i >>>= shift;
} while (i != 0);
return new String(buf, charPos, (32 - charPos));
}
The question is, in toUnsignedString, why we create a char arr of 32 chars?
32 characters is how much you need to represent an int in binary (base-2, shift of 1, used by toBinaryString).
It could be sized exactly, but I guess it has never made business sense to attempt that optimisation.
Because that method is also called by toBinaryString(), and an int is up to 32 digits in binary.
Because the max value for an int in Java is : 2^31 - 1

Categories

Resources