use super::*;
use alloc::vec;
fn runnable_tiers() -> vec::Vec<ExactCopyTier> {
let mut tiers = vec![ExactCopyTier::Scalar];
#[cfg(all(
feature = "std",
feature = "kernel-sse",
any(target_arch = "x86", target_arch = "x86_64")
))]
{
let caps = detect_x86_caps();
if caps.sse2 {
tiers.push(ExactCopyTier::Sse2);
}
#[cfg(feature = "kernel-avx2")]
if caps.avx2 {
tiers.push(ExactCopyTier::Avx2);
}
}
#[cfg(all(
target_arch = "aarch64",
target_feature = "neon",
feature = "kernel-neon"
))]
tiers.push(ExactCopyTier::Neon);
tiers
}
#[test]
fn every_runnable_tier_copies_the_same_bytes_as_memcpy() {
let src: vec::Vec<u8> = (0..4096u32)
.map(|i| (i.wrapping_mul(2654435761) >> 24) as u8)
.collect();
for tier in runnable_tiers() {
for len in 33..2048usize {
let mut got = vec![0u8; len];
unsafe { copy_exact_medium(src.as_ptr(), got.as_mut_ptr(), len, tier) };
assert_eq!(
&got[..],
&src[..len],
"copy_exact_medium mismatch at len={len} on {tier:?}"
);
}
}
}
#[test]
fn the_resolved_tier_is_one_this_cpu_can_run() {
assert!(
runnable_tiers().contains(&ExactCopyTier::resolve()),
"resolve() picked a tier this CPU cannot execute",
);
}
#[test]
fn copy_bytes_overshooting_zero_len_is_noop() {
let src = [1_u8, 2, 3, 4];
let mut dst = [9_u8, 9, 9, 9];
unsafe {
copy_bytes_overshooting((src.as_ptr(), src.len()), (dst.as_mut_ptr(), dst.len()), 0);
}
assert_eq!(dst, [9_u8, 9, 9, 9]);
}
#[test]
fn copy_bytes_overshooting_fallback_exact_copy_when_caps_are_tight() {
let len = 65; let src = vec![5_u8; len];
let mut dst = vec![0_u8; len];
unsafe {
copy_bytes_overshooting((src.as_ptr(), len), (dst.as_mut_ptr(), len), len);
}
assert_eq!(dst, src);
}
#[test]
fn copy_bytes_overshooting_single_op_small() {
for len in 1..=16 {
let mut src = [0u8; 32];
for (i, b) in src.iter_mut().enumerate() {
*b = i as u8;
}
let mut dst = [0u8; 32];
unsafe {
copy_bytes_overshooting((src.as_ptr(), 32), (dst.as_mut_ptr(), 32), len);
}
assert_eq!(&dst[..len], &src[..len], "len={len}");
}
}
#[test]
fn copy_scalar_copies_requested_bytes() {
let src = [11_u8, 12, 13, 14, 15, 16, 17, 18];
let mut dst = [0_u8; 8];
unsafe { copy_scalar(src.as_ptr(), dst.as_mut_ptr(), src.len()) };
assert_eq!(dst, src);
}
#[cfg(all(
feature = "std",
feature = "kernel-sse",
any(target_arch = "x86", target_arch = "x86_64")
))]
#[test]
fn copy_sse2_copies_full_chunk_when_available() {
if !std::arch::is_x86_feature_detected!("sse2") {
return;
}
let src = [7_u8; 16];
let mut dst = [0_u8; 16];
unsafe { copy_sse2(src.as_ptr(), dst.as_mut_ptr(), 16) };
assert_eq!(dst, src);
}
#[cfg(all(
feature = "std",
feature = "kernel-avx2",
any(target_arch = "x86", target_arch = "x86_64")
))]
#[test]
fn copy_avx2_copies_full_chunk_when_available() {
if !std::arch::is_x86_feature_detected!("avx2") {
return;
}
let src = [8_u8; 32];
let mut dst = [0_u8; 32];
unsafe { copy_avx2(src.as_ptr(), dst.as_mut_ptr(), 32) };
assert_eq!(dst, src);
}
#[cfg(all(
feature = "std",
feature = "kernel-avx2",
any(target_arch = "x86", target_arch = "x86_64")
))]
#[test]
fn copy_avx2_copies_full_unroll2_iteration() {
use alloc::vec::Vec;
if !std::arch::is_x86_feature_detected!("avx2") {
return;
}
let src: Vec<u8> = (0..64u8).collect();
let mut dst = [0_u8; 64];
unsafe { copy_avx2(src.as_ptr(), dst.as_mut_ptr(), 64) };
assert_eq!(&dst[..], &src[..]);
}
#[cfg(all(
feature = "std",
feature = "kernel-avx2",
any(target_arch = "x86", target_arch = "x86_64")
))]
#[test]
fn copy_avx2_copies_unroll2_loop_plus_residual_tail() {
use alloc::vec::Vec;
if !std::arch::is_x86_feature_detected!("avx2") {
return;
}
let src: Vec<u8> = (0..96u8).collect();
let mut dst = [0_u8; 96];
unsafe { copy_avx2(src.as_ptr(), dst.as_mut_ptr(), 96) };
assert_eq!(&dst[..], &src[..]);
assert_eq!(&dst[60..68], &[60, 61, 62, 63, 64, 65, 66, 67]);
}
#[cfg(all(
feature = "std",
feature = "kernel-vbmi2",
any(target_arch = "x86", target_arch = "x86_64")
))]
#[test]
fn copy_avx512_copies_full_chunk_when_available() {
if !std::arch::is_x86_feature_detected!("avx512f") {
return;
}
let src = [9_u8; 64];
let mut dst = [0_u8; 64];
unsafe { copy_avx512(src.as_ptr(), dst.as_mut_ptr(), 64) };
assert_eq!(dst, src);
}