1mod rev;
2use super::{
3 CheckBuilderErr, Digest, LinearCheck,
4 endian::{Endian, WordSpec},
5};
6use crate::{bitnum::BitNum, checksum::parse_hex, endian::Signedness};
7use crate::{checksum::Checksum, endian::SignedInt, keyval::KeyValIter};
8pub use rev::reverse_crc;
9#[cfg(feature = "parallel")]
10pub use rev::reverse_crc_para;
11use std::fmt::Display;
12use std::str::FromStr;
13#[derive(Clone, Debug)]
30pub struct CrcBuilder<Sum: BitNum> {
31 width: Option<usize>,
32 poly: Option<Sum>,
33 init: Option<Sum>,
34 xorout: Option<Sum>,
35 refin: Option<bool>,
36 refout: Option<bool>,
37 input_endian: Option<Endian>,
38 output_endian: Option<Endian>,
39 wordsize: Option<usize>,
40 check: Option<Sum>,
41 name: Option<String>,
42}
43
44impl<Sum: BitNum> CrcBuilder<Sum> {
45 pub fn poly(&mut self, p: Sum) -> &mut Self {
47 self.poly = Some(p);
48 self
49 }
50 pub fn width(&mut self, w: usize) -> &mut Self {
52 self.width = Some(w);
53 self
54 }
55 pub fn init(&mut self, s: Sum) -> &mut Self {
57 self.init = Some(s);
58 self
59 }
60 pub fn xorout(&mut self, s: Sum) -> &mut Self {
62 self.xorout = Some(s);
63 self
64 }
65 pub fn refin(&mut self, i: bool) -> &mut Self {
67 self.refin = Some(i);
68 self
69 }
70 pub fn refout(&mut self, o: bool) -> &mut Self {
72 self.refout = Some(o);
73 self
74 }
75 pub fn inendian(&mut self, e: Endian) -> &mut Self {
77 self.input_endian = Some(e);
78 self
79 }
80 pub fn wordsize(&mut self, n: usize) -> &mut Self {
82 self.wordsize = Some(n);
83 self
84 }
85 pub fn outendian(&mut self, e: Endian) -> &mut Self {
87 self.output_endian = Some(e);
88 self
89 }
90 pub fn check(&mut self, c: Sum) -> &mut Self {
92 self.check = Some(c);
93 self
94 }
95 pub fn name(&mut self, s: &str) -> &mut Self {
97 self.name = Some(s.to_owned());
98 self
99 }
100 pub fn build(&self) -> Result<CRC<Sum>, CheckBuilderErr> {
103 let width = match self.width {
104 None => return Err(CheckBuilderErr::MissingParameter("width")),
105 Some(w) => w,
106 };
107 if width > 128 {
108 return Err(CheckBuilderErr::ValueOutOfRange("width"));
109 }
110 let mask = Sum::one() << (width - 1);
111 let mask = mask ^ (mask - Sum::one());
112 let poly = match self.poly {
113 None => return Err(CheckBuilderErr::MissingParameter("poly")),
114 Some(p) => p,
115 };
116 let init = self.init.unwrap_or_else(Sum::zero);
117 let xorout = self.xorout.unwrap_or_else(Sum::zero);
118 let refout = self.refout.unwrap_or(false);
119 if poly.bits() < width || poly.bits() < 8 {
122 return Err(CheckBuilderErr::ValueOutOfRange("width"));
123 }
124 if poly & Sum::one() != Sum::one() {
126 return Err(CheckBuilderErr::ValueOutOfRange("poly"));
127 }
128 if poly & !mask != Sum::zero() {
129 return Err(CheckBuilderErr::ValueOutOfRange("poly"));
130 }
131 if init & !mask != Sum::zero() {
132 return Err(CheckBuilderErr::ValueOutOfRange("init"));
133 }
134 if xorout & !mask != Sum::zero() {
135 return Err(CheckBuilderErr::ValueOutOfRange("xorout"));
136 }
137 let wordsize = self.wordsize.unwrap_or(8);
138 if wordsize == 0 || wordsize % 8 != 0 || wordsize > 64 {
139 return Err(CheckBuilderErr::ValueOutOfRange("wordsize"));
140 }
141 let refin = self.refin.unwrap_or(false);
142 let input_endian = self.input_endian.unwrap_or(Endian::Big);
143 let wordspec = WordSpec {
144 input_endian,
145 wordsize,
146 output_endian: self.output_endian.unwrap_or(Endian::Big),
147 signedness: Signedness::Unsigned,
148 };
149 let crc = CRC {
150 width,
151 poly,
152 init,
153 xorout,
154 refin,
155 refout,
156 wordspec,
157 mask,
158 name: self.name.clone(),
159 table: CRC::<Sum>::generate_crc_table(poly, width),
160 };
161 match self.check {
162 Some(chk) => {
163 if chk & !mask != Sum::zero() {
164 Err(CheckBuilderErr::ValueOutOfRange("check"))
165 } else if crc.digest(&b"123456789"[..]).unwrap() != chk {
166 Err(CheckBuilderErr::CheckFail)
167 } else {
168 Ok(crc)
169 }
170 }
171 None => Ok(crc),
172 }
173 }
174}
175
176#[derive(PartialEq, Eq)]
177pub struct CRC<Sum: BitNum> {
178 init: Sum,
179 xorout: Sum,
180 refin: bool,
181 refout: bool,
182 poly: Sum,
183 wordspec: WordSpec,
184 mask: Sum,
185 width: usize,
186 name: Option<String>,
187 table: Box<[Sum; 256]>,
188}
189
190impl<Sum: BitNum> Display for CRC<Sum> {
191 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
192 match &self.name {
193 Some(n) => write!(f, "{}", n),
194 None => {
195 write!(
196 f,
197 "crc width={} poly={:#x} init={:#x} xorout={:#x} refin={} refout={}",
198 self.width, self.poly, self.init, self.xorout, self.refin, self.refout
199 )?;
200 if self.wordspec.word_bytes() != 1 || self.wordspec.input_endian != Endian::Big {
201 write!(
202 f,
203 " wordsize={} in_endian={}",
204 self.wordspec.wordsize, self.wordspec.input_endian
205 )?;
206 };
207 if self.width > 8 {
208 write!(f, " out_endian={}", self.wordspec.output_endian)?;
209 };
210 Ok(())
211 }
212 }
213 }
214}
215
216impl<Sum: BitNum> CRC<Sum> {
217 pub fn with_options() -> CrcBuilder<Sum> {
219 CrcBuilder {
220 poly: None,
221 init: None,
222 xorout: None,
223 width: None,
224 refin: None,
225 refout: None,
226 input_endian: None,
227 output_endian: None,
228 wordsize: None,
229 check: None,
230 name: None,
231 }
232 }
233 fn generate_crc_table(generator: Sum, width: usize) -> Box<[Sum; 256]> {
236 let mut table = Box::new([Sum::zero(); 256]);
237 let mut crc = Sum::one() << (width - 1);
238 for i in 0..8 {
239 let addition = if Sum::zero() != crc & Sum::one() << (width - 1) {
240 crc = crc ^ Sum::one() << (width - 1);
242 generator
243 } else {
244 Sum::zero()
245 };
246 crc = (crc << 1usize) ^ addition;
247 let validrange = 1usize << i;
248 for x in 0..validrange {
249 table[validrange + x] = table[x] ^ crc;
250 }
251 }
252 table
253 }
254 fn get_table_entry(&self, x: Sum) -> Sum {
256 let index: u8 = match x.try_into() {
257 Ok(byte) => byte,
258 Err(_) => panic!("Internal error: non-byte index into CRC lookup table"),
259 };
260 self.table[index as usize]
261 }
262 fn regularize(&self, sum: Sum) -> Sum {
264 if self.refout {
265 sum.revbits() >> (sum.bits() - self.width)
266 } else {
267 sum
268 }
269 }
270}
271impl<Sum: BitNum> FromStr for CrcBuilder<Sum> {
272 fn from_str(s: &str) -> Result<CrcBuilder<Sum>, CheckBuilderErr> {
274 let mut crc = CRC::<Sum>::with_options();
275 for x in KeyValIter::new(s) {
276 let (current_key, current_val) = match x {
277 Err(key) => return Err(CheckBuilderErr::MalformedString(key)),
278 Ok(s) => s,
279 };
280 let crc_op = match current_key.as_str() {
281 "width" => usize::from_str(¤t_val).ok().map(|x| crc.width(x)),
284 "poly" => Some(crc.poly(parse_hex::<Sum>(¤t_val, "poly")?)),
285 "init" => Some(crc.init(parse_hex::<Sum>(¤t_val, "init")?)),
286 "xorout" => Some(crc.xorout(parse_hex::<Sum>(¤t_val, "xorout")?)),
287 "refin" => bool::from_str(¤t_val).ok().map(|x| crc.refin(x)),
288 "refout" => bool::from_str(¤t_val).ok().map(|x| crc.refout(x)),
289 "in_endian" => Endian::from_str(¤t_val).ok().map(|x| crc.inendian(x)),
290 "wordsize" => usize::from_str(¤t_val).ok().map(|x| crc.wordsize(x)),
291 "out_endian" => Endian::from_str(¤t_val)
292 .ok()
293 .map(|x| crc.outendian(x)),
294 "residue" => Some(&mut crc),
295 "check" => Some(crc.check(parse_hex::<Sum>(¤t_val, "check")?)),
296 "name" => Some(crc.name(¤t_val)),
297 _ => return Err(CheckBuilderErr::UnknownKey(current_key)),
298 };
299 match crc_op {
300 Some(c) => crc = c.clone(),
301 None => return Err(CheckBuilderErr::MalformedString(current_key)),
302 }
303 }
304 Ok(crc)
305 }
306 type Err = CheckBuilderErr;
307}
308
309impl<Sum: BitNum> FromStr for CRC<Sum> {
310 fn from_str(s: &str) -> Result<CRC<Sum>, CheckBuilderErr> {
318 CrcBuilder::<Sum>::from_str(s)?.build()
319 }
320 type Err = CheckBuilderErr;
321}
322
323impl<S: BitNum> Digest for CRC<S> {
324 type Sum = S;
325 fn init(&self) -> Self::Sum {
326 self.regularize(self.init)
329 }
330 fn dig_word(&self, sum: Self::Sum, word: SignedInt<u64>) -> Self::Sum {
331 let mut refsum = self.regularize(sum);
334 let inword = if self.refin {
335 word.value.reverse_bits() >> (64 - self.wordspec.wordsize)
336 } else {
337 word.value
338 };
339 for x in (0..self.wordspec.word_bytes()).rev() {
340 let inbyte = (inword >> (x * 8)) as u8;
341 refsum = if self.width <= 8 {
342 let overhang = (refsum << (8 - self.width)) ^ S::from(inbyte);
344 self.get_table_entry(overhang)
345 } else {
346 let overhang = refsum >> (self.width - 8) ^ S::from(inbyte);
348 let l_remain = (refsum << 8) & self.mask;
349 self.get_table_entry(overhang) ^ l_remain
350 }
351 }
352 self.regularize(refsum)
353 }
354 fn finalize(&self, sum: Self::Sum) -> Self::Sum {
355 sum ^ self.xorout
356 }
357
358 fn to_bytes(&self, s: Self::Sum) -> Vec<u8> {
359 self.wordspec.output_to_bytes(s, self.width)
360 }
361
362 fn checksum_from_bytes(&self, bytes: &[u8]) -> Option<Self::Sum> {
363 Checksum::from_bytes(bytes, self.wordspec.output_endian, self.width)
364 }
365
366 fn wordspec(&self) -> WordSpec {
367 self.wordspec
368 }
369}
370
371impl<S: BitNum> LinearCheck for CRC<S> {
372 type Shift = S;
373 fn shift(&self, sum: Self::Sum, shift: &Self::Shift) -> Self::Sum {
374 let sum = self.regularize(sum);
375 let mut lo_part = if *shift & Self::Shift::one() != Self::Shift::zero() {
378 sum
379 } else {
380 Self::Sum::zero()
381 };
382 let mut hi_part = Self::Sum::zero();
383 for i in 1..self.width {
384 if *shift >> i & Self::Shift::one() != Self::Shift::zero() {
385 lo_part = lo_part ^ sum << i;
386 hi_part = hi_part ^ sum >> (self.width - i);
387 }
388 }
389 lo_part = lo_part & self.mask;
390
391 let mut bits_left = self.width;
393 while bits_left > 0 {
394 let shift_amount = bits_left.min(8);
395 bits_left -= shift_amount;
396 let new_part = self.get_table_entry(hi_part >> (self.width - shift_amount));
397 if shift_amount >= hi_part.bits() {
399 hi_part = new_part
400 } else {
401 hi_part = (hi_part << shift_amount) & self.mask ^ new_part;
402 }
403 }
404 self.regularize(hi_part ^ lo_part)
405 }
406 fn add(&self, sum_a: Self::Sum, sum_b: &Self::Sum) -> Self::Sum {
407 sum_a ^ *sum_b
408 }
409 fn negate(&self, sum: Self::Sum) -> Self::Sum {
410 sum
412 }
413 fn init_shift(&self) -> Self::Shift {
414 Self::Shift::one()
415 }
416 fn inc_shift(&self, mut shift: Self::Shift) -> Self::Shift {
417 for _ in 0..self.wordspec.word_bytes() {
419 shift = if self.width <= 8 {
420 let overhang = shift << (8 - self.width);
421 self.get_table_entry(overhang)
422 } else {
423 let overhang = shift >> (self.width - 8);
424 let l_remain = (shift << 8) & self.mask;
425 self.get_table_entry(overhang) ^ l_remain
426 }
427 }
428 shift
429 }
430}
431
432#[cfg(test)]
433mod tests {
434 use super::*;
435 use crate::checksum::tests::{check_example, test_find, test_prop, test_shifts};
436 #[test]
437 fn cms_16() {
438 assert!(
439 CRC::<u32>::with_options()
440 .poly(0x8005)
441 .width(16)
442 .init(0xffff)
443 .check(0xaee7)
444 .build()
445 .is_ok()
446 );
447 let crc = CRC::<u16>::with_options()
448 .poly(0x8005)
449 .width(16)
450 .init(0xffff)
451 .check(0xaee7)
452 .build()
453 .unwrap();
454 test_shifts(&crc);
455 test_find(&crc);
456 test_prop(&crc);
457 check_example(&crc, 0x6bd6);
458 }
459 #[test]
460 fn gsm_3() {
461 let crc = CRC::<u8>::with_options()
462 .poly(0x3)
463 .width(3)
464 .xorout(0x7)
465 .check(0x4)
466 .build()
467 .unwrap();
468 test_shifts(&crc);
469 test_prop(&crc);
470 check_example(&crc, 7);
471 let crc = CRC::<u128>::with_options()
472 .poly(0x3)
473 .width(3)
474 .xorout(0x7)
475 .check(0x4)
476 .build()
477 .unwrap();
478 test_shifts(&crc);
479 test_prop(&crc);
480 check_example(&crc, 7);
481 }
482 #[test]
483 fn rohc_7() {
484 let crc = CRC::<u8>::with_options()
485 .poly(0x4f)
486 .width(7)
487 .init(0x7f)
488 .refin(true)
489 .refout(true)
490 .check(0x53)
491 .build()
492 .unwrap();
493 test_shifts(&crc);
494 test_prop(&crc);
495 check_example(&crc, 0x25);
496 let crc = CRC::<u32>::with_options()
497 .poly(0x4f)
498 .width(7)
499 .init(0x7f)
500 .refin(true)
501 .refout(true)
502 .check(0x53)
503 .build()
504 .unwrap();
505 test_shifts(&crc);
506 test_prop(&crc);
507 check_example(&crc, 0x25);
508 }
509 #[test]
510 fn usb_5() {
511 let crc = CRC::<u8>::with_options()
512 .poly(0x05)
513 .width(5)
514 .init(0x1f)
515 .refin(true)
516 .refout(true)
517 .xorout(0x1f)
518 .check(0x19)
519 .build()
520 .unwrap();
521 test_shifts(&crc);
522 test_prop(&crc);
523 check_example(&crc, 0x17);
524 }
525 #[test]
526 fn umts_12() {
527 let crc = CRC::<u16>::with_options()
528 .poly(0x80f)
529 .width(12)
530 .refout(true)
531 .check(0xdaf)
532 .build()
533 .unwrap();
534 test_shifts(&crc);
535 test_find(&crc);
536 test_prop(&crc);
537 check_example(&crc, 0x35a);
538 }
539 #[test]
540 fn en13757_16() {
541 let crc = CRC::<u16>::with_options()
542 .poly(0x3d65)
543 .width(16)
544 .xorout(0xffff)
545 .check(0xc2b7)
546 .build()
547 .unwrap();
548 test_shifts(&crc);
549 test_find(&crc);
550 test_prop(&crc);
551 check_example(&crc, 0x69e2);
552 }
553 #[test]
554 fn mpt1327_15() {
555 let crc = CRC::<u16>::with_options()
556 .poly(0x6815)
557 .width(15)
558 .xorout(0x0001)
559 .check(0x2566)
560 .build()
561 .unwrap();
562 test_shifts(&crc);
563 test_find(&crc);
564 test_prop(&crc);
565 check_example(&crc, 0x1993);
566 }
567 #[test]
568 fn canfd_17() {
569 let crc = CRC::<u32>::with_options()
570 .poly(0x1685b)
571 .width(17)
572 .check(0x04f03)
573 .build()
574 .unwrap();
575 test_shifts(&crc);
576 test_find(&crc);
577 test_prop(&crc);
578 check_example(&crc, 0x00f396);
579 }
580 #[test]
581 fn bzip2_32() {
582 let crc = CRC::<u32>::with_options()
583 .poly(0x04c11db7)
584 .width(32)
585 .init(0xffffffff)
586 .xorout(0xffffffff)
587 .check(0xfc891918)
588 .build()
589 .unwrap();
590 test_shifts(&crc);
591 test_find(&crc);
592 test_prop(&crc);
593 check_example(&crc, 0xe8c5033d);
594 }
595 #[test]
596 fn iscsi_32() {
597 let crc = CRC::<u32>::with_options()
598 .poly(0x1edc6f41)
599 .width(32)
600 .init(0xffffffff)
601 .xorout(0xffffffff)
602 .refin(true)
603 .refout(true)
604 .check(0xe3069283)
605 .build()
606 .unwrap();
607 test_shifts(&crc);
608 test_find(&crc);
609 test_prop(&crc);
610 check_example(&crc, 0x5a513507);
611 }
612 #[test]
613 fn gsm_40() {
614 let crc = CRC::<u64>::with_options()
615 .poly(0x0004820009)
616 .width(40)
617 .xorout(0xffffffffff)
618 .check(0xd4164fc646)
619 .build()
620 .unwrap();
621 test_shifts(&crc);
622 test_find(&crc);
623 test_prop(&crc);
624 check_example(&crc, 0x4165335176)
625 }
626 #[test]
627 fn xz_64() {
628 let crc = CRC::<u64>::with_options()
629 .poly(0x42f0e1eba9ea3693)
630 .width(64)
631 .init(0xffffffffffffffff)
632 .refin(true)
633 .refout(true)
634 .xorout(0xffffffffffffffff)
635 .check(0x995dc9bbdf1939fa)
636 .build()
637 .unwrap();
638 test_shifts(&crc);
639 test_find(&crc);
640 test_prop(&crc);
641 check_example(&crc, 0xb03d0f148fcab729);
642 }
643 #[test]
644 fn darc_82() {
645 let crc = CRC::<u128>::with_options()
646 .poly(0x0308c0111011401440411)
647 .width(82)
648 .refin(true)
649 .refout(true)
650 .check(0x09ea83f625023801fd612)
651 .build()
652 .unwrap();
653 test_shifts(&crc);
654 test_find(&crc);
655 test_prop(&crc);
656 check_example(&crc, 0x030c57c0142280dfd62847)
657 }
658 #[test]
659 fn parity_1() {
660 let crc = CRC::<u8>::with_options()
661 .poly(1)
662 .width(1)
663 .check(1)
664 .build()
665 .unwrap();
666 test_shifts(&crc);
667 test_prop(&crc);
668 check_example(&crc, 0);
669 }
670 #[test]
671 fn i4321_8() {
672 let crc = CRC::<u8>::with_options()
673 .poly(0x7)
674 .width(8)
675 .xorout(0x55)
676 .check(0xa1)
677 .build()
678 .unwrap();
679 test_shifts(&crc);
680 test_prop(&crc);
681 check_example(&crc, 0x96);
682 }
683 #[test]
684 fn isoiec144433a_16() {
685 let crc = CRC::<u16>::with_options()
686 .poly(0x1021)
687 .width(16)
688 .init(0xc6c6u16)
689 .refin(true)
690 .refout(true)
691 .check(0xbf05)
692 .build()
693 .unwrap();
694 test_shifts(&crc);
695 test_find(&crc);
696 test_prop(&crc);
697 check_example(&crc, 0x6b68);
698 }
699 #[test]
700 fn arc_16() {
701 let crc = CRC::<u16>::from_str("width=16 poly=0x8005 init=0x0000 refin=true refout=true xorout=0x0000 check=0xbb3d residue=0x0000 name=\"CRC-16/ARC\"")
702 .unwrap();
703 test_shifts(&crc);
704 test_find(&crc);
705 test_prop(&crc);
706 check_example(&crc, 0xf15e);
707 }
708 #[test]
709 fn something_16() {
710 let crc = CRC::<u16>::from_str("init=0x5ff\npoly=0x4465 width=15").unwrap();
711 test_shifts(&crc);
712 test_find(&crc);
713 test_prop(&crc);
714 check_example(&crc, 0x2cfa);
715 assert!(CRC::<u16>::from_str("init=0x5ff\npoly=0x4465 width=\"15").is_err());
716 CRC::<u16>::from_str(" init=0x533\n\t\npoly=0x4465 width=\"15\" ").unwrap();
717 }
718}