Invio di un UUID Java a C ++ come byte e ritorno su TCP
-
20-08-2019 - |
Domanda
Sto cercando di inviare un UUID Java a C ++, dove verrà utilizzato come GUID, quindi rispedirlo e vederlo come UUID, e spero di inviarlo attraverso appena 16 byte.
Qualche suggerimento su un modo semplice per farlo?
Ho un modo complicato di farlo, inviando da Java a C ++, dove chiedo all'UUID i suoi bit minimi e più significativi, scrivo questo in un ByteBuffer e poi lo leggo come byte.
Ecco il mio modo sciocco e complicato di ottenere 2 lunghi da un UUID, inviandoli a 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);
Funziona, ma sembra troppo complesso e non riesco ancora a farlo funzionare nella direzione opposta.
(Sto usando i buffer di protocollo google per inviare i due long avanti e indietro)
- Alex
Soluzione
Ho qualcosa che funziona.
Invece di inviarlo come due long, lo invio come byte, ecco il codice Java:
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;
}
In questo modo, i byte possono essere usati direttamente sul lato C ++. Suppongo che il cambio dell'ordine dei byte potrebbe essere fatto su o fine
C ++
memcpy(&guid, data, 16);
Altri suggerimenti
Probabilmente è più facile usare getMostSignificantBits
e getLeastSignificant
bit per ottenere long
valori e inviarli. Allo stesso modo puoi ricostruire l'UUID da quei due long usando il costruttore appropriato.
È un peccato che non ci sia una coppia di metodi toByteArray
/ fromByteArray
:(
Il tuo modo attuale va bene, niente di male nel farlo in quel modo. Un altro approccio è semplicemente comunicare con la rappresentazione in formato stringa dell'UUID, inviare la stringa, analizzarla in c ++.
A proposito, i byte non hanno endianess, a meno che non si stia eseguendo il casting di un array byte / char o simile a un tipo intero, è sufficiente determinare l'endianess assegnando i byte nell'ordine appropriato.
Ecco cosa devo fare per convertire un GUID C ++ in un UUID Java. Sul lato C ++, la struttura GUID viene appena convertita in byte. La conversione in C ++ può quindi seguire le stesse linee.
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;
}
Forse potresti spiegare perché non stai semplicemente facendo.
UUID uuid =
x.id1 = uuid.getMostSignificantBits();
x.id2 = uuid.getLeastSignificantBits();
P.S. Mentre leggo di nuovo il post di @Jon Skeet, penso che questo sia più o meno lo stesso consiglio. ;)