Перевести CRC8 с C на Java

Я получил кусок кода на C, который вычисляет CRC8-значение массива байтов. Мне нужно перевести его на Java.

Вот код C:

CRC_POLYNOM = 0x9c;
CRC_PRESET = 0xFF;

unsigned int CRC = CRC_PRESET;
for (i = 0; i < Len; i++)
{
  crc ^= FRAME[i];
  for (j = 0; j < 8; j++)
  {
    if (crc & 0x01)
        crc = (crc >> 1) ^ CRC_POLYNOM;
    else
        crc = (crc >> 1);
  }
}

Что мне удалось сделать, так это Java:

public static long calculateCRC8(byte[] b, int len) {
  long crc = CRC_PRESET;
  for (int i = 0; i < len; i++) {
    crc ^= b[i];
    for (int j = 0; j < 8; j++) {
      if ((crc & 0x01) == 0)
        crc = (crc >> 1) ^ CRC_POLYNOM;
      else
        crc = crc >> 1;
    }
  }
return crc;
}

Для примера массива байтов:

byte[] b = new byte[] {1, 56, -23, 3, 0, 19, 0, 0, 2, 0, 3, 13, 8, -34, 7, 9, 42, 18, 26, -5, 54, 11, -94, -46, -128, 4, 48, 52, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 1, -32, -80, 0, 98, -5, 71, 0, 64, 0, 0, 0, 0, -116, 1, 104, 2};

код C возвращает 29, мой код Java возвращает 44. Что я сделал не так?

Я думаю, это из-за подписанных типов данных Java, так как я могу это исправить?


person Alexander Ciesielski    schedule 13.08.2014    source источник


Ответы (3)


if (crc & 0x01)

Это проверяет, установлен ли младший бит.

if ((crc & 0x01) == 0)

Это проверяет, очищен ли нижний бит.

Вы также должны использовать беззнаковый сдвиг вправо в коде Java (т. е. >>> вместо >> в двух местах) и маскировать результат с помощью 0xff перед его возвратом.

EDIT И, наконец, вам нужно изменить это:

crc ^= b[i];

к этому:

crc ^= b[i] & 0xff;

Однако вам действительно следует выбросить все это и найти метод, управляемый таблицей. Это в восемь раз быстрее, чем это.

EDIT 2 Версия, управляемая таблицами, переработана для реализации java.util.zip.Checksum:

public class CRC8 implements Checksum
{
    private final short init;
    private final short[]   crcTable = new short[256];
    private short   value;

    /**
     * Construct a CRC8 specifying the polynomial and initial value.
     * @param polynomial Polynomial, typically one of the POLYNOMIAL_* constants.
     * @param init Initial value, typically either 0xff or zero.
     */
    public CRC8(int polynomial, short init)
    {
        this.value = this.init = init;
        for (int dividend = 0; dividend < 256; dividend++)
        {
            int remainder = dividend ;//<< 8;
            for (int bit = 0; bit < 8; ++bit)
                if ((remainder & 0x01) != 0)
                    remainder = (remainder >>> 1) ^ polynomial;
                else
                    remainder >>>= 1;
            crcTable[dividend] = (short)remainder;
        }
    }

    @Override
    public void update(byte[] buffer, int offset, int len)
    {
        for (int i = 0; i < len; i++)
        {
            int data = buffer[offset+i] ^ value;
            value = (short)(crcTable[data & 0xff] ^ (value << 8));
        }
    }

    /**
     * Updates the current checksum with the specified array of bytes.
     * Equivalent to calling <code>update(buffer, 0, buffer.length)</code>.
     * @param buffer the byte array to update the checksum with
     */
    public void update(byte[] buffer)
    {
        update(buffer, 0, buffer.length);
    }

    @Override
    public void update(int b)
    {
        update(new byte[]{(byte)b}, 0, 1);
    }

    @Override
    public long getValue()
    {
        return value & 0xff;
    }

    @Override
    public void reset()
    {
        value = init;
    }

