Java byte arrays and copying ints into them - java

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.

Related

Calculating CRC for int32 data format using the Modbus protocol

I am connecting to a device using the modbus protocol. I need to obtain 3 values from the machine. The first value is of the data format int16 and when I send an example byte array:
static byte[] hz = new byte[] { (byte) 0x01, (byte) 0x03, (byte) 0x00,
(byte) 0x33, (byte) 0x00, (byte) 0x01 };
and use a CRC calculation method I obtained from a previous question I asked on the subject.
// Compute the MODBUS RTU CRC
private static int ModRTU_CRC(byte[] buf, int len)
{
int crc = 0xFFFF;
for (int pos = 0; pos < len; pos++) {
crc ^= (int)buf[pos]; // XOR byte into least sig. byte of crc
for (int i = 8; i != 0; i--) { // Loop over each bit
if ((crc & 0x0001) != 0) { // If the LSB is set
crc >>= 1; // Shift right and XOR 0xA001
crc ^= 0xA001;
}
else // Else LSB is not set
crc >>= 1; // Just shift right
}
}
// Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes)
return crc;
}
I can recieve a response. However, the other two values are of the int32 data format and do not return a reply when I use this method. To help troubleshoot I am using a program called Realterm. to fire off the commands as well. I use it to append a Modbus 16 CRC to the end of the byte stream and send it, this works for all three and returns the desired reply. Is this a case of the data format not working with this specific calculation formula? Whats the difference between CRC16 and Modbus16?
Modbus16 is a CRC16. CRC calculations have several parameters:
the bit width, in this case 16
the polynomial, in this case 0xA001
the initial value,in this case 0xFFFF
the bit order
whether the final CRC is inverted with an XOR.
There are quite a number of CRC16s defined, with different values for these parameters, and this appears to be one of them. See the Wikipedia article on Cyclic Redundancy Checks for more informaton.
class Obliczenia {
short POLYNOM = (short) 0x0A001;
short[] TAB = {2,3,8,0x13,0x88,1,0x90,0,0x3c,2,0};
short crc = (short) 0xffff;
short CRC_LByte,CRC_HByte;
public Obliczenia() {
for (short dana : TAB) {
crc= CRC16( crc, dana);
}
System.out.println("KOD CRC="+Integer.toHexString(crc&0xffff));
CRC_LByte=(short)(crc & 0x00ff);
CRC_HByte=(short)((crc & 0xff00)/256);
System.out.println(" W ramce CRC_LByte="+Integer.toHexString(CRC_LByte)+ " CRC_HByte "+Integer.toHexString(CRC_HByte));
}
short CRC16(short crct, short data) {
crct = (short) (((crct ^ data) | 0xff00) & (crct | 0x00ff));
for (int i = 0; i < 8; i++) {
boolean LSB = ((short) (crct & 1)) == 1;
crct=(short) ((crct >>> 1)&0x7fff);
if (LSB) {
crct = (short) (crct ^ POLYNOM);
}
}
return crct;
}
}

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

Transforming byte values so that their MSB is removed / restored

