简体   繁体   中英

Converting a C++ Checksum function to Java

I'm trying to convert this C++ checksum to Java but for the time being I've failed. What am I doing wrong?

What is it supposed to do? It is supposed to return a positive checksum for a buffer in OpenGL

Here's the C part.

DWORD QuickChecksum(DWORD *data, int size){

if(!data) {
    return 0x0; 
}

DWORD sum;
DWORD tmp;
sum = *data;

for(int i = 1; i < (size/4); i++)
{
    tmp = data[i];
    tmp = (DWORD)(sum >> 29) + tmp;
    tmp = (DWORD)(sum >> 17) + tmp;
    sum = (DWORD)(sum << 3)  ^ tmp;
}

return sum;
}

And here is what I have tried in Java. As far As I know DWORD is 32bit so I use int in a long to get a unsigned int which should be done in java with >>>?

I've been looking at this problem so much now that I've grown blind to it.

public static long getChecksum(byte[] data, int size) {
    long sum, tmp;
    sum = getInt(new byte[]{data[0], data[1], data[2], data[3]},true) & 0xFF;
    for(int I = 4; I < data.length; I += 4)
    {
        tmp = getInt(new byte[]{data[I],data[I+1],data[I+2],data[I+3]},true) & 0xFF;
        tmp = (sum >>> 29) + tmp;
        tmp = (sum >>> 17) + tmp;
        sum = (sum << 3) ^ tmp;
    }
    return sum & 0xFF;
}

private static int getInt(byte[] bytes, boolean big) {
    ByteBuffer bb = ByteBuffer.wrap(bytes);
    return bb.getInt();
}

Thank you all for your help!

The obvious error is that, in three places, you AND the input word and the final checksum with 0xff , losing the upper 24 bits. Presumably, you're trying to reduce the long value to 32 bits, which requires an AND with 0xffffffffL . You'll also need to convert the return value of getInt() to long before doing that, otherwise you'll still get the sign extension you're trying to avoid.

My Java's a bit rusty, but I'm fairly sure that you'll get the correct results by sticking with int , as long as you use >>> for the right-shift (as you do).

You also have a bug where you unconditionally read the first four bytes without checking that the input isn't empty.

You'll also need to make sure that the input has a multiple of 4 bytes; either by checking the length, or by changing it to work with int[] rather than byte[] like the C version does. And of course there's no need for a size parameter, since Java arrays carry their size around with them.

The following should give the same results as the C version:

public static int checksum(int[] data)
{
    if (data.length == 0) {
        return 0;
    }

    int sum = data[0];
    for (int i = 1; i < data.length; ++i) {
        int tmp = data[i];
        tmp = (sum >>> 29) + tmp;
        tmp = (sum >>> 17) + tmp;
        sum = (sum << 3)   ^ tmp;
    }

    return sum;
}

In Java, >>> performs unsigned shift, meaning it will insert bit value 0 to new bits shifted in, turning negative number to positive. Signed shift >> extends the sign bit, which is 1 for negative values, so negative numbers stay negative.

To fix your code, at least replace 0xFF with 0xFFFFFFFF in your & operations. Also, I think you may have to do that every time you assign to tmp and sum , not just once after the loop (not 100% sure, would have to go through the code to see if right bits get preserved and no extra bits sneak in even without ANDing, so better safe than sorry).

I'd also add as first thing in the method:

if (data.length & 3 != 0) 
    throw new IllegalArgumentException("byte buffer size not multiple of 4");

Also, I'd either remove the size argument, or actually use it instead of data.length (after checking it is valid), in case data might have more bytes than you want to process.

The C++ version returns 32 bits of the sum , but your Java version does & 0xFF , which only leaves 8.

  1. You do unsigned shift with >>> and all the other operations are defined so that their result in signed Java type is equal to the same operation on C++ unsigned type except for interpretation of the signed bit. So you can actually use int here.
  2. If you keep using long , you need to use & 0xFFFFFFFF to get 32 bits.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM