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