Steve
Steve

Reputation: 798

unsigned long in java, using BigInteger for arithmetics, but BigInteger.toByteArray returns 14 bytes instead of 8

I have the following c code which id like to port to Java

unsigned long long* data=(unsigned long long*)pBuffer; // file data
unsigned long long crypt = 0x0000;
unsigned long long next_crypt;
unsigned int len = size >> 3;

for(unsigned int i=0; i<len;i++) {
        next_crypt = crypt+data[i]-0xCAFEBABE;      
        data[i] = ((data[i]<<0x1d)|(data[i]>>0x23))+0xCAFEBABE;
        data[i] =  (data[i]<<0x0e)|(data[i]>>0x32);
        data[i] = data[i] - crypt;
        crypt = next_crypt;     

    }

I tried to port this to java using long, however this would result in negative values. Therefor i switched to biginteger since i have to do arithmetics (bit shifting etc).

I got the desired 64bit unsigned long value using BigInteger, however when i wanted to convert it to byte (BigInteger.toByteArray) it was 14 bytes long and no longer 8 bytes - so i cannot modify my array/file anymore. I tried using toLongValue() but the data was incorrect.

Thanks

Upvotes: 1

Views: 1749

Answers (2)

tc.
tc.

Reputation: 33592

The nice thing about Java is that it's guaranteed to be twos-complement, so provided you use >>> instead of >> and avoid % and / and inequalities, arithmetic is effectively unsigned anyway.

Upvotes: 1

rlibby
rlibby

Reputation: 6021

Your C code is relying on bits being shifted off the high-order end of the unsigned long long. (These are rotated around to the other end by the other shift.) BigInteger is arbitrary precision and hence has no end, so left-shifted bits are never shifted off.

You could construct a 64-bit BigInteger bitwise AND mask and AND it after the left shifts. This is an intuitive solution.

You could also just simply ignore the high-order bytes.

byte[] bar = foo.toByteArray();
if (bar.length > 8) {
    bar = System.arrayCopy(bar, bar.length - 8, new byte[8], 0, 8);
}

If len is large, then this simple solution would be wasteful of memory.

In any case there is a saner and higher-performing solution. Java's signed integer types are all guaranteed to have two's complement semantics. The bit semantics for arithmetic with two's complement integers and unsigned integers are identical--the difference is only in the interpretation of the value! So just use the original C code (substituting in Java's long) and at the end, interpret them your way.

byte[] longToByteArray(long x) {
    byte[] array = new byte[8];
    for (int i = 7; i >= 0; i--) {
        array[i] = (byte)x;
        x >>>= 8;
    }
}

By the way, be sure you replace the >> operator in the C code with Java's >>> operator.

Upvotes: 2

Related Questions