I have a binary protocol which extracts the MSB of each payload byte into a MSB collection byte (septett) for transmission, and re-injects the MSBs on receiver side. The payload consists of n four byte frames, depending on sender (six in my case).
Those are two example frames, with their septett (last byte), as seen on the wire:
0x2E 0x00 0x5F 0x00 0x04
0x79 0x01 0x38 0x22 0x04
Those are the same frames, client side, with the MSBs re-injected:
0x2E 0x00 0xDF 0x00
0x79 0x01 0xB8 0x22
The C functions that do the transformation are defined on pages 9 and 10 in this document. My version of these, in Java, is below. The problem I have is that none of this works, and I'm confused as to why. I pass my four bytes from the wire, and get same bytes out, untouched. I could use some help figuring out what's wrong here (probably something trivial that I fail to see).
private static byte[] vbusExtractSeptett(byte[] data, int offset, int length) {
byte septett = 0;
for (int i = 0; i < length; i++) {
if ((data[offset + i] & 0x80) != 0) {
data[offset + i] &= 0x7F;
septett |= 1 << i;
}
}
data[offset + length] = septett;
return data;
}
private static byte[] vbusInjectSeptett(final byte[] data, int offset, int length) {
byte septett = data[offset + length];
for (int i = 0; i < length; i++) {
if ((septett & (1 << i)) != 0)
data[offset + i] |= 0x80;
}
return data;
}
In Java, a byte is signed. Without reading through all your code, I bet that is the problem.
The C code in your document uses unsigned char math.
Since Java doesn't have "unsigned", you probably need to do all your math in shorts (or ints) and then convert back to bytes. Be sure to mask off the sign bit, e.g. something like
byte theResult = theIntIDidtheMathOn & 0xFF;
data[index] = theResult;
The other answer is correct, you are not taking into account Java's use of signed bytes.
There are a few possibilities for solving this:
You can do the stuff above with "& 0xFF"
You can just treat everything as ints (as I have done below)
You could use a library to help. Some examples are JOOU (which was started in response to this lack of unsigned types) or netty channelbuffers (NB: although netty is focussed on network IO, like sockets etc., its channel buffer class is great for dealing with bytestreams and signed/unsigned types of different lengths and I have used it quite a lot for transforming binary protocols like the one you're handling.)
I have written a "solution" to your question below.
public class SOAnswer
{
private static void vbusExtractSeptett(int[] data, int offset, int length) {
int septett = 0;
for (int i = 0; i < length; i++) {
if ((data[offset + i] & 0x80) != 0) {
data[offset + i] &= 0x7F;
septett |= 1 << i;
}
}
data[offset + length] = septett;
}
private static void vbusInjectSeptett(final int[] data, int offset, int length) {
int septett = data[offset + length];
for (int i = 0; i < length; i++) {
if ((septett & (1 << i)) != 0)
data[offset + i] |= 0x80;
}
// clear the septett byte
data[offset + length] = 0x00;
}
private static void printIntArrayAsHEX(int[] array)
{
StringBuilder builder = new StringBuilder();
for ( int a : array )
{
String s = Integer.toHexString( a );
if (s.length() == 1)
builder.append( "0" );
builder.append(s + ":");
}
builder.substring( 0, builder.lastIndexOf( ":" ) - 1 );
System.out.println(builder.toString());
}
public static void main( String[] args )
{
// Create an array long enough for the extracting/injecting
int[] arr = new int[]{0x2E, 0x00, 0xDF, 0x00, 0x00};
// see what it looks like
printIntArrayAsHEX(arr);
// perform extraction
vbusExtractSeptett( arr, 0, 4 );
// see what it looks like
printIntArrayAsHEX(arr);
// Perform injection
vbusInjectSeptett( arr, 0, 4 );
// see what it looks like
printIntArrayAsHEX(arr);
}
}
One recommendation I would have for you is to think about if you really want to to re-implement the C code verbatim (especially the very functional programming style of passing an array of primitive types, an offset into the array and the length of the array). Maybe something more like this would be more OO:
private static int[] vbusExtractSeptettJAVASTYLE(int[] data) {
int[] extractedData = Arrays.copyOf( data, data.length +1 );
int septett = 0;
for (int i = 0; i < data.length; i++) {
if ((data[i] & 0x80) != 0) {
extractedData[i] = data[i] &= 0x7F;
septett |= 1 << i;
}
}
extractedData[extractedData.length-1] = septett;
return extractedData;
}

How to get byte[] from float number

How to get byte[] from float number? I need to create message where for data I have four bytes, datum can be unsigned int( it is easy to get byte[] from int), binary and float ( but I don't know how to get four bytes from float ). Any solution ?
You can use Float.floatToRawIntBits(float) but I suspect you don't need byte[] but instead want to be able to write to a stream of bytes. In which case I would use DataOutputStream.writeFloat(float)
If you are using NIO, you can use ByteBuffer.putFloat() An advantage of ByteBuffer is that you can specify a ByteOrder with ByteBuffer.order() so you can handle either Big or Little endian.
Class java.lang.Float has methods floatToIntBits() and floatToRawIntBits() which you can use to get at the bit pattern of a float (as an int). So you could do something like this:
float value = 1.5e-3f;
int bits = Float.floatToIntBits(value);
byte[] bytes = new byte[4];
bytes[0] = (byte)(bits & 0xff);
bytes[1] = (byte)((bits >> 8) & 0xff);
bytes[2] = (byte)((bits >> 16) & 0xff);
bytes[3] = (byte)((bits >> 24) & 0xff);
Note: You'd have to find out for your particular application which of floatToIntBits() or floatToRawIntBits() is appropriate and you'd have to determine in which order you need the bytes (little or big endian).
Without any math involved, you can do that by writing the value via a DataOutputStream and then fetch the resulting output:
ByteArrayOutputStream bos = new ByteArrayOutputStream(4);
DataOutputStream dos = new DataOutputStream(bos);
dos.writeFloat(yourFloat);
byte[] bytes = bos.toByteArray();
// at this point, your bytes will contain the 4-byte representation of the float.
If you think it's easy getting the bytes of an int, Float.floatToIntBits is probably what you want:
float f = ...;
int i = Float.floatToIntBits(f);
byte[] floatBytes = toBytes(i);
public static void main(String[] args)
{
float f = 23f;
byte[] op = new byte[4];
int fi = Float.floatToIntBits(f);
for (int i = 0; i < 4; i++)
{
int offset = (op.length - 1 - i) * 8;
op[i] = (byte) ((fi >>> offset) & 0xff);
}
for(byte b : op)
{
System.out.format("0x%02X ", b);
}
}

byte array to short array and back again in java

I'm having some issues taking audio data stored in a byte array, converting it to a big-endian short array, encoding it, then changing it back into a byte array. Here is what I have. The original audio data is stored in audioBytes2. I am using the same format for decode with a minus on the cos function instead. Unfortunately, changing the byte and short data types is non-negotiable.
short[] audioData = null;
int nlengthInSamples = audioBytes2.length / 2;
audioData = new short[nlengthInSamples];
for (int i = 0; i < nlengthInSamples; i++) {
short MSB = (short) audioBytes2[2*i+1];
short LSB = (short) audioBytes2[2*i];
audioData[i] = (short) (MSB << 8 | (255 & LSB));
}
int i = 0;
while (i < audioData.length) {
audioData[i] = (short)(audioData[i] + (short)5*Math.cos(2*Math.PI*i/(((Number)EncodeBox.getValue()).intValue())));
i++;
}
short x = 0;
i = 0;
while (i < audioData.length) {
x = audioData[i];
audioBytes2[2*i+1] = (byte)(x >>> 0);
audioBytes2[2*i] = (byte)(x >>> 8);
i++;
}
I have done everything that I can think of to make this work, but the closest I've come is getting it to work every other encode/decode and I have no idea why. Thanks for any help.
I also suggest you try ByteBuffer.
byte[] bytes = {};
short[] shorts = new short[bytes.length/2];
// to turn bytes to shorts as either big endian or little endian.
ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts);
// to turn shorts back to bytes.
byte[] bytes2 = new byte[shortsA.length * 2];
ByteBuffer.wrap(bytes2).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(shortsA);
public short bytesToShort(byte[] bytes) {
return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getShort();
}
public byte[] shortToBytes(short value) {
return ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN).putShort(value).array();
}
How about some ByteBuffers?
byte[] payload = new byte[]{0x7F,0x1B,0x10,0x11};
ByteBuffer bb = ByteBuffer.wrap(payload).order(ByteOrder.BIG_ENDIAN);
ShortBuffer sb = bb.asShortBuffer();
while(sb.hasRemaining()){
System.out.println(sb.get());
}
byte[2] bytes;
int r = bytes[1] & 0xFF;
r = (r << 8) | (bytes[0] & 0xFF);
short s = (short)r;
Your code is doing little-endian shorts, not big. You've the indexing for MSB and LSB swapped.
Since you are using big-endian shorts, you could be using a DataInputStream wrapped around a ByteArrayInputStream (and DataOutputStream/ByteArrayOutputStream) on the other end, rather than doing your own decoding.
If you're getting every other decode working, I'd guess you've got an odd number of bytes, or an off-by-one error elsewhere which is causing your mistake to get fixed on every other pass.
Finally, I'd step through the array with i+=2 and use MSB= arr[i] and LSB=arr[i+1] rather than multiplying by 2, but that's just me.
It looks like you are swapping the byte order between reading the bytes in and writing them back out (unsure if this is intentional or not).

Categories

Resources