#![cfg(all(target_arch = "aarch64", feature = "std"))]
use proptest::prelude::*;
use proptest::test_runner::{Config as ProptestConfig, TestRunner};
const BOUND: u8 = 0xC0;
fn utf8_string_strategy() -> impl Strategy<Value = String> {
let ranges = prop::char::ranges(std::borrow::Cow::Borrowed(&[
'\u{0020}'..='\u{007E}',
'\u{00A0}'..='\u{024F}',
'\u{0300}'..='\u{036F}',
'\u{0370}'..='\u{097F}',
'\u{1100}'..='\u{11FF}',
'\u{4E00}'..='\u{9FFF}',
'\u{AC00}'..='\u{D7A3}',
'\u{1F300}'..='\u{1FAFF}',
'\u{20000}'..='\u{2A6DF}',
]));
prop::collection::vec(ranges, 16..1024).prop_map(|chars| chars.into_iter().collect::<String>())
}
unsafe fn first_disagreement(bytes: &[u8]) -> Option<usize> {
let mut offset = 0usize;
while offset + 64 <= bytes.len() {
let ptr = unsafe { bytes.as_ptr().add(offset) };
let neon_mask = unsafe { simd_normalizer::simd_test_api::neon_scan_chunk(ptr, BOUND) };
let sve2_mask = unsafe { simd_normalizer::simd_test_api::sve2_scan_chunk(ptr, BOUND) };
if neon_mask != sve2_mask {
return Some(offset);
}
offset += 64;
}
None
}
#[test]
fn neon_and_sve2_agree_on_random_inputs() {
if !std::arch::is_aarch64_feature_detected!("sve2") {
eprintln!(
"skipping neon_and_sve2_agree_on_random_inputs: host lacks SVE2 \
(expected on Apple Silicon; needs Graviton 4 / Ampere AmpereOne \
/ Neoverse-N2/V2/V3 for real verification)"
);
return;
}
let mut runner = TestRunner::new(ProptestConfig::with_cases(1000));
let strategy = utf8_string_strategy();
runner
.run(&strategy, |mut s| {
while s.len() < 64 {
s.push_str("aaaaaaaa");
}
let disagreement = unsafe { first_disagreement(s.as_bytes()) };
prop_assert!(
disagreement.is_none(),
"NEON and SVE2 disagreed at chunk offset {:?} on input of length {}",
disagreement,
s.len()
);
Ok(())
})
.expect("NEON and SVE2 vtables disagreed on a generated input");
}
#[test]
fn neon_and_sve2_agree_on_targeted_patterns() {
if !std::arch::is_aarch64_feature_detected!("sve2") {
eprintln!(
"skipping neon_and_sve2_agree_on_targeted_patterns: host lacks \
SVE2 (expected on Apple Silicon)"
);
return;
}
let mut cases: Vec<[u8; 64]> = vec![
[0x00; 64], [0xFF; 64], [BOUND; 64], [BOUND - 1; 64], ];
for i in 0..64 {
let mut buf = [0u8; 64];
buf[i] = 0xFF;
cases.push(buf);
}
let mut alt = [0u8; 64];
for (i, b) in alt.iter_mut().enumerate() {
*b = if i % 2 == 0 { 0x00 } else { 0xFF };
}
cases.push(alt);
for (idx, case) in cases.iter().enumerate() {
let neon_mask =
unsafe { simd_normalizer::simd_test_api::neon_scan_chunk(case.as_ptr(), BOUND) };
let sve2_mask =
unsafe { simd_normalizer::simd_test_api::sve2_scan_chunk(case.as_ptr(), BOUND) };
assert_eq!(
neon_mask, sve2_mask,
"NEON/SVE2 disagreement on targeted case #{idx}: \
neon=0x{neon_mask:016x} sve2=0x{sve2_mask:016x}"
);
}
}