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);
Related
I m trying to convert 8 bits into one byte. The way the bits are represented are by using a byte object that only contains a 1 or a 0.
If i have a 8 length byte array with these bits, how can i convert them into one byte.
public byte bitsToByte(byte[] bits) {
//Something in here. Each byte inside bits is either a 1 or a 0.
}
Can anyone help?
Thanks
public static byte bitsToByte(byte[] bits){
byte b = 0;
for (int i = 0; i < 8; i++)
b |= (bits[i]&1) << i;
return b;
}
//as an added bonus, the reverse.
public static byte[] bitsToByte(byte bits){
byte[] b = new byte[8];
for (int i = 0; i < 8; i++)
b[i] = (byte) ((bits&(1 << i)) >>> i);
return b;
}
left shift by 1 first for each bit in the array and then add the bit to the byte.
The implementation is based on the assumption that first bit in the array is sign bit and following bits are the magnitude in higher to lower positions of the byte.
public byte bitsToByte(byte[] bits) {
byte value = 0;
for (byte b : bits) {
value <<=1;
value += b;
}
return value;
}
Test the method:
public static void main(String[] args) {
BitsToByte bitsToByte = new BitsToByte();
byte bits[] = new byte[]{0,0,1,0,1,1,0,1}; // 1 + 0 + 4 + 8 + 0 + 32 + 0 + 0
byte value = bitsToByte.bitsToByte(bits);
System.out.println(value);
}
output:
45
Covert the byte array into a byte value (in the same order):
public static byte bitsToByte1(byte[] bits){
byte result = 0;
for (byte i = 0; i < bits.length; i++) {
byte tmp = bits[i];
tmp <<= i; // Perform the left shift by "i" times. "i" position of the bit
result |= tmp; // perform the bit-wise OR
}
return result;
}
input: (same array in reverse)
byte bits1[] = new byte[]{1,0,1,1,0,1,0,0};
value = bitsToByte1(bits1);
System.out.println(value);
output:
45
I am trying to implement the Tiny Encryption Algorithm (TEA) in java. Since the algorithm divides each 64 bit block into left and right sub-blocks, where each sub-block is 32 unsigned integer according to this source.
As expected, I faced the issue of java not supporting unsigned 32 bit integer. I was getting errors about the number Format everywhere.
So I decided to use BigInteger, which introduced a new problem to me. Based on my understanding, since TEA uses shifting and addition of 32 bits integers, It is supposed to keep the result of these operation in 32 bits, so that the ciphertext will also be 32 bits per sub-block. However, with shifting and addition, BigInteger did not keep 32 bits per sub-block. Indeed, I got unexpectedly a very large number of bits as a ciphertext output.
So I decided to keep the BigInteger, while implementing the shift, addition, and subtraction in separate methods. Unfortunately, I am not getting correct results. As illustrated below, the decrypted ciphertext does not equal the original plaintext. What is the solution to my problem? I am getting the following output:
Original Plain Text:0x0123456789ABCDEF
CipherText:0xa0761126d09724fd
Decrypted CipherText is:0x8d5a4a234b3c6720
Below is my code.
import java.math.BigInteger;
public class TEA {
BigInteger [] K ; //128 bits key
private String plainText;
public static final BigInteger delta = new BigInteger("9e3779b9",16);
//constructor receives a string of plaintext and 128 bit key in hexadecimal
public TEA(String plainText, String key)
{
parseKey(key);
}
//constructor receives a hexadecimal
public TEA(String key)
{
parseKey(key);
}
//parses a 128 bit key, given in hexadecimal form, and store its value in 4 integers (total of 128 bits),
private void parseKey(String key)
{
if(key.substring(0,2).equals("0x"))
key= key.substring(2);
//validating input
if(key.length() != 32)
{
System.out.println("Invalid key size!");
return;
}
//dividing the key into 4 strings
String[] kStr = new String[4];
int index=-1;
for(int i=0; i<key.length(); i++)
{
if(i%8 == 0)
{
index++;
kStr[index]="";
}
kStr[index] = kStr[index] + key.charAt(i);
}
//converting the 4 hex strings into 4 integers
K= new BigInteger[4];
for(int i=0; i<4; i++)
K[i] = new BigInteger(kStr[i], 16);
}
//receives a plaintext block of 64 bits in hexadecimal to be encrypted
//returns the cipher block
String encryptBlock(String plainTextBlock)
{
if(plainTextBlock.substring(0,2).equals("0x"))
plainTextBlock= plainTextBlock.substring(2);
//validating input
if(plainTextBlock.length()!=16)
{
System.out.println("Invalid block size!");
return null;
}
//separating the string block into left and right blocks
String LStr = plainTextBlock.substring(0, 8); //left block (32 bit)
String RStr = plainTextBlock.substring(8); //right block (32 bit)
//converting left and right blocks to integers
BigInteger L = new BigInteger(LStr, 16);
BigInteger R = new BigInteger(RStr, 16);
BigInteger sum= new BigInteger("0");
//32 rounds
for(int i=0; i<32; i++)
{
sum = sum.add(delta);
L= sum(L, (sum(shiftLeft(R,4),K[0])) .xor(sum(R,sum)) .xor(sum(shiftRight(R,5),K[1]))) ;
R= sum(R, (sum(shiftLeft(L,4),K[2])) .xor(sum(L,sum)) .xor(sum(shiftRight(L,5),K[3]))) ;
//R= R.add( (shiftLeft(R,4).add(K[2])).xor(L.add(sum)).xor(shiftRight(L,5).add(K[3])) );
}
//joining back the blocks as hex
String cipherBlock = "0x"+L.toString(16)+R.toString(16)+"";
return cipherBlock;
}
//receives a ciphertext block of 64 bits in hexadecimal to be decrypted
//returns the plaintext block
String decryptBlock(String cipherBlock)
{
if(cipherBlock.substring(0,2).equals("0x"))
cipherBlock= cipherBlock.substring(2);
//validating input
if(cipherBlock.length()!=16)
{
System.out.println("Invalid block size!");
return null;
}
//separating the string block into left and right blocks
String LStr = cipherBlock.substring(0, 8); //left block (32 bit)
String RStr = cipherBlock.substring(8); //right block (32 bit)
//converting left and right blocks to integers
BigInteger L = new BigInteger(LStr, 16);
BigInteger R = new BigInteger(RStr, 16);
BigInteger sum= shiftLeft(delta,5);
//32 rounds
for(int i=0; i<32; i++)
{
R= subtract(R, (sum(shiftLeft(L,4),K[2])) .xor(sum(L,sum)) .xor(sum(shiftRight(L,5),K[3]))) ;
L= subtract(L, (sum(shiftLeft(R,4),K[0])) .xor(sum(R,sum)) .xor(sum(shiftRight(R,5),K[1]))) ;
//R= R.subtract( (L.shiftLeft(4).add(K[2])).xor(L.add(sum)).xor(L.shiftRight(5).add(K[3])) );
//L= L.subtract( (R.shiftLeft(4).add(K[0])).xor(R.add(sum)).xor(R.shiftRight(5).add(K[1])) );
sum = sum.subtract(delta);
}
//joining back the blocks as hex
String plainTextBlock = "0x"+L.toString(16)+R.toString(16)+"";
return plainTextBlock;
}
private BigInteger shiftLeft(BigInteger x, int steps)
{
BigInteger shifted=null;
boolean negative =false;
String xStr = x.toString(2);
//removing negative sign while shifting (currently)
if(xStr.charAt(0)=='-')
{
negative= true;
xStr = xStr.substring(1);
}
int additionalSize = 32- xStr.length();
for(int i=0; i<additionalSize; i++)
xStr= "0"+xStr;
for(int i=0; i<steps; i++)
{
xStr = xStr.substring(1);
xStr = xStr+"0";
}
//one last addition of negative sign if the number is negative
if(negative==true)
xStr= "-"+xStr;
//System.out.println(xStr);
shifted = new BigInteger(xStr,2);
return shifted;
}
private BigInteger shiftRight(BigInteger x, int steps)
{
BigInteger shifted=null;
boolean negative = false;
String xStr = x.toString(2);
//removing negative sign while shifting (currently)
if(xStr.charAt(0)=='-')
{
negative= true;
xStr = xStr.substring(1);
}
int additionalSize = 32- xStr.length();
for(int i=0; i<additionalSize; i++)
xStr= "0"+xStr;
for(int i=0; i<steps; i++)
{
xStr = xStr.substring(0,xStr.length()-1);
xStr = "0"+xStr;
}
//one last addition of negative sign if the number is negative
if(negative==true)
xStr= "-"+xStr;
shifted = new BigInteger(xStr,2);
return shifted;
}
private BigInteger sum(BigInteger a, BigInteger b)
{
BigInteger sum = a.add(b);
String sumStr = sum.toString(2);
if(sumStr.length()>32)
{
int diff = sumStr.length()- 32;
sumStr = sumStr.substring(diff);
}
BigInteger newSum = new BigInteger(sumStr,2);
return newSum;
}
private BigInteger subtract(BigInteger a, BigInteger b)
{
BigInteger sub = a.subtract(b);
String subStr = sub.toString(2);
if(subStr.length()>32)
{
int diff = subStr.length()- 32;
subStr = subStr.substring(diff);
}
BigInteger newSub = new BigInteger(subStr,2);
return newSub;
}
public static void main(String[] args)
{
String plainText="0x0123456789ABCDEF";
String key= "0xA56BABCD00000000FFFFFFFFABCDEF01";
TEA tea = new TEA(key);
String cipherText = tea.encryptBlock(plainText);
System.out.println("Original Plain Text:"+plainText);
System.out.println("CipherText:"+cipherText);
System.out.println("Decrypted CipherText is:"+tea.decryptBlock(cipherText));
}
}
I saw no reason to use BigIntegers, so I tried my own implementation in Java that is almost a verbatim copy of the C code in the wikipedia article. It looks correct to me, though I don't have tests to run against it.
public class TEAToy {
private final static int DELTA = 0x9e3779b9;
private final static int DECRYPT_SUM_INIT = 0xC6EF3720;
private final static long MASK32 = (1L << 32) - 1;
public static long encrypt(long in, int [] k) {
int v1 = (int) in;
int v0 = (int) (in >>> 32);
int sum = 0;
for (int i=0; i<32; i++) {
sum += DELTA;
v0 += ((v1<<4) + k[0]) ^ (v1 + sum) ^ ((v1>>>5) + k[1]);
v1 += ((v0<<4) + k[2]) ^ (v0 + sum) ^ ((v0>>>5) + k[3]);
}
return (v0 & MASK32) << 32 | (v1 & MASK32);
}
public static long decrypt(long in, int [] k) {
int v1 = (int) in;
int v0 = (int) (in >>> 32);
int sum = DECRYPT_SUM_INIT;
for (int i=0; i<32; i++) {
v1 -= ((v0<<4) + k[2]) ^ (v0 + sum) ^ ((v0>>>5) + k[3]);
v0 -= ((v1<<4) + k[0]) ^ (v1 + sum) ^ ((v1>>>5) + k[1]);
sum -= DELTA;
}
return (v0 & MASK32) << 32 | (v1 & MASK32);
}
As you can see, the fact the Java's ints are signed makes very little difference.
I have the following hex string:
00000000000008a3a41b85b8b29ad444def299fee21793cd8b9e567eab02cd81
but I want it to look like this:
81cd02ab7e569e8bcd9317e2fe99f2de44d49ab2b8851ba4a308000000000000 (Big
endian)
I think I have to reverse and swap the string, but something like this doesn't give me right result:
String hex = "00000000000008a3a41b85b8b29ad444def299fee21793cd8b9e567eab02cd81";
hex = new StringBuilder(hex).reverse().toString();
Result:
81dc20bae765e9b8dc39712eef992fed444da92b8b58b14a3a80000000000000
(wrong)
81cd02ab7e569e8bcd9317e2fe99f2de44d49ab2b8851ba4a308000000000000
(should be)
The swapping:
public static String hexSwap(String origHex) {
// make a number from the hex
BigInteger orig = new BigInteger(origHex,16);
// get the bytes to swap
byte[] origBytes = orig.toByteArray();
int i = 0;
while(origBytes[i] == 0) i++;
// swap the bytes
byte[] swapBytes = new byte[origBytes.length];
for(/**/; i < origBytes.length; i++) {
swapBytes[i] = origBytes[origBytes.length - i - 1];
}
BigInteger swap = new BigInteger(swapBytes);
return swap.toString(10);
}
hex = hexSwap(hex);
Result:
026053973026883595670517176393898043396144045912271014791797784
(wrong)
81cd02ab7e569e8bcd9317e2fe99f2de44d49ab2b8851ba4a308000000000000
(should be)
Can anyone give me a example of how to accomplish this?
Thank you a lot :)
You need to swap each pair of characters, as you're reversing the order of the bytes, not the nybbles. So something like:
public static String reverseHex(String originalHex) {
// TODO: Validation that the length is even
int lengthInBytes = originalHex.length() / 2;
char[] chars = new char[lengthInBytes * 2];
for (int index = 0; index < lengthInBytes; index++) {
int reversedIndex = lengthInBytes - 1 - index;
chars[reversedIndex * 2] = originalHex.charAt(index * 2);
chars[reversedIndex * 2 + 1] = originalHex.charAt(index * 2 + 1);
}
return new String(chars);
}
I need to store a couple binary sequences that are 16 bits in length into a byte array (of length 2). The one or two binary numbers don't change, so a function that does conversion might be overkill. Say for example the 16 bit binary sequence is 1111000011110001. How do I store that in a byte array of length two?
String val = "1111000011110001";
byte[] bval = new BigInteger(val, 2).toByteArray();
There are other options, but I found it best to use BigInteger class, that has conversion to byte array, for this kind of problems. I prefer if, because I can instantiate class from String, that can represent various bases like 8, 16, etc. and also output it as such.
Edit: Mondays ... :P
public static byte[] getRoger(String val) throws NumberFormatException,
NullPointerException {
byte[] result = new byte[2];
byte[] holder = new BigInteger(val, 2).toByteArray();
if (holder.length == 1) result[0] = holder[0];
else if (holder.length > 1) {
result[1] = holder[holder.length - 2];
result[0] = holder[holder.length - 1];
}
return result;
}
Example:
int bitarray = 12321;
String val = Integer.toString(bitarray, 2);
System.out.println(new StringBuilder().append(bitarray).append(':').append(val)
.append(':').append(Arrays.toString(getRoger(val))).append('\n'));
I have been disappointed with all of the solutions I have found to converting strings of bits to byte arrays and vice versa -- all have been buggy (even the BigInteger solution above), and very few are as efficient as they should be.
I realize the OP was only concerned with a bit string to an array of two bytes, which the BitInteger approach seems to work fine for. However, since this post is currently the first search result when searching "bit string to byte array java" in Google, I am going to post my general solution here for people dealing with huge strings and/or huge byte arrays.
Note that my solution below is the only solution I have ran that passes all of my test cases -- many online solutions to this relatively simple problem simply do not work.
Code
/**
* Zips (compresses) bit strings to byte arrays and unzips (decompresses)
* byte arrays to bit strings.
*
* #author ryan
*
*/
public class BitZip {
private static final byte[] BIT_MASKS = new byte[] {1, 2, 4, 8, 16, 32, 64, -128};
private static final int BITS_PER_BYTE = 8;
private static final int MAX_BIT_INDEX_IN_BYTE = BITS_PER_BYTE - 1;
/**
* Decompress the specified byte array to a string.
* <p>
* This function does not pad with zeros for any bit-string result
* with a length indivisible by 8.
*
* #param bytes The bytes to convert into a string of bits, with byte[0]
* consisting of the least significant bits in the byte array.
* #return The string of bits representing the byte array.
*/
public static final String unzip(final byte[] bytes) {
int byteCount = bytes.length;
int bitCount = byteCount * BITS_PER_BYTE;
char[] bits = new char[bitCount];
{
int bytesIndex = 0;
int iLeft = Math.max(bitCount - BITS_PER_BYTE, 0);
while (bytesIndex < byteCount) {
byte value = bytes[bytesIndex];
for (int b = MAX_BIT_INDEX_IN_BYTE; b >= 0; --b) {
bits[iLeft + b] = ((value % 2) == 0 ? '0' : '1');
value >>= 1;
}
iLeft = Math.max(iLeft - BITS_PER_BYTE, 0);
++bytesIndex;
}
}
return new String(bits).replaceFirst("^0+(?!$)", "");
}
/**
* Compresses the specified bit string to a byte array, ignoring trailing
* zeros past the most significant set bit.
*
* #param bits The string of bits (composed strictly of '0' and '1' characters)
* to convert into an array of bytes.
* #return The bits, as a byte array with byte[0] containing the least
* significant bits.
*/
public static final byte[] zip(final String bits) {
if ((bits == null) || bits.isEmpty()) {
// No observations -- return nothing.
return new byte[0];
}
char[] bitChars = bits.toCharArray();
int bitCount = bitChars.length;
int left;
for (left = 0; left < bitCount; ++left) {
// Ignore leading zeros.
if (bitChars[left] == '1') {
break;
}
}
if (bitCount == left) {
// Only '0's in the string.
return new byte[] {0};
}
int cBits = bitCount - left;
byte[] bytes = new byte[((cBits) / BITS_PER_BYTE) + (((cBits % BITS_PER_BYTE) > 0) ? 1 : 0)];
{
int iRight = bitCount - 1;
int iLeft = Math.max(bitCount - BITS_PER_BYTE, left);
int bytesIndex = 0;
byte _byte = 0;
while (bytesIndex < bytes.length) {
while (iLeft <= iRight) {
if (bitChars[iLeft] == '1') {
_byte |= BIT_MASKS[iRight - iLeft];
}
++iLeft;
}
bytes[bytesIndex++] = _byte;
iRight = Math.max(iRight - BITS_PER_BYTE, left);
iLeft = Math.max((1 + iRight) - BITS_PER_BYTE, left);
_byte = 0;
}
}
return bytes;
}
}
Performance
I was bored at work so I did some performance testing comparing against the accepted answer here for when N is large. (Pretending to ignore the fact that the BigInteger approach posted above doesn't even work properly as a general approach.)
This is running with a random bit string of size 5M and a random byte array of size 1M:
String -> byte[] -- BigInteger result: 39098ms
String -> byte[] -- BitZip result: 29ms
byte[] -> String -- Integer result: 138ms
byte[] -> String -- BitZip result: 71ms
And the code:
public static void main(String[] argv) {
int testByteLength = 1000000;
int testStringLength = 5000000;
// Independently random.
final byte[] randomBytes = new byte[testByteLength];
final String randomBitString;
{
StringBuilder sb = new StringBuilder();
Random rand = new Random();
for (int i = 0; i < testStringLength; ++i) {
int value = rand.nextInt(1 + i);
sb.append((value % 2) == 0 ? '0' : '1');
randomBytes[i % testByteLength] = (byte) value;
}
randomBitString = sb.toString();
}
byte[] resultCompress;
String resultDecompress;
{
Stopwatch s = new Stopwatch();
TimeUnit ms = TimeUnit.MILLISECONDS;
{
s.start();
{
resultCompress = compressFromBigIntegerToByteArray(randomBitString);
}
s.stop();
{
System.out.println("String -> byte[] -- BigInteger result: " + s.elapsed(ms) + "ms");
}
s.reset();
}
{
s.start();
{
resultCompress = zip(randomBitString);
}
s.stop();
{
System.out.println("String -> byte[] -- BitZip result: " + s.elapsed(ms) + "ms");
}
s.reset();
}
{
s.start();
{
resultDecompress = decompressFromIntegerParseInt(randomBytes);
}
s.stop();
{
System.out.println("byte[] -> String -- Integer result: " + s.elapsed(ms) + "ms");
}
s.reset();
}
{
s.start();
{
resultDecompress = unzip(randomBytes);
}
s.stop();
{
System.out.println("byte[] -> String -- BitZip result: " + s.elapsed(ms) + "ms");
}
s.reset();
}
}
}
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. ;)