Java bitwise right shift of 255 into byte value - java

I have a short counter which increments and is used as an identifier. I need to send the identifier as binary data on a network socket.
So if I have a short value I can do this to 'pack' in two bytes:
short id = 1;
byte val = (byte)(id & 0xFF);
byte val2 = (byte)((id >>> 8) & 0xFF);
System.out.printf("id=%d, val = id & 0xFF = %d, val2 = (id >>> 8) & 0xFF = %d\n",
id, val, val2);
Now when id = 1 this works fine.
But when id = 255 I get:
short id = 255;
byte val = (byte)(id & 0xFF); //val = -1
byte val2 = (byte)((id >>> 8) & 0xFF); //val2 = 0
Now I know that 255 in binary is 11111111 and this means -1 for a java byte value. Am I correct in thinking that how java interprets it is immaterial and if I send as byte binary 11111111 then at the other end it can interpret this however it wants?

Yes, your thinkings are right.
Just for your information: there is a very nice class that does this for you. It is called DataInputStream and DataOutputStream.
Socket socket = ...;
DataInputStream dis = new DataInputStream(socket.getInputStream());
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
/* Write a short */
short out = 255;
dos.writeShort(out);
/* Read a short */
short in = dis.readShort();

In Java, all data types are interpreted as signed. That is, any number with most significant bit equal to 1 is a negative number. When you send the number over the network it doesn't matter how it's interpreted by Java, as you still send the same bits. So yes, your code will work as intended.

Related

Java Byte Shift

I have a byte array that should be treated as 2 bytes of unsigned data.
So code below represents 257. I am trying to store it as short, but I am getting an error: The left-hand side of an assignment must be a variable. Is there any clever way to do this? Please help.
byte[] unsigned2ByteNum = new byte[2];
unsigned2ByteNum[0] = 1;
unsigned2ByteNum[1] = 1;
short port = 0;
(byte)(port >> 8) = portNum[0];
(byte)(port) = portNum[1];
You got this backwards. You can't assign a value to an expression, you can only assign a value to a variable.
short port = (short) ((portNum[0] << 8) | (0xff & portNum[1]));
This is assuming portNum[0] is the high byte.
EDIT : corrected the conversion to treat a negative low byte as unsigned (so that, for example, {1,-1} will be converted to 511 instead of -1).

Bit operations converting to an integer

I have some binary operations that are not working like I expect.
I have byte array with the first 2 bytes having these values : 0x5, and 0xE0.
I want to combine them into an integer value that should be 0x5E0.
I tried doing :
int val = (b[i]) << 8 | b[i+1];
but the value is coming out 0xFFFFFFEE0 and the first byte 0x5 is getting lost
I thought this would be easy? What am I doing wrong?
Try: int val = ((b[i] & 0xff) << 8) | (b[i + 1] & 0xff). Bytes are (unfortunately) signed in Java, so if the high bit is set, it gets sign-extended when converted to an integer.
The problem is that byte data type is signed. Therefore, b[i+1] gets sign-extended before performing the operation, becoming 0xFFFFFFE0. When it gets OR-ed with 0x0500 from b[i]<<8, the 0x0500 gets lost.
You can fix this by AND-ing with 0xFF before performing the operation:
public static int toInt16(byte high, byte low) {
int res = (high << 8);
res |= (low & 0xFF);
return res & 0xFFFF;
}
Demo.

Convert long to "unsigned, 4 byte integer in network byte order" with ByteBuffer

The protocol I'm using requires sending back the current position in a file as a "unsigned, 4 byte integer in network byte order". There are several questions on this, but they are assuming I'm using Integers, not Longs
I am attempting to port this to NIO's ByteBuffer so it can be sent in the socket channel:
long bytesTransfered = ... some number of bytes transfered...
//TODO: What does this actually do?
outBuffer[0] = (byte) ((bytesTransfered >> 24) & 0xff);
outBuffer[1] = (byte) ((bytesTransfered >> 16) & 0xff);
outBuffer[2] = (byte) ((bytesTransfered >> 8) & 0xff);
//TODO: Why does netbeans say this does nothing?
outBuffer[3] = (byte) ((bytesTransfered >> 0) & 0xff);
Are their any methods in ByteBuffer that accomplish this? Hopefully in a more obvious, self-descriptive way then the bit-shifting magic above?
Whether signed or unsigned, the bits are the same.
If you cast a long to an int, the JVM discards the high-order bits. The issue comes when promoting an int to a long: Java will sign-extend the value, filling in the high-order bits of the long with the most-significant bit of the int.
To resolve this problem, simply apply a mask to the long. The following should make this clear:
long value = Integer.MAX_VALUE + 1234L;
System.out.println("original value = " + value);
int iValue = (int)value;
System.out.println("value as int = " + iValue);
byte[] array = new byte[4];
ByteBuffer buf = ByteBuffer.wrap(array);
buf.putInt(0, iValue);
int iRetrieved = buf.getInt(0);
System.out.println("int from buf = " + iRetrieved);
long retrieved = iRetrieved;
System.out.println("converted to long = " + retrieved);
retrieved = retrieved & 0xFFFFFFFFL;
System.out.println("high bytes masked = " + retrieved);
However, be aware that you still have only 32 bits. If the filesizes is greater than 4Gb you won't be able to fit it into 4 bytes (and if you have to worry about files > 2G, then you should worry about files > 4G).
That's exactly what ByteBuffer.putInt() is for. You say you're using long but you also only want to write four bytes, so you'll have to cast your long to int. Or else use putLong() and get 8 bytes.

Convert char[] to byte[] without losing 'bits'

