diff --git a/src/codec.rs b/src/codec.rs index d2e4a86..bd80ad7 100644 --- a/src/codec.rs +++ b/src/codec.rs @@ -162,6 +162,15 @@ fn quad(word: u64) -> u32 { | u32::from(HALVES[1][((word >> 8) & 0xff0f) as usize]) << 16 } +/// The eight bytes of `group` from `at`, as one little-endian word. Copied as +/// a fixed eight bytes, the read compiles to a single load; spelling out the +/// bytes one by one measured several times dearer in fuel. +fn word(group: &[u8; 12], at: usize) -> u64 { + let mut bytes = [0; 8]; + bytes.copy_from_slice(&group[at..at + 8]); + u64::from_le_bytes(bytes) +} + /// How many characters `bytes` bytes encode to, padding included. pub fn encoded_length(bytes: usize) -> usize { bytes.div_ceil(3) * 4 @@ -171,23 +180,18 @@ pub fn encoded_length(bytes: usize) -> usize { /// exactly `encoded_length` characters. Twelve bytes go through each step /// of the main loop, for the same reason sixteen characters do in `decode`. pub fn encode(bytes: &[u8], out: &mut [u8]) { - let wide = bytes.chunks_exact(12); - let (wide_out, rest_out) = out.split_at_mut(bytes.len() / 12 * 16); - let rest = wide.remainder(); - for (group, chars) in wide.zip(wide_out.chunks_exact_mut(16)) { - let (Some(low), Some(high)) = - (group.first_chunk::<8>(), group.last_chunk::<8>()) - else { - continue; - }; - let low = u64::from_le_bytes(*low); - let high = u64::from_le_bytes(*high); + let (wide, rest) = bytes.as_chunks::<12>(); + let (wide_out, rest_out) = out.split_at_mut(wide.len() * 16); + let (wide_out, _) = wide_out.as_chunks_mut::<16>(); + let (rest_out, _) = rest_out.as_chunks_mut::<4>(); + for (group, chars) in wide.iter().zip(wide_out) { + let (low, high) = (word(group, 0), word(group, 4)); let front = u64::from(quad(low)) | u64::from(quad(low >> 24)) << 32; let back = u64::from(quad(high >> 16)) | u64::from(quad(high >> 40)) << 32; chars[..8].copy_from_slice(&front.to_le_bytes()); chars[8..].copy_from_slice(&back.to_le_bytes()); } - for (group, chars) in rest.chunks(3).zip(rest_out.chunks_exact_mut(4)) { + for (group, chars) in rest.chunks(3).zip(rest_out) { let mut padded = [0; 8]; padded[..group.len()].copy_from_slice(group); chars.copy_from_slice(&quad(u64::from_le_bytes(padded)).to_le_bytes());