2012-12-12 32 views
1

我试图将此C++校验和转换为Java,但暂时失败。我究竟做错了什么?将C++校验和函数转换为Java

它应该做什么? 它应该返回OpenGL中的缓冲区的正校验和

这是C部分。

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; 
} 

这里是我在Java中尝试过的。据我所知DWORD是32位,所以我用长整型得到一个unsigned int,应该在java中用>>>完成>>>?

我一直在研究这个问题,现在我已经对此视而不见了。

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(); 
} 

谢谢大家的帮忙!

+1

没有详细看这个,你有没有保证字节顺序是正确的? – NPE

+3

那里有什么&0xFF? – themel

+1

鉴于您提出了“应该做什么?”这个问题,我建议您退后一步,问自己是否应该首先使用它。网络非常适合查找代码片段,但个人而言,我希望在我准备将它们包含在自己的工作中之前了解他们正在做什么。 – PeteH

回答

1

显而易见的错误是,在三个地方,你与输入字和最终校验和0xff,失去了高位24位。假设你试图将long的值减少到32位,这需要一个AND与0xffffffffL。在此之前,您还需要将返回值getInt()转换为long,否则您仍然会得到您要避免的符号扩展名。

我的Java有点生疏,但我相当肯定,只要你使用>>>来实现右移(就像你这样做),你就可以通过坚持int得到正确的结果。

您还有一个错误,您无条件读取前四个字节而未检查输入是否为空。

您还需要确保输入具有4个字节的倍数;通过检查长度,或者将其更改为与一起使用,而不是像C版本那样使用byte[]。当然,不需要size参数,因为Java数组的大小随着它们的大小而变化。

以下应该得到相同的结果C版:

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; 
} 
+0

刚刚做了所有这些更改,甚至尝试了您的代码(您的代码确实给了我类似于正确结果的结果),但结果是:旧校验和= 4011644098,新校验和为713196854,并且由于某种原因仍然存在负校验和。难道它需要很长时间才能工作? – user1897599

+0

@ user1897599:您将在Java中得到负面校验和,因为Java没有无符号类型的概念。二进制值应该是正确的;如果你真的想要一个正值,那么我想你可以用'long'来处理返回值('return(long)sum&0xffffffffL'),但中间结果不需要。如果您得到不同的积极结果,那么数据或计算结果都不相同。尝试通过调试器来查看两者的区别。 –

0

在Java中,>>>执行无符号偏移,这意味着它将插入比特值0到新的比特移位,转动负数转正。带符号的移位>>扩展了符号位,负值为1,所以负数保持负值。

要修复您的代码,请至少在您的&操作中将0xFF替换为0xFFFFFFFF。另外,我认为你可能必须这样做,每次你分配tmpsum,不只是一次循环后(不是100%肯定,将不得不通过代码,看看是否保留了正确的位,没有额外的位偷偷摸摸在没有ANDING的情况下,安全性比抱歉更好)。

我也想在方法中添加的第一件事:

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

而且,我要么删除size参数,或者(检查它是否有效后),实际使用它,而不是data.length,在案例数据可能比您想要处理的字节数多。

0

C++版本返回sum的32位,但你的Java版本确实& 0xFF,只留下8

  1. 你做>>>和所有其他的操作无符号转变的定义,使他们的结果有符号Java类型除了解释有符号位之外,与C++无符号类型相同。所以你可以在这里使用int
  2. 如果您继续使用long,您需要使用​​来获取32位。