#[cfg(target_arch = "x86")] use core::arch::x86 as arch; #[cfg(target_arch = "x86_64")] use core::arch::x86_64 as arch;
#[derive(Clone)] pubstruct State {
state: u32,
}
impl State { #[cfg(not(feature = "std"))] pubfn new(state: u32) -> Option<Self> { if cfg!(target_feature = "pclmulqdq")
&& cfg!(target_feature = "sse2")
&& cfg!(target_feature = "sse4.1")
{ // SAFETY: The conditions above ensure that all // required instructions are supported by the CPU.
Some(Self { state })
} else {
None
}
}
#[cfg(feature = "std")] pubfn new(state: u32) -> Option<Self> { if is_x86_feature_detected!("pclmulqdq")
&& is_x86_feature_detected!("sse2")
&& is_x86_feature_detected!("sse4.1")
{ // SAFETY: The conditions above ensure that all // required instructions are supported by the CPU.
Some(Self { state })
} else {
None
}
}
pubfn update(&mutself, buf: &[u8]) { // SAFETY: The `State::new` constructor ensures that all // required instructions are supported by the CPU. self.state = unsafe { calculate(self.state, buf) }
}
#[target_feature(enable = "pclmulqdq", enable = "sse2", enable = "sse4.1")] unsafefn calculate(crc: u32, mut data: &[u8]) -> u32 { // In theory we can accelerate smaller chunks too, but for now just rely on // the fallback implementation as it's too much hassle and doesn't seem too // beneficial. if data.len() < 128 { return ::baseline::update_fast_16(crc, data);
}
let k3k4 = arch::_mm_set_epi64x(K4, K3); letmut x = reduce128(x3, x2, k3k4);
x = reduce128(x, x1, k3k4);
x = reduce128(x, x0, k3k4);
// Step 2: fold by 1 loop while data.len() >= 16 {
x = reduce128(x, get(&mut data), k3k4);
}
debug("128 > 64 init", x);
// Perform step 3, reduction from 128 bits to 64 bits. This is // significantly different from the paper and basically doesn't follow it // at all. It's not really clear why, but implementations of this algorithm // in Chrome/Linux diverge in the same way. It is beyond me why this is // different than the paper, maybe the paper has like errata or something? // Unclear. // // It's also not clear to me what's actually happening here and/or why, but // algebraically what's happening is: // // x = (x[0:63] • K4) ^ x[64:127] // 96 bit result // x = ((x[0:31] as u64) • K5) ^ x[32:95] // 64 bit result // // It's... not clear to me what's going on here. The paper itself is pretty // vague on this part but definitely uses different constants at least. // It's not clear to me, reading the paper, where the xor operations are // happening or why things are shifting around. This implementation... // appears to work though! let x = arch::_mm_xor_si128(
arch::_mm_clmulepi64_si128(x, k3k4, 0x10),
arch::_mm_srli_si128(x, 8),
); let x = arch::_mm_xor_si128(
arch::_mm_clmulepi64_si128(
arch::_mm_and_si128(x, arch::_mm_set_epi32(0, 0, 0, !0)),
arch::_mm_set_epi64x(0, K5), 0x00,
),
arch::_mm_srli_si128(x, 4),
);
debug("128 > 64 xx", x);
// Perform a Barrett reduction from our now 64 bits to 32 bits. The // algorithm for this is described at the end of the paper, and note that // this also implements the "bit reflected input" variant. let pu = arch::_mm_set_epi64x(U_PRIME, P_X);
// T1(x) = ⌊(R(x) % x^32)⌋ • μ let t1 = arch::_mm_clmulepi64_si128(
arch::_mm_and_si128(x, arch::_mm_set_epi32(0, 0, 0, !0)),
pu, 0x10,
); // T2(x) = ⌊(T1(x) % x^32)⌋ • P(x) let t2 = arch::_mm_clmulepi64_si128(
arch::_mm_and_si128(t1, arch::_mm_set_epi32(0, 0, 0, !0)),
pu, 0x00,
); // We're doing the bit-reflected variant, so get the upper 32-bits of the // 64-bit result instead of the lower 32-bits. // // C(x) = R(x) ^ T2(x) / x^32 let c = arch::_mm_extract_epi32(arch::_mm_xor_si128(x, t2), 1) as u32;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.