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)]
12pub 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 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 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 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 pub fn reset(&mut self) {
215 self.sum = self.initial;
216 }
217
218 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 #[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 #[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 #[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 #[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 #[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0xD4164FC646\", &crc.to_string());")]
356 pub fn crc40gsm() -> CRCu64 {
358 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 #[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0x6C40DF5F0B497347\", &crc.to_string());")]
379 pub fn crc64() -> CRCu64 {
381 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 #[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0xB90956C775A41001\", &crc.to_string());")]
402 pub fn crc64iso() -> CRCu64 {
404 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 #[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0x62EC59E3F1A4F00A\", &crc.to_string());")]
425 pub fn crc64we() -> CRCu64 {
427 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 #[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0xE9C6D914C4B8D9CA\", &crc.to_string());")]
448 pub fn crc64jones() -> CRCu64 {
450 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}