I'm developing an Android 2.3.3 application with Java.
This app is an iOS code with unsigned data types port to Java.
On iOS it works with UInt16 and UInt8. In one case instead using byte[] I'm using char[].
But know I have to send that char[] as a byte[] using a DatagramPacket.
If one element of char[] is 128, how can I do to insert into byte[] and the receiver gets 128. I don't know what happens if I do this:
char c = (char)128;
byte b = (byte)c;
Which will be b value?
128 = 10000000. b = -1 or b = 127?
How can I convert char[] to byte[] without losing any bits?
In Java char is an unsigned 16-bit quantity. So you can directly convert your uint16 to char without doing anything else.
For unsigned 8-bit quantity you have 2 options:
Use byte. It also holds 8 bits. You don't lose any bits just because it is signed. However, if you do arithmetic with it you need to remember that Java will scale byte up automatically to an int and sign-extend it. To prevent this just always mask it like this:
byte b;
int foo = 5 * (b & 0xFF);
Use char. It is unsigned and can hold 16 bits so the 8 bits will fit in there quite nicely. To put a byte into a char just do this:
byte b;
char c = (char)(b & 0xFF); // Mask off the low-order 8 bits
To put a char into a byte just do:
char c;
byte b = (byte)c; // Truncates to 8 bits
Be aware that byte in Java is signed, so that whenever you do arithmetic with it you need to mask the low-order 8 bits only (to prevent sign-extension). Like this:
byte b;
int foo = (b & 0xFF);
You can do all the normal bitwise operations you want with a byte without having to mask:
byte b;
if (b & 0x80) ... // Test a flag
b |= 0x40; // Set a flag
b ^= 0x20; // Flip a flag from 0 to 1 or 1 to 0
b ^= ~0x10; // Clear a flag
byte x = b << 3; // Shift left 3 bits and assign
byte x = b >>> 4; // Shift right 4 bits and assign (WITHOUT sign extension)
I think you need to rethink your approach so you don't end up needing to convert char[] to byte[].
If your data really is characters, then you want to look at various serialization techniques, such as using new String(char[]) to create a string and then using getBytes(Charset) to get the bytes as encoded by a given Charset (because, of course, the same characters result in different bytes when encoded in ASCII or UTF-8 or UTF-16, etc.).
But from your question, it sounds like you're not really using characters, you're just using char as a 16-bit type. If so, doing the conversion isn't difficult, something along these lines:
byte[] toBytes(char[] chars) {
byte[] bytes = new byte[chars.length * 2];
int ci, bi;
char ch;
bi = 0;
for (ci = 0; ci < chars.length; ++ci) {
ch = chars[ci];
bytes[bi++] = (byte)((ch & 0xFF00) >> 8);
bytes[bi++] = (byte)(ch & 0x00FF);
}
return bytes;
}
Reverse the masks if you want the result to be small-endian instead.
But again, I would look at your overall approach and try to avoid this.

Read structured data from binary file -?

I know the file structure, suppose this structure is this:
[3-bytes long int],[1-byte long unsigned integer],[4-bytes long unsigned integer]
So the file contains chains of such records.
What is the most elegent way to parse such a file in Java?
Supposedly, we can define a byte[] array of overall length and read it with InputStream, but how then convert its subelements into correct integer values?
First thing, byte value in java is signed, we need unsigned value in our case.
Next thing, are there useful methods that allow to convert a sub-array of bytes, say, bytes from 1-st to 4-th into a correct integer value?
I know for sure, there are functions pack & unpack in Perl, that allow you to represent a string of bytes as an expression, let's say "VV" means 2 unsigned long int values. You define such a string and provide it as an argument to a pack or unpack functions, along with the bytes to be packed/unpacked. Are there such things in Java / Apache libs etc ?
Like #Bryan Kyle example but shorter. I like shorter, but that doesn't mean clearer, you decide. ;) Note: readByte() is signed and will have unexpected results if not masked with 0xFF.
DataInputStream dis = ...
// assuming BIG_ENDIAN format
int a = dis.read() << 16 | dis.read() << 8 | dis.read();
short b = (short) dis.read();
long c = dis.readInt() & 0xFFFFFFFFL;
or
ByteBuffer bb =
bb.position(a_random_postion);
int a = (bb.get() & 0xFF) << 16 | (bb.get() & 0xFF) << 8 | (bb.get() & 0xFF);
short b = (short) (bb.get() & 0xFF);
long c = bb.readInt() & 0xFFFFFFFFL;
You may take a look at this sample BinaryReader class which is based on the DataInputStream class.
You should be able to do this using a DataInputStream. It's been a while since I've done much development like this, but the trick I seem to remember is that if there's an impedance mis-match between your input format and the language's data types you'll need to construct the data byte by byte. In this case, it looks like you'll need to do that because the data structure has oddly sized structures.
To give you an example to read the first record you might need to do something like this (I'm using a, b, and c for the attributes of the record)
DataInputStream dis = ...
int a = 0;
a = dis.readByte();
a = a << 8;
a = a | dis.readByte();
a = a << 8;
a = a | dis.readByte();
short b = 0;
b = dis.readByte();
long c = 0;
c = dis.readByte();
c = c << 8;
c = c | dis.readByte();
c = c << 8;
c = c | dis.readByte();
c = c << 8;
c = c | dis.readByte();
Obviously, this code could be tightened up by compounding some of the statements, but you get the general idea. What you might notice is that for each of the attributes being read I have to use a primitive that's larger than needed so there aren't any overflow errors. For reference, in Java:
byte = 1 byte
short = 16 bit, 2 bytes
int = 32 bits, 4 bytes
long = 64 bits, 8 bytes

Categories