pub use self::{dimension::Dim, utils::*};
pub(crate) mod dimension;
use super::Rank;
pub trait IntoDimension {
type Dim: Dimension;
fn into_dimension(self) -> Self::Dim;
}
pub trait Dimension {
type Pattern;
fn as_slice(&self) -> &[usize];
fn rank(&self) -> Rank;
fn size(&self) -> usize;
#[doc(hidden)]
fn stride_offset(index: &Self, strides: &Self) -> isize {
izip!(index.as_slice(), strides.as_slice())
.fold(0, |acc, (&i, &s)| acc + stride_offset(i, s))
}
}
pub(crate) mod utils {
use crate::index::{Ix, Ixs};
use crate::shape::{Shape, ShapeError, Stride};
use core::mem;
pub fn can_index_slice<A>(
data: &[A],
shape: &Shape,
stride: &Stride,
) -> Result<(), ShapeError> {
let max_offset = max_abs_offset_check_overflow::<A>(shape, stride)?;
can_index_slice_impl(max_offset, data.len(), shape, stride)
}
pub fn dim_stride_overlap(dim: &Shape, strides: &Stride) -> bool {
let order = strides._fastest_varying_stride_order();
let mut sum_prev_offsets = 0;
for &index in order.as_slice() {
let d = dim[index];
let s = (strides[index] as isize).abs();
match d {
0 => return false,
1 => {}
_ => {
if s <= sum_prev_offsets {
return true;
}
sum_prev_offsets += (d - 1) as isize * s;
}
}
}
false
}
pub fn max_abs_offset_check_overflow<A>(
dim: &Shape,
strides: &Stride,
) -> Result<usize, ShapeError> {
max_abs_offset_check_overflow_impl(mem::size_of::<A>(), dim, strides)
}
pub fn size_of_shape_checked(dim: &Shape) -> Result<usize, ShapeError> {
let size_nonzero = dim
.as_slice()
.iter()
.filter(|&&d| d != 0)
.try_fold(1usize, |acc, &d| acc.checked_mul(d))
.ok_or(ShapeError::Overflow)?;
if size_nonzero > ::std::isize::MAX as usize {
Err(ShapeError::Overflow)
} else {
Ok(dim.size())
}
}
#[inline(always)]
pub fn stride_offset(n: Ix, stride: Ix) -> isize {
(n as isize) * (stride as Ixs)
}
fn can_index_slice_impl(
max_offset: usize,
data_len: usize,
dim: &Shape,
strides: &Stride,
) -> Result<(), ShapeError> {
let is_empty = dim.as_slice().iter().any(|&d| d == 0);
if is_empty && max_offset > data_len {
return Err(ShapeError::OutOfBounds);
}
if !is_empty && max_offset >= data_len {
return Err(ShapeError::OutOfBounds);
}
if !is_empty && dim_stride_overlap(dim, strides) {
return Err(ShapeError::Unsupported);
}
Ok(())
}
fn max_abs_offset_check_overflow_impl(
elem_size: usize,
dim: &Shape,
strides: &Stride,
) -> Result<usize, ShapeError> {
if dim.rank() != strides.rank() {
return Err(ShapeError::IncompatibleLayout);
}
let _ = size_of_shape_checked(dim)?;
let max_offset: usize = izip!(dim.as_slice(), strides.as_slice())
.try_fold(0usize, |acc, (&d, &s)| {
let s = s as isize;
let off = d.saturating_sub(1).checked_mul(s.unsigned_abs())?;
acc.checked_add(off)
})
.ok_or_else(|| ShapeError::Overflow)?;
if max_offset > isize::MAX as usize {
return Err(ShapeError::Overflow);
}
let max_offset_bytes = max_offset
.checked_mul(elem_size)
.ok_or(ShapeError::Overflow)?;
if max_offset_bytes > isize::MAX as usize {
return Err(ShapeError::Overflow);
}
Ok(max_offset)
}
}