views:

1111

answers:

5

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
A: 

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 :(

Jon Skeet
Thanks for the response Jon. So I tried that, and it wasn't easy to put the long values back together into a GUID on the other end :)I've got it working one way, but not the other way. I'll add some code to my question.
Alex Black
You shouldn't have to switch endianness of the longs at the Java side. You may have to at the C++ side.
Jon Skeet
I guess a simpler question to ask is:a. if I send the MostSigBits and LeastSigBits over to C++ as longs, how do I combine them back into the same GUID?b. From C++, if I have a GUID, how do I break it apart into the most and least bits in such a way that Java can put it back together as the same GUID?
Alex Black
I'm afraid I don't know about the C++ side of things :(
Jon Skeet
A: 

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

Peter Lawrey
Because when I put them back together in C++ I don't get the same GUID. (See my original question for how I am putting them back together)
Alex Black
A UUID is just a unique identifier. I assume there is a reason the numbers have to appear the same (e.g. for comparing logs?)
Peter Lawrey
+1  A: 

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.

nos
+2  A: 

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);
Alex Black
A: 

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;
}
Hannes de Jager