计算 Vec<u8> 上的 CRC16

bir*_*d99 1 crc rust crc16

我通过串行端口发送和接收原始二进制数据,因此我在向量中存储了预定义的消息u8。我需要计算 16 位 CRC 并在发送之前将其附加到末尾,但是我不断遇到转换和整数溢出问题。这是我之前在 C 中完成计算的方式:

void Serial::AddCRC(void *data, int len, void *checksum)
{
    uint8_t *dataPtr = (uint8_t *)data;
    uint8_t *crcPtr = (uint8_t *)checksum;
    uint16_t crc = 0x0;
    unsigned char x;
    int byteCount = 0;

    while ((len--) > 0) {
        x = (unsigned char)(crc >> 8 ^ dataPtr[byteCount++]);
        x ^= (unsigned char)(x >> 4);
        crc = (uint16_t)((crc << 8) ^ (x << 12) ^ (x << 5) ^ x);
    }
    crcPtr[0] = crc >> 8;
    crcPtr[1] = crc &0x00ff;
}
Run Code Online (Sandbox Code Playgroud)

我尝试在 Rust 中做类似的事情,但首先遇到了一些借用检查器问题,所以我尝试简化它,只编写一个函数来计算 crc 并返回结果u16,而不需要改变向量:

#[allow(overflowing_literals)]
pub fn checksum(msg: &Vec<u8>) -> u16{
    if msg.len() == 0 {return 0}

    let crc: u16 = 0x0;
    let x: u16;
    for byte in msg.iter() {
        x = crc >> 8 ^ byte;
        x ^= x >> 4;
        crc = (crc << 8) ^ (x << 12) ^ (x << 5) ^ x;
    }
    crc
}
Run Code Online (Sandbox Code Playgroud)

但是我找不到一种方法来完成这项工作。上面发布的代码无法编译,因为我无法在 au8和 a之间执行按位异u16或,但是数据被视为u8s 因为它是原始字节,因此无法更改。我可以将 the 添加mut到向量中并使其可变,然后转换为 au16但这似乎是一种危险的做法,而且我不需要改变向量来计算校验和:

error[E0308]: mismatched types
  --> vips_interface/src\beacon_comms.rs:14:24
   |
14 |         x = crc >> 8 ^ byte;
   |                        ^^^^ expected `u16`, found `u8`

error[E0277]: no implementation for `u16 ^ &u8`
  --> vips_interface/src\beacon_comms.rs:14:22
   |
14 |         x = crc >> 8 ^ byte;
   |                      ^ no implementation for `u16 ^ &u8`
Run Code Online (Sandbox Code Playgroud)

在 Rust 中实现类似功能的最佳方法是什么?rust 编译器非常适合捕获整数类型溢出,但不幸的是,这是 CRC 工作方式的关键部分,因此我允许溢出文字,但这似乎并没有解决它。我查看了一些提到 CRC 计算的包,但没有一个提供我想要的,而且它是一个相当简单的计算,所以我宁愿将其用作学习练习。

pro*_*-fh 5

我没有看细节,只是编译了一下。

首先,Vec不需要,任何切片都适合(即使它来自 a Vec)。

byteu8是对该切片中a 的引用,因此*byte是 this 的副本u8(因为u8Copy),然后我们可以将其转换为 a u16

crc在循环中更新,因此需要限定符mut

x是循环本地的,并且在几个步骤中更新,因此它mut也需要限定符。

注意:在您的 C++ 代码中,有(x << 12)withx声明为unsigned char。在 C/C++ 中,会发生整数提升,并且x会提升到int移位之前。在 Rust 中,如果x是 a u8,则这种转变在任何情况下都会给出零(Rust 抱怨这一点)。我x设 au16并取消高字节,但我不确定这是否是您想要的。

pub fn checksum(msg: &[u8]) -> u16 {
    let mut crc: u16 = 0x0;
    for byte in msg.iter() {
        let mut x = ((crc >> 8) ^ (*byte as u16)) & 255;
        x ^= x >> 4;
        crc = (crc << 8) ^ (x << 12) ^ (x << 5) ^ x;
    }
    crc
}
Run Code Online (Sandbox Code Playgroud)