#[cfg(all(target_arch = "x86_64", not(miri)))]
use super::detect::{detect_capability, SimdCapability};
#[cfg(all(target_arch = "x86_64", not(miri)))]
#[inline]
pub(crate) fn copy_bytes(src: &[u8], dst: &mut [u8]) {
let len = core::cmp::min(src.len(), dst.len());
if len == 0 {
return;
}
let src_ptr = src.as_ptr();
let dst_ptr = dst.as_mut_ptr();
unsafe {
if detect_capability() >= SimdCapability::Avx2 {
copy_bytes_avx2(src_ptr, dst_ptr, len);
} else {
copy_bytes_sse2(src_ptr, dst_ptr, len);
}
}
}
#[cfg(all(target_arch = "aarch64", not(miri)))]
#[inline]
pub(crate) fn copy_bytes(src: &[u8], dst: &mut [u8]) {
let len = core::cmp::min(src.len(), dst.len());
if len == 0 {
return;
}
let src_ptr = src.as_ptr();
let dst_ptr = dst.as_mut_ptr();
unsafe {
copy_bytes_neon(src_ptr, dst_ptr, len);
}
}
#[cfg(not(all(any(target_arch = "x86_64", target_arch = "aarch64"), not(miri))))]
#[inline]
pub(crate) fn copy_bytes(src: &[u8], dst: &mut [u8]) {
let len = core::cmp::min(src.len(), dst.len());
dst[..len].copy_from_slice(&src[..len]);
}
#[cfg(all(target_arch = "x86_64", not(miri)))]
#[target_feature(enable = "avx2")]
unsafe fn copy_bytes_avx2(src: *const u8, dst: *mut u8, len: usize) {
use core::arch::x86_64::{__m256i, _mm256_loadu_si256, _mm256_storeu_si256};
let mut offset = 0usize;
while offset + 32 <= len {
let chunk = _mm256_loadu_si256(src.add(offset) as *const __m256i);
_mm256_storeu_si256(dst.add(offset) as *mut __m256i, chunk);
offset += 32;
}
while offset < len {
*dst.add(offset) = *src.add(offset);
offset += 1;
}
}
#[cfg(all(target_arch = "x86_64", not(miri)))]
#[target_feature(enable = "sse2")]
unsafe fn copy_bytes_sse2(src: *const u8, dst: *mut u8, len: usize) {
use core::arch::x86_64::{__m128i, _mm_loadu_si128, _mm_storeu_si128};
let mut offset = 0usize;
while offset + 16 <= len {
let chunk = _mm_loadu_si128(src.add(offset) as *const __m128i);
_mm_storeu_si128(dst.add(offset) as *mut __m128i, chunk);
offset += 16;
}
while offset < len {
*dst.add(offset) = *src.add(offset);
offset += 1;
}
}
#[cfg(all(target_arch = "aarch64", not(miri)))]
#[target_feature(enable = "neon")]
unsafe fn copy_bytes_neon(src: *const u8, dst: *mut u8, len: usize) {
use core::arch::aarch64::{vld1q_u8, vst1q_u8};
let mut offset = 0usize;
while offset + 16 <= len {
let chunk = vld1q_u8(src.add(offset));
vst1q_u8(dst.add(offset), chunk);
offset += 16;
}
while offset < len {
*dst.add(offset) = *src.add(offset);
offset += 1;
}
}
#[cfg(test)]
mod tests {
extern crate alloc;
use super::*;
#[test]
fn copy_bytes_matches_reference_all_lengths() {
for len in 0..=200usize {
let src: alloc::vec::Vec<u8> = (0..len).map(|i| (i * 31 + 7) as u8).collect();
let mut dst = alloc::vec![0u8; len];
copy_bytes(&src, &mut dst);
assert_eq!(src, dst, "mismatch at len {len}");
}
}
#[test]
fn copy_bytes_respects_shorter_length() {
let src = [1u8, 2, 3, 4, 5];
let mut dst = [0u8; 3];
copy_bytes(&src, &mut dst);
assert_eq!(dst, [1, 2, 3]);
}
}