crc_fast/structs.rs
1// Copyright 2025 Don MacAskill. Licensed under MIT or Apache-2.0.
2
3#![allow(dead_code)]
4
5use crate::traits::{CrcCalculator, CrcWidth};
6use crate::{arch, cache, CrcAlgorithm, CrcParams};
7
8/// CRC algorithm parameters matching the CRC catalogue specification.
9///
10/// This struct describes a CRC algorithm using the fields specified by the
11/// [Catalogue of parametrised CRC algorithms](https://reveng.sourceforge.io/crc-catalogue/all.htm).
12#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13pub struct Algorithm<W> {
14 /// The number of bit cells in the linear feedback shift register; the degree of the generator
15 /// polynomial, minus one.
16 pub width: u8,
17 /// The generator polynomial that sets the feedback tap positions of the shift register.
18 pub poly: W,
19 /// The settings of the bit cells at the start of each calculation, before reading the first
20 /// message bit.
21 pub init: W,
22 /// If `true`, characters are read bit-by-bit, least significant bit (LSB) first;
23 /// if `false`, most significant bit (MSB) first.
24 pub refin: bool,
25 /// If `true`, the contents of the register after reading the last message bit are reflected
26 /// before presentation; if `false`, they are unreflected.
27 pub refout: bool,
28 /// The XOR value applied to the contents of the register after the last message bit has been
29 /// read and after the optional reflection.
30 pub xorout: W,
31 /// The contents of the register after initialising, reading the UTF-8 string `"123456789"`,
32 /// optionally reflecting, and applying the final XOR.
33 pub check: W,
34 /// The contents of the register after initialising, reading an error-free codeword and
35 /// optionally reflecting the register, but not applying the final XOR.
36 pub residue: W,
37}
38
39/// CRC-16 width implementation
40#[derive(Clone, Copy)]
41pub struct Width16;
42
43impl CrcWidth for Width16 {
44 const WIDTH: u32 = 16;
45 type Value = u16;
46}
47
48/// CRC-32 width implementation
49#[derive(Clone, Copy)]
50pub struct Width32;
51
52impl CrcWidth for Width32 {
53 const WIDTH: u32 = 32;
54 type Value = u32;
55}
56
57/// CRC-64 width implementation
58#[derive(Clone, Copy)]
59pub struct Width64;
60
61impl CrcWidth for Width64 {
62 const WIDTH: u32 = 64;
63 type Value = u64;
64}
65
66/// CRC State wrapper to manage the SIMD operations and reflection mode
67#[derive(Debug, Clone, Copy)]
68pub struct CrcState<T> {
69 pub value: T,
70 pub reflected: bool,
71}
72
73pub(crate) struct Calculator {}
74
75impl CrcCalculator for Calculator {
76 #[inline(always)]
77 fn calculate(state: u64, data: &[u8], params: &CrcParams) -> u64 {
78 unsafe { arch::update(state, data, params) }
79 }
80}
81
82impl CrcParams {
83 /// Creates custom CRC parameters for a given set of Rocksoft CRC parameters.
84 ///
85 /// Uses an internal cache to avoid regenerating folding keys for identical parameter sets.
86 /// The first call with a given set of parameters will generate and cache the keys, while
87 /// subsequent calls with the same parameters will use the cached keys for optimal performance.
88 ///
89 /// Does not support mis-matched refin/refout parameters, so both must be true or both false.
90 ///
91 /// Rocksoft parameters for lots of variants: https://reveng.sourceforge.io/crc-catalogue/all.htm
92 pub fn new(
93 name: &'static str,
94 width: u8,
95 poly: u64,
96 init: u64,
97 reflected: bool,
98 xorout: u64,
99 check: u64,
100 ) -> Self {
101 let keys_array = cache::get_or_generate_keys(width, poly, reflected);
102 let keys = crate::CrcKeysStorage::from_keys_fold_256(keys_array);
103
104 // Validate width is supported
105 if width != 16 && width != 32 && width != 64 {
106 panic!("Unsupported width: {width}");
107 }
108
109 // For reflected CRC-16, bit-reverse the init value for the SIMD algorithm
110 let init_algorithm = if width == 16 && reflected {
111 (init as u16).reverse_bits() as u64
112 } else {
113 init
114 };
115
116 Self {
117 algorithm: CrcAlgorithm::CrcCustom,
118 name,
119 width,
120 poly,
121 init,
122 init_algorithm,
123 refin: reflected,
124 refout: reflected,
125 xorout,
126 check,
127 keys,
128 }
129 }
130
131 /// Gets a key at the specified index, returning 0 if out of bounds.
132 /// This provides safe access regardless of internal key storage format.
133 #[inline(always)]
134 pub fn get_key(&self, index: usize) -> u64 {
135 self.keys.get_key(index)
136 }
137
138 /// Gets a key at the specified index, returning None if out of bounds.
139 /// This provides optional key access for cases where bounds checking is needed.
140 #[inline(always)]
141 pub fn get_key_checked(&self, index: usize) -> Option<u64> {
142 if index < self.keys.key_count() {
143 Some(self.keys.get_key(index))
144 } else {
145 None
146 }
147 }
148
149 /// Returns the number of keys available in this CrcParams instance.
150 #[inline(always)]
151 pub fn key_count(&self) -> usize {
152 self.keys.key_count()
153 }
154}