unsigned long в Java, используя BigInteger для арифметики, но BigInteger.toByteArray возвращает 14 байтов вместо 8 - PullRequest
1 голос
/ 12 марта 2011

У меня есть следующий код c, который любит портировать на 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;     

    }

Я попытался перенести это на Java, используя long, однако это привело бы к отрицательным значениям. Поэтому я перешел на biginteger, так как мне нужно заниматься арифметикой (сдвигом битов и т. Д.).

Я получил желаемое 64-битное беззнаковое длинное значение, используя BigInteger, однако, когда я захотел преобразовать его в байт (BigInteger.toByteArray), его длина составляла 14 байт, а не 8, поэтому я не могу больше изменять свой массив / файл. Я пытался использовать toLongValue (), но данные были неверными.

Спасибо

Ответы [ 2 ]

2 голосов
/ 12 марта 2011

Ваш код C полагается на биты, сдвигаемые от старшего конца unsigned long long. (Они поворачиваются на другой конец другим сдвигом.) BigInteger имеет произвольную точность и, следовательно, не имеет конца, поэтому сдвинутые влево биты никогда не сдвигаются.

Вы можете создать 64-битную BigInteger поразрядную маску AND и AND после сдвига влево. Это интуитивное решение.

Вы также можете просто игнорировать старшие байты.

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

Если len большое, то это простое решение будет растрачивать память.

В любом случае есть более разумное и более эффективное решение. Все целочисленные типы Java со знаком гарантированно будут иметь семантику дополнения до двух. Битовая семантика для арифметики с двумя целыми числами дополнения и целыми числами без знака идентична - разница только в интерпретации значения! Так что просто используйте исходный код C (подстановка в Java long) и в конце интерпретируйте их по-своему .

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

Кстати, обязательно замените оператор >> в коде C на оператор Java >>>.

1 голос
/ 12 марта 2011

Приятной особенностью Java является то, что она гарантированно дополняет два, поэтому при условии, что вы используете >>> вместо >> и избегаете% и / и неравенств, арифметика в любом случае фактически не имеет знака.

...