Я пытаюсь преобразовать эту контрольную сумму 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-битным, поэтому я использую int в long, чтобы получить неподписанное 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
теряя старшие 24 бита. Предположительно, вы пытаетесь уменьшить long
значение до 32 бит, что требует AND с 0xffffffffL
, Вам также необходимо преобразовать возвращаемое значение getInt()
в long
прежде чем сделать это, в противном случае вы все равно получите расширение знака, которое вы пытаетесь избежать.
Мой Java немного ржавый, но я уверен, что вы получите правильные результаты, если будете придерживаться int
, пока вы используете >>>
для сдвига вправо (как вы делаете).
У вас также есть ошибка, когда вы безоговорочно читаете первые четыре байта, не проверяя, что ввод не пустой.
Вам также нужно убедиться, что входные данные кратны 4 байтам; либо путем проверки длины, либо путем изменения его для работы с int[]
скорее, чем byte[]
как версия C делает. И, конечно, нет необходимости 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;
}
Версия C ++ возвращает 32 бита sum
, но ваша версия Java делает & 0xFF
, который только оставляет 8.
>>>
и все другие операции определены так, чтобы их результат в знаковом типе Java был равен той же операции над типом без знака C ++, за исключением интерпретации подписанного бита. Таким образом, вы можете использовать int
Вот.long
нужно использовать & 0xFFFFFFFF
получить 32 бита.В Java >>>
выполняет сдвиг без знака, то есть он вставит значение бита 0 в новые сдвинутые биты, превратив отрицательное число в положительное. Смена подписи >>
расширяет знаковый бит, который равен 1 для отрицательных значений, поэтому отрицательные числа остаются отрицательными.
Чтобы исправить свой код, хотя бы заменить 0xFF
с 0xFFFFFFFF
в вашем & операции. Кроме того, я думаю, что вам, возможно, придется делать это каждый раз, когда вы назначаете tmp
а также sum
не один раз после цикла (не уверен на 100%, придется пройти через код, чтобы увидеть, сохранятся ли правильные биты и не появятся ли дополнительные биты даже без ANDing, так что это лучше, чем потом сожалеть).
Я также добавил бы в качестве первого шага в методе:
if (data.length & 3 != 0)
throw new IllegalArgumentException("byte buffer size not multiple of 4");
Кроме того, я бы либо удалить size
аргумент, или на самом деле использовать его вместо data.length
(после проверки он действителен), если данные могут иметь больше байтов, чем вы хотите обработать.