【问题标题】:Converting a C++ Checksum function to Java将 C++ 校验和函数转换为 Java
【发布时间】:2012-12-12 11:48:39
【问题描述】:

我正在尝试将此 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 位的,所以我在 long 中使用 int 来获得一个 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();
}

感谢大家的帮助!

【问题讨论】:

  • 不详细看这个,你确定字节序是正确的吗?
  • &0xFF 在那里做什么?
  • 鉴于您提出“它应该做什么?”的问题,我建议您退后一步,先问问自己是否应该首先使用它。网络非常适合查找代码的 sn-ps,但就我个人而言,我喜欢在准备将它们包含到我自己的工作中之前了解它们在做什么。
  • @PeteH 这不仅仅是我从互联网上找到的 sn-p,它已经在生产代码中使用了很长时间。 (鉴于我没有编写原始代码)但是由于在应用程序的开发中从 C 到 Java 的变化,我仍然需要将那段特定的代码转换为 Java。这就是为什么我知道 C 部分在这种情况下确实给出了正确的结果。

标签: java c++ checksum


【解决方案1】:

明显的错误是,在三个地方,您将输入字和最终校验和与0xff 进行与运算,丢失了高 24 位。据推测,您正在尝试将 long 值减少到 32 位,这需要与 0xffffffffL 进行 AND。在执行此操作之前,您还需要将 getInt() 的返回值转换为 long,否则您仍然会得到想要避免的符号扩展。

我的 Java 有点生疏了,但我相当肯定,只要你使用 &gt;&gt;&gt; 进行右移(就像你一样),坚持使用 int 就会得到正确的结果。

您还有一个错误,即您在未检查输入是否为空的情况下无条件读取前四个字节。

您还需要确保输入具有 4 个字节的倍数;通过检查长度,或者将其更改为使用 int[] 而不是像 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;
}

【讨论】:

  • 刚刚进行了所有这些更改,甚至尝试了您的代码(您的代码确实给了我类似于正确的结果),但结果是:旧校验和 = 4011644098,新校验和是 713196854 并且有由于某种原因仍然是负校验和。莫非要工作很久?
  • @user1897599:你会在 Java 中得到负校验和,因为 Java 没有无符号类型的概念。二进制值应该是正确的;如果您真的想要一个正值,那么我想您可以将long 用于返回值(return (long)sum &amp; 0xffffffffL;),但中间结果不需要它。如果您得到不同的阳性结果,那么数据或计算会有所不同。尝试使用调试器单步执行两者,看看差异在哪里。
【解决方案2】:

C++ 版本返回 sum 的 32 位,但您的 Java 版本返回 &amp; 0xFF,只剩下 8 位。

  1. 您使用&gt;&gt;&gt; 进行无符号移位,并且定义了所有其他操作,以便它们在有符号Java 类型中的结果等于对C++ 无符号类型的相同操作,但有符号位的解释除外。所以你实际上可以在这里使用int
  2. 如果你继续使用long,你需要使用&amp; 0xFFFFFFFF来获得32位。

【讨论】:

    【解决方案3】:

    在 Java 中,&gt;&gt;&gt; 执行无符号移位,这意味着它将位值 0 插入新移入的位,将负数变为正数。有符号移位&gt;&gt; 扩展了符号位,负值为 1,因此负数保持负数。

    要修复您的代码,至少在 & 操作中将 0xFF 替换为 0xFFFFFFFF。另外,我认为您每次分配给tmpsum 时可能都必须这样做,而不仅仅是在循环之后一次(不是100%肯定,必须通过代码查看是否保留了正确的位并且即使没有 ANDing,也没有额外的位潜入,所以安全总比抱歉好)。

    我还要在方法中添加第一件事:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-27
      • 1970-01-01
      • 1970-01-01
      • 2011-12-24
      相关资源
      最近更新 更多