bp7/
crc.rs

1use super::bundle::*;
2
3/******************************
4 *
5 * CRC
6 *
7 ******************************/
8
9pub type CrcRawType = u8;
10
11use crc::{Crc, CRC_16_IBM_SDLC, CRC_32_ISCSI};
12
13pub const X25: Crc<u16> = Crc::<u16>::new(&CRC_16_IBM_SDLC);
14pub const CASTAGNOLI: Crc<u32> = Crc::<u32>::new(&CRC_32_ISCSI);
15
16#[derive(Debug, Clone, PartialEq, Default)]
17pub enum CrcValue {
18    #[default]
19    CrcNo,
20    Crc16Empty,
21    Crc32Empty,
22    Crc16([u8; 2]),
23    Crc32([u8; 4]),
24    Unknown(CrcRawType),
25}
26
27impl CrcValue {
28    pub fn has_crc(&self) -> bool {
29        // TODO: handle unknown
30        *self != CrcValue::CrcNo
31    }
32    pub fn to_code(&self) -> CrcRawType {
33        match self {
34            CrcValue::CrcNo => CRC_NO,
35            CrcValue::Crc16(_) => CRC_16,
36            CrcValue::Crc16Empty => CRC_16,
37            CrcValue::Crc32(_) => CRC_32,
38            CrcValue::Crc32Empty => CRC_32,
39            CrcValue::Unknown(code) => *code,
40        }
41    }
42    pub fn bytes(&self) -> Option<&[u8]> {
43        match self {
44            CrcValue::Unknown(_) => None,
45            CrcValue::CrcNo => None,
46            CrcValue::Crc16(buf) => Some(buf),
47            CrcValue::Crc16Empty => Some(&CRC16_EMPTY),
48            CrcValue::Crc32(buf) => Some(buf),
49            CrcValue::Crc32Empty => Some(&CRC32_EMPTY),
50        }
51    }
52}
53pub(crate) const CRC16_EMPTY: [u8; 2] = [0; 2];
54pub(crate) const CRC32_EMPTY: [u8; 4] = [0; 4];
55
56pub const CRC_NO: CrcRawType = 0;
57pub const CRC_16: CrcRawType = 1;
58pub const CRC_32: CrcRawType = 2;
59
60pub trait CRCFuncations {
61    fn to_string(self) -> String;
62}
63impl CRCFuncations for CrcRawType {
64    fn to_string(self) -> String {
65        match self {
66            CRC_NO => String::from("no"),
67            CRC_16 => String::from("16"),
68            CRC_32 => String::from("32"),
69            _ => String::from("unknown"),
70        }
71    }
72}
73
74pub trait CrcBlock: Block + Clone {
75    /// Convert block struct to a serializable enum
76    fn has_crc(&self) -> bool {
77        self.crc_value().has_crc()
78    }
79    /// Recalculate crc value
80    fn update_crc(&mut self) {
81        let new_crc = calculate_crc(self);
82        self.set_crc(new_crc);
83    }
84    /// Check if crc value is valid
85    fn check_crc(&mut self) -> bool {
86        check_crc(self)
87    }
88    /// Reset crc field to an empty value
89    fn reset_crc(&mut self) {
90        if self.has_crc() {
91            match self.crc_value() {
92                CrcValue::Crc16(_) => self.set_crc(CrcValue::Crc16Empty),
93                CrcValue::Crc32(_) => self.set_crc(CrcValue::Crc32Empty),
94                _ => {}
95            }
96        }
97    }
98    /// Returns raw crc checksum
99    fn crc(&self) -> Option<&[u8]> {
100        self.crc_value().bytes()
101    }
102    /// Set crc type
103    /// CRC_NO, CRC_16, CRC_32
104    fn set_crc_type(&mut self, crc_value: CrcRawType) {
105        if crc_value == CRC_NO {
106            self.set_crc(CrcValue::CrcNo);
107        } else if crc_value == CRC_16 {
108            self.set_crc(CrcValue::Crc16Empty);
109        } else if crc_value == CRC_32 {
110            self.set_crc(CrcValue::Crc32Empty);
111        } else {
112            self.set_crc(CrcValue::Unknown(crc_value));
113        }
114    }
115    /// Return the crc type code
116    fn crc_type(&self) -> CrcRawType {
117        self.crc_value().to_code()
118    }
119    fn crc_value(&self) -> &CrcValue;
120    fn set_crc(&mut self, crc: CrcValue);
121}
122
123pub fn calculate_crc<T: CrcBlock + Block>(blck: &mut T) -> CrcValue {
124    match blck.crc_type() {
125        CRC_NO => CrcValue::CrcNo,
126        CRC_16 => {
127            let crc_bak = blck.crc_value().clone(); // Backup original crc
128            blck.reset_crc(); // set empty crc
129            let data = blck.to_cbor(); // TODO: optimize this encoding away
130                                       // also tried crc16 crate, not a bit faster
131            let chksm = X25.checksum(&data);
132            let output_crc = chksm.to_be_bytes();
133            blck.set_crc(crc_bak); // restore orginal crc
134            CrcValue::Crc16(output_crc)
135        }
136        CRC_32 => {
137            let crc_bak = blck.crc_value().clone(); // Backup original crc
138            blck.reset_crc(); // set empty crc
139            let data = blck.to_cbor(); // TODO: optimize this encoding away
140                                       // also tried crc32fast, was not significantly faster
141            let chksm = CASTAGNOLI.checksum(&data);
142            let output_crc = chksm.to_be_bytes();
143            blck.set_crc(crc_bak); // restore orginal crc
144            CrcValue::Crc32(output_crc)
145        }
146        _ => {
147            panic!("Unknown crc type");
148        }
149    }
150}
151pub fn check_crc<T: CrcBlock + Block>(blck: &mut T) -> bool {
152    if !blck.has_crc() {
153        return !blck.has_crc();
154    }
155    calculate_crc(blck).bytes() == blck.crc()
156}