1use crate::CRC32_INITIAL_VALUE;
2
3#[cfg(target_arch = "aarch64")]
4pub(crate) mod acle;
5mod braid;
6mod combine;
7#[cfg(target_arch = "x86_64")]
8mod pclmulqdq;
9#[cfg(target_arch = "x86_64")]
10#[cfg(feature = "vpclmulqdq")]
11mod vpclmulqdq;
12
13pub use combine::{crc32_combine, crc32_combine_gen, crc32_combine_op};
14
15pub fn crc32(start: u32, buf: &[u8]) -> u32 {
16 if buf.len() < 64 {
19 return crc32_braid(start, buf);
20 }
21
22 let mut crc_state = Crc32Fold::new_with_initial(start);
23 crc_state.fold(buf, start);
24 crc_state.finish()
25}
26
27pub fn crc32_braid(start: u32, buf: &[u8]) -> u32 {
28 braid::crc32_braid::<5>(start, buf)
29}
30
31pub fn get_crc_table() -> &'static [u32; 256] {
32 braid::get_crc_table()
33}
34
35#[derive(Debug, Clone, Copy)]
36pub struct Crc32Fold {
37 #[cfg(target_arch = "x86_64")]
38 fold: pclmulqdq::Accumulator,
39 value: u32,
40}
41
42impl Default for Crc32Fold {
43 fn default() -> Self {
44 Self::new()
45 }
46}
47
48impl Crc32Fold {
49 pub const fn new() -> Self {
50 Self::new_with_initial(CRC32_INITIAL_VALUE)
51 }
52
53 pub const fn new_with_initial(initial: u32) -> Self {
54 Self {
55 #[cfg(target_arch = "x86_64")]
56 fold: pclmulqdq::Accumulator::new(),
57 value: initial,
58 }
59 }
60
61 pub fn fold(&mut self, src: &[u8], _start: u32) {
62 #[cfg(target_arch = "x86_64")]
63 if crate::cpu_features::is_enabled_pclmulqdq() {
64 return unsafe { self.fold.fold(src, _start) };
65 }
66
67 #[cfg(target_arch = "aarch64")]
68 if crate::cpu_features::is_enabled_crc() {
69 self.value = unsafe { self::acle::crc32_acle_aarch64(self.value, src) };
70 return;
71 }
72
73 self.value = braid::crc32_braid::<5>(self.value, src);
75 }
76
77 pub fn fold_copy(&mut self, dst: &mut [u8], src: &[u8]) {
78 #[cfg(target_arch = "x86_64")]
79 if crate::cpu_features::is_enabled_pclmulqdq() {
80 return unsafe { self.fold.fold_copy(dst, src) };
81 }
82
83 self.fold(src, 0);
84 dst[..src.len()].copy_from_slice(src);
85 }
86
87 pub fn finish(self) -> u32 {
88 #[cfg(target_arch = "x86_64")]
89 if crate::cpu_features::is_enabled_pclmulqdq() {
90 return unsafe { self.fold.finish() };
91 }
92
93 self.value
94 }
95}
96
97#[cfg(test)]
98mod test {
99 use braid::crc32_braid;
100
101 use super::*;
102
103 const INPUT: [u8; 1024] = {
104 let mut array = [0; 1024];
105 let mut i = 0;
106 while i < array.len() {
107 array[i] = i as u8;
108 i += 1;
109 }
110
111 array
112 };
113
114 #[test]
115 fn test_crc32_fold() {
116 let mut h = crc32fast::Hasher::new_with_initial(CRC32_INITIAL_VALUE);
118 h.update(&INPUT);
119 assert_eq!(crc32(CRC32_INITIAL_VALUE, &INPUT), h.finalize());
120 }
121
122 #[test]
123 fn test_crc32_fold_align() {
124 for i in 0..16 {
126 for start in [CRC32_INITIAL_VALUE, 42] {
127 let mut h = crc32fast::Hasher::new_with_initial(start);
128 h.update(&INPUT[i..]);
129 assert_eq!(
130 crc32(start, &INPUT[i..]),
131 h.finalize(),
132 "offset = {i}, start = {start}"
133 );
134 }
135 }
136 }
137
138 quickcheck::quickcheck! {
139 fn crc_fold_is_crc32fast(v: Vec<u8>, start: u32) -> bool {
140 let mut h = crc32fast::Hasher::new_with_initial(start);
141 h.update(&v);
142
143 let a = crc32(start, &v) ;
144 let b = h.finalize();
145
146 a == b
147 }
148 }
149
150 #[test]
151 fn chunked() {
152 const INPUT: &[&[u8]] = &[
153 &[116],
154 &[111, 107, 105, 111, 44, 32, 97, 115],
155 &[121, 110, 99, 45, 115, 116, 100, 44],
156 &[32, 97, 110, 100, 32, 115, 109, 111],
157 &[108, 46, 32, 89, 111, 117, 226, 128],
158 &[153, 118, 101, 32, 112, 114, 111, 98],
159 &[97, 98, 108, 121, 32, 117, 115, 101],
160 &[100, 32, 116, 104, 101, 109, 32, 97],
161 &[116, 32, 115, 111, 109, 101, 32, 112],
162 &[111, 105, 110, 116, 44, 32, 101, 105],
163 &[116, 104, 101, 114, 32, 100, 105, 114],
164 &[101, 99, 116, 108, 121, 32, 111, 114],
165 &[0],
166 ];
167
168 const START: u32 = 2380683574;
169
170 let mut in_chunks = START;
171 for chunk in INPUT {
172 in_chunks = crc32(in_chunks, chunk);
173 }
174
175 let flattened: Vec<_> = INPUT.iter().copied().flatten().copied().collect();
176 let flat = crc32(START, &flattened);
177
178 assert_eq!(in_chunks, flat);
179 }
180
181 #[test]
182 fn nasty_alignment() {
183 const START: u32 = 2380683574;
184
185 const FLAT: &[u8] = &[
186 116, 111, 107, 105, 111, 44, 32, 97, 115, 121, 110, 99, 45, 115, 116, 100, 44, 32, 97,
187 110, 100, 32, 115, 109, 111, 108, 46, 32, 89, 111, 117, 226, 128, 153, 118, 101, 32,
188 112, 114, 111, 98, 97, 98, 108, 121, 32, 117, 115, 101, 100, 32, 116, 104, 101, 109,
189 32, 97, 116, 32, 115, 111, 109, 101, 32, 112, 111, 105, 110, 116, 44, 32, 101, 105,
190 116, 104, 101, 114, 32, 100, 105, 114, 101, 99, 116, 108, 121, 32, 111, 114, 0,
191 ];
192
193 let mut i = 0;
194 let mut flat = FLAT.to_vec();
195 while flat[i..].as_ptr() as usize % 16 != 15 {
196 flat.insert(0, 0);
197 i += 1;
198 }
199
200 let flat = &flat[i..];
201
202 assert_eq!(crc32_braid::<5>(START, flat), crc32(START, flat));
203 assert_eq!(crc32(2380683574, flat), 1175758345);
204 }
205}