use super::validation;
use crate::error::{CoreError, ErrorContext, ErrorLocation};
use ::ndarray::{
Array, ArrayBase, ArrayView as NdArrayView, ArrayViewMut as NdArrayViewMut, Data, Dimension,
Ix1, Ix2, RawDataMut,
};
pub type ArrayView<'a, A, D> = NdArrayView<'a, A, D>;
pub type ViewMut<'a, A, D> = NdArrayViewMut<'a, A, D>;
pub unsafe fn view_as<A, B, S, D>(array: &ArrayBase<S, D>) -> Result<ArrayView<'_, B, D>, CoreError>
where
A: Clone,
S: Data<Elem = A>,
D: Dimension,
{
validation::check_not_empty(array)?;
let a_size = std::mem::size_of::<A>();
let b_size = std::mem::size_of::<B>();
if a_size == 0 || b_size == 0 {
return Err(CoreError::ValidationError(
ErrorContext::new("Cannot reinterpret view of zero-sized type".to_string())
.with_location(ErrorLocation::new(file!(), line!())),
));
}
if a_size != b_size {
return Err(CoreError::NotImplementedError(
ErrorContext::new(format!(
"view_as with differing element sizes ({a_size} vs {b_size}) requires a shape \
change not representable in the generic dimension D; \
reshape to Ix1 first or use a dedicated 1-D helper"
))
.with_location(ErrorLocation::new(file!(), line!())),
));
}
let ptr = array.as_ptr() as *const B;
if (ptr as usize) % std::mem::align_of::<B>() != 0 {
return Err(CoreError::ValidationError(
ErrorContext::new(format!(
"Data pointer is not aligned for the target type (required alignment: {})",
std::mem::align_of::<B>()
))
.with_location(ErrorLocation::new(file!(), line!())),
));
}
let raw_view = array.raw_view().cast::<B>();
Ok(raw_view.deref_into_view())
}
pub unsafe fn view_mut_as<A, B, S, D>(
array: &mut ArrayBase<S, D>,
) -> Result<ViewMut<'_, B, D>, CoreError>
where
A: Clone,
S: Data<Elem = A> + RawDataMut,
D: Dimension,
{
validation::check_not_empty(array)?;
let a_size = std::mem::size_of::<A>();
let b_size = std::mem::size_of::<B>();
if a_size == 0 || b_size == 0 {
return Err(CoreError::ValidationError(
ErrorContext::new("Cannot reinterpret view of zero-sized type".to_string())
.with_location(ErrorLocation::new(file!(), line!())),
));
}
if a_size != b_size {
return Err(CoreError::NotImplementedError(
ErrorContext::new(format!(
"view_mut_as with differing element sizes ({a_size} vs {b_size}) requires a \
shape change not representable in the generic dimension D; \
reshape to Ix1 first or use a dedicated 1-D helper"
))
.with_location(ErrorLocation::new(file!(), line!())),
));
}
let ptr = array.as_ptr() as *const B;
if (ptr as usize) % std::mem::align_of::<B>() != 0 {
return Err(CoreError::ValidationError(
ErrorContext::new(format!(
"Data pointer is not aligned for the target type (required alignment: {})",
std::mem::align_of::<B>()
))
.with_location(ErrorLocation::new(file!(), line!())),
));
}
let raw_view_mut = array.raw_view_mut().cast::<B>();
Ok(raw_view_mut.deref_into_view_mut())
}
#[allow(dead_code)]
pub fn transpose_view<A, S>(array: &ArrayBase<S, Ix2>) -> Result<Array<A, Ix2>, CoreError>
where
A: Clone,
S: Data<Elem = A>,
{
validation::check_not_empty(array)?;
Ok(array.to_owned().t().to_owned())
}
#[allow(dead_code)]
pub fn diagonal_view<A, S>(array: &ArrayBase<S, Ix2>) -> Result<Array<A, Ix1>, CoreError>
where
A: Clone,
S: Data<Elem = A>,
{
validation::check_not_empty(array)?;
validation::check_square(array)?;
Ok(array.diag().to_owned())
}
#[cfg(test)]
mod tests {
use super::*;
use ndarray::array;
#[test]
fn test_view_as_same_size_f32_as_u32() {
let arr = array![0.0_f32, 1.0_f32];
let view = unsafe { view_as::<f32, u32, _, _>(&arr) };
assert!(view.is_ok(), "view_as should succeed for same-size types");
let view = view.expect("view_as returned Err unexpectedly");
assert_eq!(view[0], 0x0000_0000_u32);
assert_eq!(view[1], 0x3F80_0000_u32);
}
#[test]
fn test_view_as_u32_as_i32_round_trip() {
let arr = array![0_u32, 0xFFFF_FFFF_u32];
let view = unsafe { view_as::<u32, i32, _, _>(&arr) };
assert!(view.is_ok());
let view = view.expect("view_as returned Err unexpectedly");
assert_eq!(view[0], 0_i32);
assert_eq!(view[1], -1_i32);
}
#[test]
fn test_view_as_different_sizes_returns_error() {
let arr = array![0_u8, 1_u8, 2_u8, 3_u8];
let result = unsafe { view_as::<u8, u32, _, _>(&arr) };
assert!(
result.is_err(),
"view_as with different element sizes must fail"
);
match result.expect_err("expected an error") {
CoreError::NotImplementedError(_) => {}
other => panic!("Expected NotImplementedError, got: {other:?}"),
}
}
#[test]
fn test_view_mut_as_same_size_mutates_original() {
let mut arr = array![0_u32, 1_u32];
{
let mut view = unsafe { view_mut_as::<u32, i32, _, _>(&mut arr) }
.expect("view_mut_as returned Err unexpectedly");
view[0] = -1_i32; }
assert_eq!(arr[0], 0xFFFF_FFFF_u32);
assert_eq!(arr[1], 1_u32); }
#[test]
fn test_view_as_empty_array_returns_error() {
let arr = ndarray::Array1::<f32>::zeros(0);
let result = unsafe { view_as::<f32, u32, _, _>(&arr) };
assert!(result.is_err(), "view_as on empty array must fail");
match result.expect_err("expected an error") {
CoreError::ValidationError(_) => {}
other => panic!("Expected ValidationError, got: {other:?}"),
}
}
}