crc_any/
crc_u64.rs

1#[cfg(feature = "alloc")]
2use alloc::fmt::{self, Debug, Display, Formatter};
3#[cfg(feature = "alloc")]
4use alloc::vec::Vec;
5
6#[cfg(feature = "heapless")]
7use heapless::Vec as HeaplessVec;
8
9use crate::{constants::crc_u64::*, lookup_table::LookUpTable};
10
11#[allow(clippy::upper_case_acronyms)]
12/// This struct can help you compute a CRC-64 (or CRC-x where **x** is equal or less than `64`) value.
13pub struct CRCu64 {
14    by_table:        bool,
15    poly:            u64,
16    lookup_table:    LookUpTable<u64>,
17    sum:             u64,
18    pub(crate) bits: u8,
19    high_bit:        u64,
20    mask:            u64,
21    initial:         u64,
22    final_xor:       u64,
23    reflect:         bool,
24    reorder:         bool,
25}
26
27#[cfg(feature = "alloc")]
28impl Debug for CRCu64 {
29    #[inline]
30    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
31        if self.by_table {
32            debug_helper::impl_debug_for_struct!(CRCu64, f, self, let .lookup_table = self.lookup_table.as_ref(), (.sum, "0x{:016X}", self.sum), .bits, (.initial, "0x{:016X}", self.initial), (.final_xor, "0x{:016X}", self.final_xor), .reflect, .reorder);
33        } else {
34            debug_helper::impl_debug_for_struct!(CRCu64, f, self, (.poly, "0x{:016X}", self.poly), (.sum, "0x{:016X}", self.sum), .bits, (.initial, "0x{:016X}", self.initial), (.final_xor, "0x{:016X}", self.final_xor), .reflect, .reorder);
35        }
36    }
37}
38
39#[cfg(feature = "alloc")]
40impl Display for CRCu64 {
41    #[inline]
42    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
43        f.write_fmt(format_args!("0x{:01$X}", self.get_crc(), (self.bits as usize + 3) >> 2))
44    }
45}
46
47impl CRCu64 {
48    /// Create a `CRCu64` instance by providing the length of bits, expression, reflection, an initial value and a final xor value.
49    pub fn create_crc(poly: u64, bits: u8, initial: u64, final_xor: u64, reflect: bool) -> CRCu64 {
50        debug_assert!(bits <= 64 && bits > 0);
51
52        if bits % 8 == 0 {
53            let lookup_table = if reflect {
54                LookUpTable::Dynamic(Self::crc_reflect_table(poly))
55            } else {
56                LookUpTable::Dynamic(Self::crc_table(poly, bits))
57            };
58
59            Self::create_crc_with_exists_lookup_table(
60                lookup_table,
61                bits,
62                initial,
63                final_xor,
64                reflect,
65            )
66        } else {
67            Self::create(
68                false,
69                LookUpTable::Static(&[0u64; 256]),
70                poly,
71                bits,
72                initial,
73                final_xor,
74                reflect,
75            )
76        }
77    }
78
79    /// Create a `CRCu64` instance by providing an existing lookup table, the length of bits, expression, reflection, an initial value and a final xor value.
80    pub(crate) fn create_crc_with_exists_lookup_table(
81        lookup_table: LookUpTable<u64>,
82        bits: u8,
83        initial: u64,
84        final_xor: u64,
85        reflect: bool,
86    ) -> CRCu64 {
87        debug_assert!(bits % 8 == 0);
88
89        Self::create(true, lookup_table, 0, bits, initial, final_xor, reflect)
90    }
91
92    #[inline]
93    fn create(
94        by_table: bool,
95        lookup_table: LookUpTable<u64>,
96        mut poly: u64,
97        bits: u8,
98        initial: u64,
99        final_xor: u64,
100        reflect: bool,
101    ) -> CRCu64 {
102        let high_bit = 1 << u64::from(bits - 1);
103        let mask = ((high_bit - 1) << 1) | 1;
104
105        let sum = if reflect { Self::reflect_function(high_bit, initial) } else { initial };
106
107        if !by_table && reflect {
108            poly = Self::reflect_function(high_bit, poly);
109        }
110
111        CRCu64 {
112            by_table,
113            poly,
114            lookup_table,
115            sum,
116            bits,
117            high_bit,
118            mask,
119            initial,
120            final_xor,
121            reflect,
122            reorder: false,
123        }
124    }
125
126    #[inline]
127    fn reflect_function(high_bit: u64, n: u64) -> u64 {
128        let mut i = high_bit;
129        let mut j = 1;
130        let mut out = 0;
131
132        while i != 0 {
133            if n & i != 0 {
134                out |= j;
135            }
136
137            j <<= 1;
138            i >>= 1;
139        }
140
141        out
142    }
143
144    #[inline]
145    fn reflect_method(&self, n: u64) -> u64 {
146        Self::reflect_function(self.high_bit, n)
147    }
148
149    /// Digest some data.
150    pub fn digest<T: ?Sized + AsRef<[u8]>>(&mut self, data: &T) {
151        if self.by_table {
152            if self.bits == 8 {
153                for n in data.as_ref().iter().copied() {
154                    let index = (self.sum as u8 ^ n) as usize;
155                    self.sum = self.lookup_table[index];
156                }
157            } else if self.reflect {
158                for n in data.as_ref().iter().copied() {
159                    let index = ((self.sum as u8) ^ n) as usize;
160                    self.sum = (self.sum >> 8) ^ self.lookup_table[index];
161                }
162            } else {
163                for n in data.as_ref().iter().copied() {
164                    let index = ((self.sum >> u64::from(self.bits - 8)) as u8 ^ n) as usize;
165                    self.sum = (self.sum << 8) ^ self.lookup_table[index];
166                }
167            }
168        } else if self.reflect {
169            for n in data.as_ref().iter().copied() {
170                let n = super::crc_u8::CRCu8::reflect_function(0x80, n);
171
172                let mut i = 0x80;
173
174                while i != 0 {
175                    let mut bit = self.sum & self.high_bit;
176
177                    self.sum <<= 1;
178
179                    if n & i != 0 {
180                        bit ^= self.high_bit;
181                    }
182
183                    if bit != 0 {
184                        self.sum ^= self.poly;
185                    }
186
187                    i >>= 1;
188                }
189            }
190        } else {
191            for n in data.as_ref().iter().copied() {
192                let mut i = 0x80;
193
194                while i != 0 {
195                    let mut bit = self.sum & self.high_bit;
196
197                    self.sum <<= 1;
198
199                    if n & i != 0 {
200                        bit ^= self.high_bit;
201                    }
202
203                    if bit != 0 {
204                        self.sum ^= self.poly;
205                    }
206
207                    i >>= 1;
208                }
209            }
210        }
211    }
212
213    /// Reset the sum.
214    pub fn reset(&mut self) {
215        self.sum = self.initial;
216    }
217
218    /// Get the current CRC value (it always returns a `u64` value). You can continue calling `digest` method even after getting a CRC value.
219    pub fn get_crc(&self) -> u64 {
220        let sum = if self.by_table || !self.reflect {
221            (self.sum ^ self.final_xor) & self.mask
222        } else {
223            (self.reflect_method(self.sum) ^ self.final_xor) & self.mask
224        };
225
226        if self.reorder {
227            let mut new_sum = 0;
228
229            let e = (self.bits as u64 + 7) >> 3;
230
231            let e_dec = e - 1;
232
233            for i in 0..e {
234                new_sum |= ((sum >> ((e_dec - i) * 8)) & 0xFF) << (i * 8);
235            }
236
237            new_sum
238        } else {
239            sum
240        }
241    }
242
243    fn crc_reflect_table(poly_rev: u64) -> [u64; 256] {
244        let mut lookup_table = [0u64; 256];
245
246        for (i, e) in lookup_table.iter_mut().enumerate() {
247            let mut v = i as u64;
248
249            #[allow(clippy::branches_sharing_code)]
250            for _ in 0..8u8 {
251                if v & 1 != 0 {
252                    v >>= 1;
253                    v ^= poly_rev;
254                } else {
255                    v >>= 1;
256                }
257            }
258
259            *e = v;
260        }
261
262        lookup_table
263    }
264
265    fn crc_table(poly: u64, bits: u8) -> [u64; 256] {
266        let mut lookup_table = [0u64; 256];
267
268        let mask1 = 1u64 << u64::from(bits - 1);
269
270        let mask2 = ((mask1 - 1) << 1) | 1;
271
272        for (i, e) in lookup_table.iter_mut().enumerate() {
273            let mut v = i as u64;
274
275            #[allow(clippy::branches_sharing_code)]
276            for _ in 0..bits {
277                if v & mask1 == 0 {
278                    v <<= 1;
279                } else {
280                    v <<= 1;
281                    v ^= poly;
282                }
283            }
284
285            *e = v & mask2;
286        }
287
288        lookup_table
289    }
290}
291
292#[cfg(feature = "alloc")]
293impl CRCu64 {
294    /// Get the current CRC value (it always returns a vec instance with a length corresponding to the CRC bits). You can continue calling `digest` method even after getting a CRC value.
295    #[inline]
296    pub fn get_crc_vec_le(&mut self) -> Vec<u8> {
297        let crc = self.get_crc();
298
299        let e = (self.bits as usize + 7) >> 3;
300
301        crc.to_le_bytes()[..e].to_vec()
302    }
303
304    /// Get the current CRC value (it always returns a vec instance with a length corresponding to the CRC bits). You can continue calling `digest` method even after getting a CRC value.
305    #[inline]
306    pub fn get_crc_vec_be(&mut self) -> Vec<u8> {
307        let crc = self.get_crc();
308
309        let e = (self.bits as usize + 7) >> 3;
310
311        crc.to_be_bytes()[(8 - e)..].to_vec()
312    }
313}
314
315#[cfg(feature = "heapless")]
316impl CRCu64 {
317    /// Get the current CRC value (it always returns a heapless vec instance with a length corresponding to the CRC bits). You can continue calling `digest` method even after getting a CRC value.
318    #[inline]
319    pub fn get_crc_heapless_vec_le(&mut self) -> HeaplessVec<u8, 8> {
320        let crc = self.get_crc();
321
322        let e = (self.bits as usize + 7) >> 3;
323
324        let mut vec = HeaplessVec::new();
325
326        vec.extend_from_slice(&crc.to_le_bytes()[..e]).unwrap();
327
328        vec
329    }
330
331    /// Get the current CRC value (it always returns a heapless vec instance with a length corresponding to the CRC bits). You can continue calling `digest` method even after getting a CRC value.
332    #[inline]
333    pub fn get_crc_heapless_vec_be(&mut self) -> HeaplessVec<u8, 8> {
334        let crc = self.get_crc();
335
336        let e = (self.bits as usize + 7) >> 3;
337
338        let mut vec = HeaplessVec::new();
339
340        vec.extend_from_slice(&crc.to_be_bytes()[(8 - e)..]).unwrap();
341
342        vec
343    }
344}
345
346impl CRCu64 {
347    /// |Check|Poly|Init|Ref|XorOut|
348    /// |---|---|---|---|---|
349    /// |0xD4164FC646|0x0004820009|0x0000000000|false|0xFFFFFFFFFF|
350    ///
351    /// ```
352    /// # use crc_any::CRCu64;
353    /// let mut crc = CRCu64::crc40gsm();
354    /// crc.digest(b"123456789");
355    #[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0xD4164FC646\", &crc.to_string());")]
356    /// ```
357    pub fn crc40gsm() -> CRCu64 {
358        // Self::create_crc(0x0000000004820009u64, 40, 0x0000000000000000, 0x000000FFFFFFFFFF, false)
359
360        let lookup_table = LookUpTable::Static(&NO_REF_40_0000000004820009);
361        Self::create_crc_with_exists_lookup_table(
362            lookup_table,
363            40,
364            0x0000000000000000,
365            0x000000FFFFFFFFFF,
366            false,
367        )
368    }
369
370    /// |Check|Poly|Init|Ref|XorOut|
371    /// |---|---|---|---|---|
372    /// |0x6C40DF5F0B497347|0x42F0E1EBA9EA3693|0x0000000000000000|false|0x0000000000000000|
373    ///
374    /// ```
375    /// # use crc_any::CRCu64;
376    /// let mut crc = CRCu64::crc64();
377    /// crc.digest(b"123456789");
378    #[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0x6C40DF5F0B497347\", &crc.to_string());")]
379    /// ```
380    pub fn crc64() -> CRCu64 {
381        // Self::create_crc(0x42F0E1EBA9EA3693, 64, 0x0000000000000000, 0x0000000000000000, false)
382
383        let lookup_table = LookUpTable::Static(&NO_REF_64_42F0E1EBA9EA3693);
384        Self::create_crc_with_exists_lookup_table(
385            lookup_table,
386            64,
387            0x0000000000000000,
388            0x0000000000000000,
389            false,
390        )
391    }
392
393    /// |Check|Poly|Init|Ref|XorOut|
394    /// |---|---|---|---|---|
395    /// |0xB90956C775A41001|0x000000000000001B (rev: 0xD800000000000000)|0xFFFFFFFFFFFFFFFF|true|0xFFFFFFFFFFFFFFFF|
396    ///
397    /// ```
398    /// # use crc_any::CRCu64;
399    /// let mut crc = CRCu64::crc64iso();
400    /// crc.digest(b"123456789");
401    #[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0xB90956C775A41001\", &crc.to_string());")]
402    /// ```
403    pub fn crc64iso() -> CRCu64 {
404        // Self::create_crc(0xD800000000000000, 64, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, true)
405
406        let lookup_table = LookUpTable::Static(&REF_64_D800000000000000);
407        Self::create_crc_with_exists_lookup_table(
408            lookup_table,
409            64,
410            0xFFFFFFFFFFFFFFFF,
411            0xFFFFFFFFFFFFFFFF,
412            true,
413        )
414    }
415
416    /// |Check|Poly|Init|Ref|XorOut|
417    /// |---|---|---|---|---|
418    /// |0x62EC59E3F1A4F00A|0x42F0E1EBA9EA3693|0xFFFFFFFFFFFFFFFF|false|0xFFFFFFFFFFFFFFFF|
419    ///
420    /// ```
421    /// # use crc_any::CRCu64;
422    /// let mut crc = CRCu64::crc64we();
423    /// crc.digest(b"123456789");
424    #[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0x62EC59E3F1A4F00A\", &crc.to_string());")]
425    /// ```
426    pub fn crc64we() -> CRCu64 {
427        // Self::create_crc(0x42F0E1EBA9EA3693, 64, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, false)
428
429        let lookup_table = LookUpTable::Static(&NO_REF_64_42F0E1EBA9EA3693);
430        Self::create_crc_with_exists_lookup_table(
431            lookup_table,
432            64,
433            0xFFFFFFFFFFFFFFFF,
434            0xFFFFFFFFFFFFFFFF,
435            false,
436        )
437    }
438
439    /// |Check|Poly|Init|Ref|XorOut|
440    /// |---|---|---|---|---|
441    /// |0xE9C6D914C4B8D9CA|0xAD93D23594C935A9 (rev: 0x95AC9329AC4BC9B5)|0x0000000000000000|true|0x0000000000000000|
442    ///
443    /// ```
444    /// # use crc_any::CRCu64;
445    /// let mut crc = CRCu64::crc64jones();
446    /// crc.digest(b"123456789");
447    #[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0xE9C6D914C4B8D9CA\", &crc.to_string());")]
448    /// ```
449    pub fn crc64jones() -> CRCu64 {
450        // Self::create_crc(0x95AC9329AC4BC9B5, 64, 0x0000000000000000, 0x0000000000000000, true)
451
452        let lookup_table = LookUpTable::Static(&REF_64_95AC9329AC4BC9B5);
453        Self::create_crc_with_exists_lookup_table(
454            lookup_table,
455            64,
456            0x0000000000000000,
457            0x0000000000000000,
458            true,
459        )
460    }
461}
462
463#[cfg(all(feature = "development", test))]
464mod tests {
465    use alloc::{fmt::Write, string::String};
466
467    use super::CRCu64;
468
469    #[test]
470    fn print_lookup_table() {
471        let crc = CRCu64::crc64jones();
472
473        let mut s = String::new();
474
475        for n in crc.lookup_table.iter().take(255) {
476            s.write_fmt(format_args!("{}u64, ", n)).unwrap();
477        }
478
479        s.write_fmt(format_args!("{}u64", crc.lookup_table[255])).unwrap();
480
481        println!("let lookup_table = [{}];", s);
482    }
483}