简体   繁体   English

在 Vec 上计算 CRC16<u8>

[英]Calculating CRC16 on Vec<u8>

I'm sending and receiving raw binary data via the serial port, so I have a predefined message stored in a u8 vector.我通过串口发送和接收原始二进制数据,所以我有一个预定义的消息存储在一个u8向量中。 I need to calculate the 16bit CRC and append that onto the end before sending it, however I keep running into issues with casting and integer overflows.我需要计算 16 位 CRC 并在发送之前将其附加到末尾,但是我一直遇到强制转换和整数溢出的问题。 This is how I have previously done the calculation in C:这就是我之前在 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;
}

I tried to do something similar in rust, but first ran into some borrow checker issues, so I tried to simplify it and just write a function to calculate the crc and return the u16 result, without needing to mutate the vector:我尝试在 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
}

however I can't find a way to make this work.但是我找不到一种方法来完成这项工作。 The code posted above fails to compile because I can't perform a bitwise xor between a u8 and a u16 , however the data is treated as u8 s because its raw bytes, so that can't change.上面发布的代码无法编译,因为我无法在u8u16之间执行按位异或,但是数据被视为u8 s,因为它的原始字节,所以不能改变。 I could add the mut to the vector and make it mutable then cast to a u16 but that seems like a risky way of doing it, and I shouldn't need to mutate the vector to calculate the checksum:我可以将mut添加到向量并使其可变然后转换为u16但这似乎是一种冒险的方式,我不应该需要改变向量来计算校验和:

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`

What is the best way of implementing a similar function in rust?在 rust 中实现类似功能的最佳方法是什么? The rust compiler is great for catching integer type overflows, but unfortunately that's a key part of how the CRC works, hence why I allowed the overflowing literals, but this didn't seem to fix it. rust 编译器非常适合捕获整数类型溢出,但不幸的是,这是 CRC 工作原理的关键部分,因此我允许溢出的文字,但这似乎并没有解决它。 I had a look at some of the crates that mentioned CRC calculations but none of them offered what I wanted, plus its a fairly simple calculation so I'd rather use this as a learning exercise.我查看了一些提到 CRC 计算的 crate,但它们都没有提供我想要的东西,而且它的计算相当简单,所以我宁愿把它用作学习练习。

I didn't look at the details, just made it compile.我没有看细节,只是编译它。

First, Vec is not needed, any slice is suitable (even if it comes from a Vec ).首先,不需要Vec ,任何切片都是合适的(即使它来自Vec )。

byte is a reference to a u8 in this slice, thus *byte is a copy of this u8 (since u8 is Copy ), then we can cast it into a u16 . byte是对该切片中的u8的引用,因此*byte是此u8的副本(因为u8Copy ),然后我们可以将其转换为u16

crc is updated in the loop, so it needs the mut qualifier. crc在循环中更新,因此它需要mut限定符。

x is local to the loop, and is updated in several steps so it needs the mut qualifier too. x在循环中是本地的,并且在几个步骤中更新,因此它也需要mut限定符。

nb : in your C++ code there is (x << 12) with x declared as unsigned char . nb :在您的 C++ 代码中有(x << 12) ,其中x声明为unsigned char In C/C++ integer promotion takes place and this x is promoted to int before the shift.在 C/C++ 中进行整数提升,并且在移位之前将此x提升为int In Rust, if x is a u8 , this shift will give zero in any case (and Rust complains about that).在 Rust 中,如果x是一个u8 ,这种转变在任何情况下都会给出零(Rust 抱怨这一点)。 I let x be a u16 and cancel the upper byte, but I'm not certain this is what you want.我让x成为u16并取消高字节,但我不确定这是你想要的。

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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM