Sending a Java UUID to C++ as bytes and back over TCP - java

I'm trying to send a Java UUID to C++, where it will be used as a GUID, then send it back and see it as a UUID, and I'm hoping to send it across as just 16 bytes.
Any suggestions on an easy way to do this?
I've got a complicated way of doing it, sending from Java to C++, where I ask the UUID for its least and most significant bits, write this into a ByteBuffer, and then read it out as bytes.
Here is my silly-complicated way of getting 2 longs out of a UUID, sending them to C++:
Java
public static byte[] asByteArray(UUID uuid)
{
long msb = uuid.getMostSignificantBits();
long lsb = uuid.getLeastSignificantBits();
byte[] buffer = new byte[16];
for (int i = 0; i < 8; i++) {
buffer[i] = (byte) (msb >>> 8 * (7 - i));
}
for (int i = 8; i < 16; i++) {
buffer[i] = (byte) (lsb >>> 8 * (7 - i));
}
return buffer;
}
byte[] bytesOriginal = asByteArray(uuid);
byte[] bytes = new byte[16];
// Reverse the first 4 bytes
bytes[0] = bytesOriginal[3];
bytes[1] = bytesOriginal[2];
bytes[2] = bytesOriginal[1];
bytes[3] = bytesOriginal[0];
// Reverse 6th and 7th
bytes[4] = bytesOriginal[5];
bytes[5] = bytesOriginal[4];
// Reverse 8th and 9th
bytes[6] = bytesOriginal[7];
bytes[7] = bytesOriginal[6];
// Copy the rest straight up
for ( int i = 8; i < 16; i++ )
{
bytes[i] = bytesOriginal[i];
}
// Use a ByteBuffer to switch our ENDIAN-ness
java.nio.ByteBuffer buffer = java.nio.ByteBuffer.allocate(16);
buffer.order(java.nio.ByteOrder.BIG_ENDIAN);
buffer.put(bytes);
buffer.order(java.nio.ByteOrder.LITTLE_ENDIAN);
buffer.position(0);
UUIDComponents x = new UUIDComponents();
x.id1 = buffer.getLong();
x.id2 = buffer.getLong();
C++
google::protobuf::int64 id1 = id.id1();
google::protobuf::int64 id2 = id.id2();
char* pGuid = (char*) &guid;
char* pGuidLast8Bytes = pGuid + 8;
memcpy(pGuid, &id1, 8);
memcpy(pGuidLast8Bytes, &id2, 8);
This works, but seems way too complex, and I can't yet get it working in the other direction.
(I'm using google protocol buffers to send the two longs back and forth)
Alex

I got something working.
Instead of sending it across as two longs, I send it across as bytes, here is the Java code:
public static UUID fromBytes( ByteString byteString)
{
byte[] bytesOriginal = byteString.toByteArray();
byte[] bytes = new byte[16];
// Reverse the first 4 bytes
bytes[0] = bytesOriginal[3];
bytes[1] = bytesOriginal[2];
bytes[2] = bytesOriginal[1];
bytes[3] = bytesOriginal[0];
// Reverse 6th and 7th
bytes[4] = bytesOriginal[5];
bytes[5] = bytesOriginal[4];
// Reverse 8th and 9th
bytes[6] = bytesOriginal[7];
bytes[7] = bytesOriginal[6];
// Copy the rest straight up
for ( int i = 8; i < 16; i++ )
{
bytes[i] = bytesOriginal[i];
}
return toUUID(bytes);
}
public static ByteString toBytes( UUID uuid )
{
byte[] bytesOriginal = asByteArray(uuid);
byte[] bytes = new byte[16];
// Reverse the first 4 bytes
bytes[0] = bytesOriginal[3];
bytes[1] = bytesOriginal[2];
bytes[2] = bytesOriginal[1];
bytes[3] = bytesOriginal[0];
// Reverse 6th and 7th
bytes[4] = bytesOriginal[5];
bytes[5] = bytesOriginal[4];
// Reverse 8th and 9th
bytes[6] = bytesOriginal[7];
bytes[7] = bytesOriginal[6];
// Copy the rest straight up
for ( int i = 8; i < 16; i++ )
{
bytes[i] = bytesOriginal[i];
}
return ByteString.copyFrom(bytes);
}
private static byte[] asByteArray(UUID uuid)
{
long msb = uuid.getMostSignificantBits();
long lsb = uuid.getLeastSignificantBits();
byte[] buffer = new byte[16];
for (int i = 0; i < 8; i++) {
buffer[i] = (byte) (msb >>> 8 * (7 - i));
}
for (int i = 8; i < 16; i++) {
buffer[i] = (byte) (lsb >>> 8 * (7 - i));
}
return buffer;
}
private static UUID toUUID(byte[] byteArray) {
long msb = 0;
long lsb = 0;
for (int i = 0; i < 8; i++)
msb = (msb << 8) | (byteArray[i] & 0xff);
for (int i = 8; i < 16; i++)
lsb = (lsb << 8) | (byteArray[i] & 0xff);
UUID result = new UUID(msb, lsb);
return result;
}
Doing it this way, the bytes can be used straight up on the C++ side. I suppose the switching around of the order of the bytes could be done on either end.
C++
memcpy(&guid, data, 16);

It's possibly easiest to use getMostSignificantBits and getLeastSignificant bits to get long values, and send those. Likewise you can reconstruct the UUID from those two longs using the appropriate constructor.
It's a shame there isn't a toByteArray/fromByteArray pair of methods :(

Your current way is fine, nothing wrong about doing it that way.
Another approace is yo just communicate with the string representation of the uuid, send the string, parse it in c++.
Btw, bytes do not have endianess, Unless you're casting a byte/char array or similar to an integer type, you just determine the endianess by assigning the bytes back in the approprate order.

Here is what I do to convert a C++ GUID to a Java UUID. On the C++ side, the GUID struct is just converted to bytes. The conversion to C++ can then just go along the same lines.
public static UUID cppGuidBytesToUuid(byte[] cppGuid) {
ByteBuffer b = ByteBuffer.wrap(cppGuid);
b.order(ByteOrder.LITTLE_ENDIAN);
java.nio.ByteBuffer out = java.nio.ByteBuffer.allocate(16);
out.order(ByteOrder.BIG_ENDIAN);
out.putInt(b.getInt());
out.putShort(b.getShort());
out.putShort(b.getShort());
out.put(b);
out.position(0);
return new UUID(out.getLong(), out.getLong());
}
// Here is the JNI code ;-)
jbyteArray GUID2ByteArray(JNIEnv *env,GUID* guid)
{
if (guid == NULL)
return NULL;
jbyteArray jGUID = env->NewByteArray(sizeof(GUID));
if (jGUID == NULL)
return NULL;
env->SetByteArrayRegion(jGUID,0,sizeof(GUID),(signed char*)(guid));
if (env->ExceptionOccurred() != NULL)
return NULL;
return jGUID;
}

Perhaps you could explain why you are not just doing.
UUID uuid =
x.id1 = uuid.getMostSignificantBits();
x.id2 = uuid.getLeastSignificantBits();
P.S. As I read #Jon Skeet's post again, I think this is much the same advice. ;)

Related

Java Byte Operation - Converting 3 Byte To Integer Data

I have some byte-int operations. But I cant figure out the problem.
First of all I have a hex data and I am holding it as an integer
public static final int hexData = 0xDFC10A;
And I am converting it to byte array with this function:
public static byte[] hexToByteArray(int hexNum)
{
ArrayList<Byte> byteBuffer = new ArrayList<>();
while (true)
{
byteBuffer.add(0, (byte) (hexNum % 256));
hexNum = hexNum / 256;
if (hexNum == 0) break;
}
byte[] data = new byte[byteBuffer.size()];
for (int i=0;i<byteBuffer.size();i++){
data[i] = byteBuffer.get(i).byteValue();
}
return data;
}
And I want to convert 3 byte array to integer back again how can I do that?
Or you can also suggest other converting functions like hex-to-3-bytes-array and 3-bytes-to-int thank you again.
UPDATE
In c# someone use below function but not working in java
public static int byte3ToInt(byte[] byte3){
int res = 0;
for (int i = 0; i < 3; i++)
{
res += res * 0xFF + byte3[i];
if (byte3[i] < 0x7F)
{
break;
}
}
return res;
}
This will give you the value:
(byte3[0] & 0xff) << 16 | (byte3[1] & 0xff) << 8 | (byte3[2] & 0xff)
This assumes, the byte array is 3 bytes long. If you need to convert also shorter arrays you can use a loop.
The conversion in the other direction (int to bytes) can be written with logical operations like this:
byte3[0] = (byte)(hexData >> 16);
byte3[1] = (byte)(hexData >> 8);
byte3[2] = (byte)(hexData);
You could use Java NIO's ByteBuffer:
byte[] bytes = ByteBuffer.allocate(4).putInt(hexNum).array();
And the other way round is possible too. Have a look at this.
As an example:
final byte[] array = new byte[] { 0x00, (byte) 0xdf, (byte) 0xc1, 0x0a };//you need 4 bytes to get an integer (padding with a 0 byte)
final int x = ByteBuffer.wrap(array).getInt();
// x contains the int 0x00dfc10a
If you want to do it similar to the C# code:
public static int byte3ToInt(final byte[] byte3) {
int res = 0;
for (int i = 0; i < 3; i++)
{
res *= 256;
if (byte3[i] < 0)
{
res += 256 + byte3[i]; //signed to unsigned conversion
} else
{
res += byte3[i];
}
}
return res;
}
to convert Integer to hex: integer.toHexString()
to convert hexString to Integer: Integer.parseInt("FF", 16);

Java: convert byte[] to base36 String

I'm a bit lost. For a project, I need to convert the output of a hash-function (SHA256) - which is a byte array - to a String using base 36.
So In the end, I want to convert the (Hex-String representation of the) Hash, which is
43A718774C572BD8A25ADBEB1BFCD5C0256AE11CECF9F9C3F925D0E52BEAF89
to base36, so the example String from above would be:
3SKVHQTXPXTEINB0AT1P0G45M4KI8U0HR8PGB96DVXSTDJKI1
For the actual conversion to base36, I found some piece of code here on StackOverflow:
public static String toBase36(byte[] bytes) {
//can provide a (byte[], offset, length) method too
StringBuffer sb = new StringBuffer();
int bitsUsed = 0; //will point how many bits from the int are to be encoded
int temp = 0;
int tempBits = 0;
long swap;
int position = 0;
while((position < bytes.length) || (bitsUsed != 0)) {
swap = 0;
if(tempBits > 0) {
//there are bits left over from previous iteration
swap = temp;
bitsUsed = tempBits;
tempBits = 0;
}
//fill some bytes
while((position < bytes.length) && (bitsUsed < 36)) {
swap <<= 8;
swap |= bytes[position++];
bitsUsed += 8;
}
if(bitsUsed > 36) {
tempBits = bitsUsed - 36; //this is always 4
temp = (int)(swap & ((1 << tempBits) - 1)); //get low bits
swap >>= tempBits; //remove low bits
bitsUsed = 36;
}
sb.append(Long.toString(swap, 36));
bitsUsed = 0;
}
return sb.toString();
}
Now I'm doing this:
// this creates my hash, being a 256-bit byte array
byte[] hash = PBKDF2.deriveKey(key.getBytes(), salt.getBytes(), 2, 256);
System.out.println(hash.length); // outputs "256"
System.out.println(toBase36(hash)); // outputs total crap
the "total crap" is something like
-7-14-8-1q-5se81u0e-3-2v-24obre-73664-7-5-5cor1o9s-6h-4k6hr-5-4-rt2z0-30-8-2u-8-onz-4a2j-6-8-18-8trzza3-3-2x-6-4153to-4e3l01me-6-azz-2-k-4ckq-nav-gu-irqpxx-el-1j-6-rmf8hs-1bb5ax-3z25u-2-2r-t5-22-6-6w1v-1p
so it's not even close to what I want. I tried to find a solution now, but it seems I'm a bit lost here. How do I get the base36-encoded String representation of the Hash that I need?
Try using BigInteger:
String hash = "43A718774C572BD8A25ADBEB1BFCD5C0256AE11CECF9F9C3F925D0E52BEAF89";
//use a radix of 16, default would be 10
String base36 = new BigInteger( hash, 16 ).toString( 36 ).toUpperCase();
This might work:
BigInteger big = new BigInteger(your_byte_array_to_hex_string, 16);
big.toString(36);

DES Send and Receive Modes for DESFire Authentication

I'm trying to authenticate DESFire card with my android application. I use the example in this link to decypher the bytes I got from the card. For that, I ruled out padding in decryption (commented out below), because DESFire documentation points it out. Also, if I don't do so, decryption returns 7 bytes for input of 8 bytes. Below are DES and TripleDES decryption functions I use:
public static byte[] TripleDES_Decrypt(byte[] data,byte[][] keys)
{
int i;
byte[] tmp = new byte[data.length];
byte[] bloc = new byte[8];
K = generateSubKeys(keys[0]);
K1 = generateSubKeys(keys[1]);
K2 = generateSubKeys(keys[2]);
for (i = 0; i < data.length; i++) {
if (i > 0 && i % 8 == 0) {
bloc = encrypt64Bloc(bloc,K2, true);
bloc = encrypt64Bloc(bloc,K1, false);
bloc = encrypt64Bloc(bloc,K, true);
System.arraycopy(bloc, 0, tmp, i - 8, bloc.length);
}
if (i < data.length)
bloc[i % 8] = data[i];
}
bloc = encrypt64Bloc(bloc,K2, true);
bloc = encrypt64Bloc(bloc,K1, false);
bloc = encrypt64Bloc(bloc,K, true);
System.arraycopy(bloc, 0, tmp, i - 8, bloc.length);
//tmp = deletePadding(tmp);
return tmp;
}
public static byte[] decrypt(byte[] data, byte[] key) {
int i;
byte[] tmp = new byte[data.length];
byte[] bloc = new byte[8];
K = generateSubKeys(key);
for (i = 0; i < data.length; i++) {
if (i > 0 && i % 8 == 0) {
bloc = encrypt64Bloc(bloc,K, true);
System.arraycopy(bloc, 0, tmp, i - 8, bloc.length);
}
if (i < data.length)
bloc[i % 8] = data[i];
}
bloc = encrypt64Bloc(bloc,K, true);
System.arraycopy(bloc, 0, tmp, i - 8, bloc.length);
//tmp = deletePadding(tmp);
return tmp;
}
According to DesFire document, I need two modes of decryption, send and receive. This blog post has some explanation about it.
However, the DESFire crypto is a bit different from the normal DES/CBC scheme: The PCD uses DES “send mode” when sending data (xor before DES), and the card uses DES “recieve mode” when recieving data (xor after DES). But when the PCD recieves data, it uses normal DES/CBC mode (xor after DES), and the card uses normal DES send mode when sending data (xor before DES).
And in Android side I follow the examples and recommendations:
// connected to tag and application
// result = encoded(randB) + af
byte[] result = idTag.transceive(Utils.wrapMessage((byte)0x0a, new byte[]{(byte)0x0}));
byte[] b0 = new byte[8];
for(int i = 0; i < 8; i++) {
b0[i] = result[i];
}
// key
byte[] key = new byte[] {(byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0,
(byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0,
(byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0,
(byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0 };
byte[][] keys = new byte[3][];
keys[0]=key; keys[1]=key; keys[2]=key;
// decrypt encoded(randB)
byte[] r0 = DES.TripleDES_Decrypt(b0, keys);
// generate randA (integer 0-7 for trying)
byte[] nr = new byte[8];
for(int i = 0; i < 8; i++) {
nr[i] = Byte.parseByte(Integer.toString(i), 16);
}
// decrypt randA
byte[] b1 = DES.TripleDES_Decrypt(nr, keys);
// shift randB and get randB'
byte[] r1 =new byte[8];
for(int i = 0; i < 7; i++) {
r1[i] = r0[i + 1];
}
r1[7]=r0[0];
// concat (randA + randB')
byte[] b2 = new byte[16];
for(int i = 0; i < 16; i++)
{
if(i <= 7) {
b2[i] = b1[i];
} else {
b2[i] = r1[i - 8];
}
}
// XOR (randA + randB') with IV
// IV is told to be consisting of 0's,
// but XOR something with 0 results the same?
for(int i=0;i<16;i++) {
b2[i] = (byte) (b2[i] ^ (byte)0x0);
}
// send AF and decrypt(A+B)
// wrap message adds needed wrapping to message (90 to left, offset bytes etc.)
result = isodepTag.transceive(Utils.wrapMessage((byte)0xaf, DES.TripleDES_Decrypt(b2, keys)));
I get the first result, the encrypted randB. However, the second "result" is always "91ae", means authentication error. I'm doing something wrong here, send wrong data to card.
Can anyone tell me what must I change in the code to work in these modes? What should I XOR with data before/after TripleDES?
Not the real question, but I read that default "Key" in DesFire card is 16 zero bytes. Also the document points that I need to use TripleDES for 16 bytes of key, DES for 8 bytes of key. So I'm using and need to use TripleDES as I haven't changed the default key, am I right?
For those who need the know about CipherBlockChaining.
EDIT: I found out that I need to do XORing before and after TripleDES and I mustn't touch TripleDES's internal operations at all. I will be trying that in a while.
Deleted the inner TripleDES lines, just saying for the ones seeing the question for the first time.
OK I got the solution. My mistake was that I was sending
3DES(randA + randB')
But I should send
3DES(randA) + 3DES(randB' XOR 3DES(randA))
Here's the authentication code for Android/Java (it's so sad that this is the only one that can be found on the net currently!):
The actual authentication code:
// send initial authentication request
byte[] result = idTag.transceive(Utils.wrapMessage((byte)0x0a, new byte[]{(byte)0x0}));
// get encrypted(randB) from the response
byte[] b0 = new byte[8];
for(int i = 0; i < 8; i++) {
b0[i] = result[i];
}
// 16 bytes default key
byte[] key = new byte[] {(byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0,
(byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0,
(byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0,
(byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0 };
// keys for TripleDes
byte[][] keys = new byte[3][];
keys[0] = key; keys[1] = key; keys[2] = key;
// decrypt encoded(randB)
byte[] r0 = DES.TripleDES_Decrypt(b0, keys);
// generate randA (integer 0-7 for trying, should randomize for real-life use)
byte[] nr = new byte[8];
for(int i = 0; i < 8; i++) {
nr[i] = Byte.parseByte(Integer.toString(i), 16);
}
// decrypt randA, should XOR with IV, but IV is all 0's, not necessary
byte[] b1 = DES.TripleDES_Decrypt(nr, keys);
// shift randB one byte left and get randB'
byte[] r1 =new byte[8];
for(int i = 0; i < 7; i++) {
r1[i] = r0[i + 1];
}
r1[7]=r0[0];
// xor randB' with randA and decrypt
byte[] b2 = new byte[8];
for(int i = 0; i < 8; i++) {
b2[i] = (byte) (b1[i] ^ r1[i]);
}
b2 = DES.TripleDES_Decrypt(b2, keys);
// concat (randA + randB')
byte[] b1b2 = new byte[16];
for (int i = 0; i < b1b2.length; i++) {
if(i <= 7) {
b1b2[i] = b1[i];
} else {
b1b2[i]=b2[i-8];
}
}
result = idTag.transceive(Utils.wrapMessage((byte)0xaf, b1b2));
TripleDes is the one in the question. wrapMessage function:
public static byte[] wrapMessage (byte command, byte[] parameters) throws Exception {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
stream.write((byte) 0x90);
stream.write(command);
stream.write((byte) 0x00);
stream.write((byte) 0x00);
if (parameters != null) {
stream.write((byte) parameters.length);
stream.write(parameters);
}
stream.write((byte) 0x00);
return stream.toByteArray();
}
EDIT: Thanks to VGe0rge, we found out the reason why this authentication doesn't work from time to time. Instead of calling the 3DES function in the question, just call:
Cipher.getInstance("DESede/CBC/NoPadding");
Ismat, did you try to write data to the DESFIRE files?
As you explained, when sending data to the card: The PCD uses DES “send mode” when sending data (xor before DES), and the card uses DES “recieve mode” when recieving data (xor after DES)
So I can't get the proper code in order to implement the TDES with the XOR before..I need to do all the crypt-decrypt-crypt and the process is to slow for my application:
res = criptoTransformDec.TransformBlock(datosEscribir, 0, 8, datosEscribir, 0);
res = criptoTransformEnc.TransformBlock(datosEscribir, 0, 8, datosEscribir, 0);
res = criptoTransformDec1.TransformBlock(datosEscribir, 0, 8, datosEscribir, 0);
int l_iAux = 0;
while (l_iAux < (datosEscribir.Length - 8))
{
criptoTransformDec2 = desDec.CreateDecryptor(claveSes1, tdesInitialVector);
//desEnc2 = new DESCryptoServiceProvider();
criptoTransformEnc2 = desEnc.CreateEncryptor(claveSes2, tdesInitialVector);
//desDec3 = new DESCryptoServiceProvider();
criptoTransformDec3 = desDec.CreateDecryptor(claveSes1, tdesInitialVector);
Array.Copy(datosEscribir, 8 + l_iAux, aux1, 0, 8);
Array.Copy(datosEscribir, l_iAux, aux2, 0, 8);
DesfireBarik.XorStr(ref aux1, ref aux2, 8);
res = criptoTransformDec2.TransformBlock(aux1, 0, 8, datosEscribir, 8 + l_iAux);
res = criptoTransformEnc2.TransformBlock(datosEscribir, 8 + l_iAux, 8, datosEscribir, 8 + l_iAux);
res = criptoTransformDec3.TransformBlock(datosEscribir, 8 + l_iAux, 8, datosEscribir, 8 + l_iAux);
l_iAux += 8;
}
private static void XorStr (ref byte[] str1, ref byte[] str2, int qty )
{
int i = 0;
for (i = 0; i < qty; i++ )
str1[i] = (byte)(str1[i] ^ str2[i]);
}

java how to convert a byte-value in a byte-array in an int/long value? [duplicate]

How do I convert a long to a byte[] and back in Java?
I'm trying convert a long to a byte[] so that I will be able to send the byte[] over a TCP connection. On the other side I want to take that byte[] and convert it back into a double.
public byte[] longToBytes(long x) {
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
buffer.putLong(x);
return buffer.array();
}
public long bytesToLong(byte[] bytes) {
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
buffer.put(bytes);
buffer.flip();//need flip
return buffer.getLong();
}
Or wrapped in a class to avoid repeatedly creating ByteBuffers:
public class ByteUtils {
private static ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
public static byte[] longToBytes(long x) {
buffer.putLong(0, x);
return buffer.array();
}
public static long bytesToLong(byte[] bytes) {
buffer.put(bytes, 0, bytes.length);
buffer.flip();//need flip
return buffer.getLong();
}
}
Since this is getting so popular, I just want to mention that I think you're better off using a library like Guava in the vast majority of cases. And if you have some strange opposition to libraries, you should probably consider this answer first for native java solutions. I think the main thing my answer really has going for it is that you don't have to worry about the endian-ness of the system yourself.
You could use the Byte conversion methods from Google Guava.
Example:
byte[] bytes = Longs.toByteArray(12345L);
I tested the ByteBuffer method against plain bitwise operations but the latter is significantly faster.
public static byte[] longToBytes(long l) {
byte[] result = new byte[8];
for (int i = 7; i >= 0; i--) {
result[i] = (byte)(l & 0xFF);
l >>= 8;
}
return result;
}
public static long bytesToLong(final byte[] b) {
long result = 0;
for (int i = 0; i < 8; i++) {
result <<= 8;
result |= (b[i] & 0xFF);
}
return result;
}
For Java 8+ we can use the static variables that were added:
public static byte[] longToBytes(long l) {
byte[] result = new byte[Long.BYTES];
for (int i = Long.BYTES - 1; i >= 0; i--) {
result[i] = (byte)(l & 0xFF);
l >>= Byte.SIZE;
}
return result;
}
public static long bytesToLong(final byte[] b) {
long result = 0;
for (int i = 0; i < Long.BYTES; i++) {
result <<= Byte.SIZE;
result |= (b[i] & 0xFF);
}
return result;
}
If you are looking for a fast unrolled version, this should do the trick, assuming a byte array called "b" with a length of 8:
byte[] -> long
long l = ((long) b[7] << 56)
| ((long) b[6] & 0xff) << 48
| ((long) b[5] & 0xff) << 40
| ((long) b[4] & 0xff) << 32
| ((long) b[3] & 0xff) << 24
| ((long) b[2] & 0xff) << 16
| ((long) b[1] & 0xff) << 8
| ((long) b[0] & 0xff);
long -> byte[] as an exact counterpart to the above
byte[] b = new byte[] {
(byte) lng,
(byte) (lng >> 8),
(byte) (lng >> 16),
(byte) (lng >> 24),
(byte) (lng >> 32),
(byte) (lng >> 40),
(byte) (lng >> 48),
(byte) (lng >> 56)};
Why do you need the byte[]? why not just write it to the socket?
I assume you mean long rather than Long, the latter needs to allow for null values.
DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(socket.getOutputStream()));
dos.writeLong(longValue);
DataInputStream dis = new DataInputStream(
new BufferedInputStream(socket.getInputStream()));
long longValue = dis.readLong();
I find this method to be most friendly.
var b = BigInteger.valueOf(x).toByteArray();
var l = new BigInteger(b);
Just write the long to a DataOutputStream with an underlying ByteArrayOutputStream. From the ByteArrayOutputStream you can get the byte-array via toByteArray():
class Main
{
public static byte[] long2byte(long l) throws IOException
{
ByteArrayOutputStream baos=new ByteArrayOutputStream(Long.SIZE/8);
DataOutputStream dos=new DataOutputStream(baos);
dos.writeLong(l);
byte[] result=baos.toByteArray();
dos.close();
return result;
}
public static long byte2long(byte[] b) throws IOException
{
ByteArrayInputStream baos=new ByteArrayInputStream(b);
DataInputStream dos=new DataInputStream(baos);
long result=dos.readLong();
dos.close();
return result;
}
public static void main (String[] args) throws java.lang.Exception
{
long l=123456L;
byte[] b=long2byte(l);
System.out.println(l+": "+byte2long(b));
}
}
Works for other primitives accordingly.
Hint: For TCP you do not need the byte[] manually. You will use a Socket socket and its streams
OutputStream os=socket.getOutputStream();
DataOutputStream dos=new DataOutputStream(os);
dos.writeLong(l);
//etc ..
instead.
All of the current answers are more complicated than they need to be and I’d hate for anyone locating this thread to walk away without a more concise option.
You can do both of these conversions in a single line.
byte[] to long:
ByteBuffer.wrap(yourBytes).getLong();
long to byte[]:
ByteBuffer.wrap(new byte[8]).putLong(yourLong).array();
You could use the implementation in org.apache.hadoop.hbase.util.Bytes http://hbase.apache.org/apidocs/org/apache/hadoop/hbase/util/Bytes.html
The source code is here:
http://grepcode.com/file/repository.cloudera.com/content/repositories/releases/com.cloudera.hbase/hbase/0.89.20100924-28/org/apache/hadoop/hbase/util/Bytes.java#Bytes.toBytes%28long%29
Look for the toLong and toBytes methods.
I believe the software license allows you to take parts of the code and use it but please verify that.
public static long bytesToLong(byte[] bytes) {
if (bytes.length > 8) {
throw new IllegalMethodParameterException("byte should not be more than 8 bytes");
}
long r = 0;
for (int i = 0; i < bytes.length; i++) {
r = r << 8;
r += bytes[i];
}
return r;
}
public static byte[] longToBytes(long l) {
ArrayList<Byte> bytes = new ArrayList<Byte>();
while (l != 0) {
bytes.add((byte) (l % (0xff + 1)));
l = l >> 8;
}
byte[] bytesp = new byte[bytes.size()];
for (int i = bytes.size() - 1, j = 0; i >= 0; i--, j++) {
bytesp[j] = bytes.get(i);
}
return bytesp;
}
I will add another answer which is the fastest one possible ׂ(yes, even more than the accepted answer), BUT it will not work for every single case. HOWEVER, it WILL work for every conceivable scenario:
You can simply use String as intermediate. Note, this WILL give you the correct result even though it seems like using String might yield the wrong results AS LONG AS YOU KNOW YOU'RE WORKING WITH "NORMAL" STRINGS. This is a method to increase effectiveness and make the code simpler which in return must use some assumptions on the data strings it operates on.
Con of using this method: If you're working with some ASCII characters like these symbols in the beginning of the ASCII table, the following lines might fail, but let's face it - you probably will never use them anyway.
Pro of using this method: Remember that most people usually work with some normal strings without any unusual characters and then the method is the simplest and fastest way to go.
from Long to byte[]:
byte[] arr = String.valueOf(longVar).getBytes();
from byte[] to Long:
long longVar = Long.valueOf(new String(byteArr)).longValue();
Kotlin extensions for Long and ByteArray types:
fun Long.toByteArray() = numberToByteArray(Long.SIZE_BYTES) { putLong(this#toByteArray) }
private inline fun numberToByteArray(size: Int, bufferFun: ByteBuffer.() -> ByteBuffer): ByteArray =
ByteBuffer.allocate(size).bufferFun().array()
#Throws(NumberFormatException::class)
fun ByteArray.toLong(): Long = toNumeric(Long.SIZE_BYTES) { long }
#Throws(NumberFormatException::class)
private inline fun <reified T: Number> ByteArray.toNumeric(size: Int, bufferFun: ByteBuffer.() -> T): T {
if (this.size != size) throw NumberFormatException("${T::class.java.simpleName} value must contains $size bytes")
return ByteBuffer.wrap(this).bufferFun()
}
You can see full code in my library https://github.com/ArtemBotnev/low-level-extensions
If you are already using an OutputStream to write to the socket, then DataOutputStream might be a good fit. Here is an example:
// Assumes you are currently working with a SocketOutputStream.
SocketOutputStream outputStream = ...
long longValue = ...
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
dataOutputStream.writeLong(longValue);
dataOutputStream.flush();
There are similar methods for short, int, float, etc. You can then use DataInputStream on the receiving side.
Here's another way to convert byte[] to long using Java 8 or newer:
private static int bytesToInt(final byte[] bytes, final int offset) {
assert offset + Integer.BYTES <= bytes.length;
return (bytes[offset + Integer.BYTES - 1] & 0xFF) |
(bytes[offset + Integer.BYTES - 2] & 0xFF) << Byte.SIZE |
(bytes[offset + Integer.BYTES - 3] & 0xFF) << Byte.SIZE * 2 |
(bytes[offset + Integer.BYTES - 4] & 0xFF) << Byte.SIZE * 3;
}
private static long bytesToLong(final byte[] bytes, final int offset) {
return toUnsignedLong(bytesToInt(bytes, offset)) << Integer.SIZE |
toUnsignedLong(bytesToInt(bytes, offset + Integer.BYTES));
}
Converting a long can be expressed as the high- and low-order bits of two integer values subject to a bitwise-OR. Note that the toUnsignedLong is from the Integer class and the first call to toUnsignedLong may be superfluous.
The opposite conversion can be unrolled as well, as others have mentioned.
From Java 9, the best approach is to use VarHandle, which will read from the byte array as if it is a long array, for performance make the VarHandle instance a static final field.
static final VarHandle HANDLE = MethodHandles.byteArrayViewVarHandle(Long.TYPE.arrayType(), ByteOrder.nativeOrder());
static long bytesToLong(byte[] bytes, int offset) {
return (long)HANDLE.get(bytes, offset);
}
static void longToBytes(byte[] bytes, int offset, long value) {
HANDLE.set(bytes, offset, value);
}
new ObjectMapper().writeValueAsString(1234L).getBytes(); //*
Clearly this is not the ideal solution in terms of bytes, but it's what everybody seems to do these days.
Given op wants to convert it back to a double at the other end precision is probably not a concern.
(* if its not clear, this says: send it over TCP as JSON)
public static long ToInt64(byte[] buffer) throws OutOfRangeException {
if (buffer.length < 8) {
throw new OutOfRangeException();
}
long int64 = 0;
int64 = buffer[0] & 0xffL;
int64 |= ((long) buffer[1] << 8) & 0xff00L;
int64 |= ((long) buffer[2] << 16) & 0xff0000L;
int64 |= ((long) buffer[3] << 24) & 0xff000000L;
int64 |= ((long) buffer[4] << 32) & 0xff00000000L;
int64 |= ((long) buffer[5] << 40) & 0xff0000000000L;
int64 |= ((long) buffer[6] << 48) & 0xff000000000000L;
int64 |= ((long) buffer[7] << 56);
return int64;
}
static byte[] longToBytes(Long l) {
return (l + "").getBytes(StandardCharsets.UTF_8);
}

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

Categories

Resources