    public static void  main(String[] args)
    {
        final int   CRC_POLYNOM = 0x9C;
        final byte  CRC_INITIAL = (byte)0xFF;

        final byte[]    data = {1, 56, -23, 3, 0, 19, 0, 0, 2, 0, 3, 13, 8, -34, 7, 9, 42, 18, 26, -5, 54, 11, -94, -46, -128, 4, 48, 52, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 1, -32, -80, 0, 98, -5, 71, 0, 64, 0, 0, 0, 0, -116, 1, 104, 2};
        CRC8    crc8 = new CRC8(CRC_POLYNOM, CRC_INITIAL);
        crc8.update(data,0,data.length);
        System.out.println("Test successful:\t"+(crc8.getValue() == 29));
    }
}
person user207421    schedule 13.08.2014
comment
if ((crc & 0x01) == 0x01) crc = ((crc >> 1) & 0xff) ^ CRC_POLYNOM; else crc = (crc >> 1) & 0xff; } } return crc; Я замаскировал тройные сдвиги вправо с помощью 0xff. Теперь он возвращает 75. - person Alexander Ciesielski; 13.08.2014
comment
@AlexanderCiesielski Продолжайте смотреть, я опубликую код таблицы, но, вероятно, не сегодня, сейчас 22:30 моего времени. - person user207421; 13.08.2014
comment
замените 'update (int)' на: public void update (int b) { update (ByteBuffer.allocate (Integer.BYTES).putInt (b).array (), 0, Integer.BYTES); } - person dr_begemot; 13.10.2020

Ваш "^" на самом деле уже указывает стрелкой на ту часть, где ошибка

эквивалент

if (crc & 0x01)

в Java будет (поскольку java нуждается в логических выражениях в if)

if ((crc & 0x01) != 0)

or

if ((crc & 0x01) == 0x01)
person michael_s    schedule 13.08.2014
comment
Спасибо за ответ. Но теперь он возвращает 47 вместо 44. - person Alexander Ciesielski; 13.08.2014
comment
Со всеми исправлениями, которые я упомянул в своем ответе? Во всех четырех местах? И инициализируется ли «crc» CRC_PRESET в коде C? - person user207421; 13.08.2014
comment
Да, я думаю, это ››› вместо ››, как в одновременном ответе EJP. Семантика здесь другая, потому что в java нет беззнаковых типов, как в C. Иногда это также раздражает при приведении к меньшему типу. - person michael_s; 13.08.2014
comment
Получаю 75 сейчас, когда я переключился на ››› и замаскировал его с помощью 0xff - person Alexander Ciesielski; 13.08.2014
comment
@AlexanderCiesielski Если вы комментируете мой ответ, прокомментируйте мой ответ. - person user207421; 13.08.2014
comment
Хммм - хорошо - теперь мне 29. Я предполагаю, что одна информация отсутствовала: FRAME имеет тип unsigned char, верно? Таким образом, вместо crc ^= FRAME[i] у нас будет crc = (crc ^ FRAME[i]) & 0xFF - и тогда не имеет значения, используете ли вы >> или >>>... лучше всегда размещать весь необходимый код для компиляции программы! - person michael_s; 13.08.2014

Я создал полную/автономную программу C по вашему вопросу:

#include <stdio.h>

#define CRC_POLYNOM 0x9c
#define CRC_PRESET 0xFF

int main() {
    unsigned int crc = CRC_PRESET;
    unsigned char FRAME[] = {1, 56, -23, 3, 0, 19, 0, 0, 2, 0, 3, 13, 8, -34, 7, 9, 42, 18, 26, -5, 54, 11, -94, -46, -128, 4, 48, 52, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 1, -32, -80, 0, 98, -5, 71, 0, 64, 0, 0, 0, 0, -116, 1, 104, 2};
    for(int i = 0; i < sizeof(FRAME); i++) {
        crc ^= FRAME[i];
        for(int j = 0; j < 8; j++) {
            if(crc & 0x01) {
                crc = (crc >> 1) ^ CRC_POLYNOM;
            } else {
                crc = (crc >> 1);
            }
        }
    }
    printf("%u\n", crc);
    return 0;
}

Здесь эквивалентный код Java автоматически создается на https://www.mtsystems.com:

package demo;

public class DemoTranslation {
    public final static int CRC_POLYNOM = 0x9c;

    public final static int CRC_PRESET = 0xFF;

    public static void main(String[] args) {
        int crc_U = CRC_PRESET;
        byte[] frame_U = {1, 56, -23, 3, 0, 19, 0, 0, 2, 0, 3, 13, 8, -34, 7, 9, 42, 18, 26, -5, 54, 11, -94, -46, -128, 4, 48, 52, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 1, -32, -80, 0, 98, -5, 71, 0, 64, 0, 0, 0, 0, -116, 1, 104, 2};
        for(int i = 0; i < frame_U.length; i++) {
            crc_U ^= Byte.toUnsignedInt(frame_U[i]);
            for(int j = 0; j < 8; j++) {
                if((crc_U & 0x01) != 0) {
                    crc_U = (crc_U >>> 1) ^ CRC_POLYNOM;
                } else {
                    crc_U = (crc_U >>> 1);
                }
            }
        }
        System.out.println(Integer.toUnsignedString(crc_U));
    }
}
person user3669782    schedule 18.11.2014