use core::mem::MaybeUninit;
use super::{bgr_widen, bgr_widen_scalar};
use crate::simd::diff::{assert_eq_over_lane_sweep, lane_sweep_lengths};
fn bgr_widen_scalar_init(src: &[u8]) -> Vec<f32> {
let n = src.len();
let mut v: Vec<f32> = Vec::with_capacity(n);
let spare: &mut [MaybeUninit<f32>] = v.spare_capacity_mut();
bgr_widen_scalar(&mut spare[..n], src);
unsafe { v.set_len(n) };
v
}
fn bgr_widen_dispatch_init(src: &[u8]) -> Vec<f32> {
let n = src.len();
let mut v: Vec<f32> = Vec::with_capacity(n);
let spare: &mut [MaybeUninit<f32>] = v.spare_capacity_mut();
bgr_widen(&mut spare[..n], src);
unsafe { v.set_len(n) };
v
}
#[cfg(target_arch = "aarch64")]
fn bgr_widen_neon_init(src: &[u8]) -> Vec<f32> {
let n = src.len();
let mut v: Vec<f32> = Vec::with_capacity(n);
let spare: &mut [MaybeUninit<f32>] = v.spare_capacity_mut();
unsafe {
super::bgr_widen_neon(&mut spare[..n], src);
v.set_len(n);
}
v
}
fn gen_bgr_src(n_pixels: usize) -> Vec<u8> {
(0..n_pixels * 3).map(|i| ((i * 7) % 256) as u8).collect()
}
#[test]
fn bgr_widen_scalar_matches_dispatcher_exact() {
fn widen_scalar(src: &[u8]) -> Vec<f32> {
bgr_widen_scalar_init(src)
}
fn widen_dispatch(src: &[u8]) -> Vec<f32> {
bgr_widen_dispatch_init(src)
}
assert_eq_over_lane_sweep(
16, widen_scalar,
widen_dispatch,
gen_bgr_src,
);
}
#[cfg(target_arch = "aarch64")]
#[test]
fn bgr_widen_neon_matches_scalar_bit_identical() {
if !crate::simd::is_neon_available() {
return;
}
for &n_pixels in &[0usize, 1, 15, 16, 17, 31, 32, 33, 47, 48, 49, 64, 100, 1024] {
let src = gen_bgr_src(n_pixels);
let scalar = bgr_widen_scalar_init(&src);
let neon = bgr_widen_neon_init(&src);
assert_eq!(
neon,
scalar,
"bgr_widen_neon vs bgr_widen_scalar differ at n_pixels={n_pixels} \
(src.len()={}, out.len()={})",
src.len(),
scalar.len()
);
}
}
#[test]
fn bgr_widen_lane_sweep_covers_tile_boundaries() {
let sweep = lane_sweep_lengths(16);
assert_eq!(sweep, [0, 1, 15, 16, 17, 31, 32, 48, 49]);
}
#[test]
fn bgr_widen_empty_is_noop() {
let buf = bgr_widen_dispatch_init(&[]);
assert!(buf.is_empty());
let buf = bgr_widen_scalar_init(&[]);
assert!(buf.is_empty());
}
#[test]
fn bgr_widen_one_pixel_swaps_r_and_b() {
let buf = bgr_widen_dispatch_init(&[10, 20, 30]);
assert_eq!(buf, vec![30.0_f32, 20.0, 10.0]);
}
#[test]
fn bgr_widen_fifteen_pixels_below_tile() {
let src = gen_bgr_src(15);
let buf = bgr_widen_dispatch_init(&src);
let scalar = bgr_widen_scalar_init(&src);
assert_eq!(buf, scalar);
assert_eq!(buf.len(), 45);
}
#[test]
fn bgr_widen_sixteen_pixels_one_full_tile() {
let src = gen_bgr_src(16);
let buf = bgr_widen_dispatch_init(&src);
let scalar = bgr_widen_scalar_init(&src);
assert_eq!(buf, scalar);
assert_eq!(buf.len(), 48);
}
#[test]
fn bgr_widen_seventeen_pixels_tile_plus_one() {
let src = gen_bgr_src(17);
let buf = bgr_widen_dispatch_init(&src);
let scalar = bgr_widen_scalar_init(&src);
assert_eq!(buf, scalar);
assert_eq!(buf.len(), 51);
}
#[test]
fn bgr_widen_forty_eight_pixels_three_full_tiles() {
let src = gen_bgr_src(48);
let buf = bgr_widen_dispatch_init(&src);
let scalar = bgr_widen_scalar_init(&src);
assert_eq!(buf, scalar);
assert_eq!(buf.len(), 144);
}
#[test]
fn bgr_widen_forty_nine_pixels_three_tiles_plus_one() {
let src = gen_bgr_src(49);
let buf = bgr_widen_dispatch_init(&src);
let scalar = bgr_widen_scalar_init(&src);
assert_eq!(buf, scalar);
assert_eq!(buf.len(), 147);
}
#[test]
fn image_to_array_bgr_matches_old_loop() {
let w = 512usize;
let h = 512usize;
let n_pixels = w * h;
let n_bytes = n_pixels * 3;
type PatternFn<'a> = Box<dyn Fn() -> Vec<u8> + 'a>;
let patterns: [(&str, PatternFn<'_>); 4] = [
("all_zero", Box::new(|| vec![0u8; n_bytes])),
("all_255", Box::new(|| vec![255u8; n_bytes])),
(
"checkerboard",
Box::new(|| {
(0..n_bytes)
.map(|i| {
let pixel_idx = i / 3;
if (pixel_idx + (pixel_idx / w)).is_multiple_of(2) {
0u8
} else {
255u8
}
})
.collect()
}),
),
(
"gradient",
Box::new(|| {
let mut v = Vec::with_capacity(n_bytes);
for y in 0..h {
for x in 0..w {
v.push((x % 256) as u8); v.push((y % 256) as u8); v.push(((x + y) % 256) as u8); }
}
v
}),
),
];
for (name, make_pattern) in &patterns {
let raw = make_pattern();
assert_eq!(raw.len(), n_bytes, "pattern {name} length mismatch");
let mut old: Vec<f32> = Vec::with_capacity(n_bytes);
for px in raw.chunks_exact(3) {
old.push(f32::from(px[2]));
old.push(f32::from(px[1]));
old.push(f32::from(px[0]));
}
assert_eq!(old.len(), n_bytes, "OLD loop length mismatch ({name})");
let new = bgr_widen_dispatch_init(&raw);
assert_eq!(
new, old,
"dispatcher must produce byte-identical output to the reference \
`chunks_exact(3) + push * 3` loop (pattern={name}, n_bytes={n_bytes})"
);
}
}
#[test]
#[should_panic(expected = "bgr_widen_scalar: src.len() (7) must be a multiple of 3")]
fn bgr_widen_scalar_panics_on_non_triplet_src_in_release() {
let src = [0u8; 7];
let mut v: Vec<f32> = Vec::with_capacity(7);
let spare: &mut [MaybeUninit<f32>] = v.spare_capacity_mut();
bgr_widen_scalar(&mut spare[..7], &src);
}
#[test]
#[should_panic(expected = "bgr_widen_scalar: out.len() (9) must equal src.len() (6)")]
fn bgr_widen_scalar_panics_on_size_mismatch_in_release() {
let src = [0u8; 6];
let mut v: Vec<f32> = Vec::with_capacity(9);
let spare: &mut [MaybeUninit<f32>] = v.spare_capacity_mut();
bgr_widen_scalar(&mut spare[..9], &src);
}
#[cfg(target_arch = "aarch64")]
#[test]
#[should_panic(expected = "bgr_widen_neon: src.len() (7) must be a multiple of 3")]
fn bgr_widen_neon_panics_on_non_triplet_src_in_release() {
if !crate::simd::is_neon_available() {
panic!("bgr_widen_neon: src.len() (7) must be a multiple of 3 (skipped — NEON unavailable)");
}
let _ = bgr_widen_neon_init(&[0u8; 7]);
}
#[cfg(target_arch = "aarch64")]
#[test]
#[should_panic(expected = "bgr_widen_neon: out.len() (9) must equal src.len() (6)")]
fn bgr_widen_neon_panics_on_size_mismatch_in_release() {
if !crate::simd::is_neon_available() {
panic!("bgr_widen_neon: out.len() (9) must equal src.len() (6) (skipped — NEON unavailable)");
}
let src = [0u8; 6];
let mut v: Vec<f32> = Vec::with_capacity(9);
let spare: &mut [MaybeUninit<f32>] = v.spare_capacity_mut();
unsafe { super::bgr_widen_neon(&mut spare[..9], &src) };
}