use super::*;
use crate::ffi::DLDevice;
use snafu::ensure;
use std::borrow::Cow;
#[inline]
pub fn compact_strides(shape: &[i64]) -> Result<Vec<i64>, Error> {
validate_shape_dimensions(shape)?;
let mut strides = vec![0; shape.len()];
let mut stride = 1i64;
for axis in (0..shape.len()).rev() {
strides[axis] = stride;
stride = stride
.checked_mul(shape[axis])
.ok_or(Error::NumElementsOverflow)?;
}
Ok(strides)
}
#[inline]
pub fn compact_strides_array<T, const N: usize>(shape: [T; N]) -> Result<[i64; N], Error>
where
T: Into<i64> + Copy,
{
let shape = shape.map(Into::into);
validate_shape_dimensions(&shape)?;
let mut strides = [0i64; N];
let mut stride = 1i64;
for axis in (0..N).rev() {
strides[axis] = stride;
stride = stride
.checked_mul(shape[axis])
.ok_or(Error::NumElementsOverflow)?;
}
Ok(strides)
}
pub fn is_compact_strides(shape: &[i64], strides: Option<&[i64]>) -> Result<bool, Error> {
validate_shape_dimensions(shape)?;
let Some(strides) = strides else {
return Ok(true);
};
ensure!(
shape.len() == strides.len(),
MismatchedStridesSnafu {
shape_len: shape.len(),
strides_len: strides.len()
}
);
if shape.contains(&0) {
return Ok(true);
}
Ok(strides == compact_strides(shape)?.as_slice())
}
fn validate_shape_dimensions(shape: &[i64]) -> Result<(), Error> {
for (axis, &value) in shape.iter().enumerate() {
ensure!(value >= 0, NegativeDimensionSnafu { axis, value });
}
Ok(())
}
impl Default for DLTensor {
fn default() -> Self {
Self {
data: std::ptr::null_mut(),
device: DLDevice::default(),
ndim: 0,
dtype: DLDataType::default(),
shape: std::ptr::null_mut(),
strides: std::ptr::null_mut(),
byte_offset: 0,
}
}
}
impl DLTensor {
pub unsafe fn shape(&self) -> Result<&[i64], Error> {
ensure!(self.ndim >= 0, NegativeNdimSnafu { ndim: self.ndim });
if self.ndim == 0 {
return Ok(&[]);
}
ensure!(!self.shape.is_null(), NullShapePtrSnafu { ndim: self.ndim });
Ok(unsafe { std::slice::from_raw_parts(self.shape, self.ndim as usize) })
}
pub unsafe fn strides(&self) -> Result<Option<&[i64]>, Error> {
ensure!(self.ndim >= 0, NegativeNdimSnafu { ndim: self.ndim });
if self.strides.is_null() || self.ndim == 0 {
return Ok(None);
}
Ok(Some(unsafe {
std::slice::from_raw_parts(self.strides, self.ndim as usize)
}))
}
pub unsafe fn strides_or_compact(&self) -> Result<Cow<'_, [i64]>, Error> {
match unsafe { self.strides()? } {
Some(strides) => Ok(Cow::Borrowed(strides)),
None => {
let shape = unsafe { self.shape()? };
if shape.is_empty() {
Ok(Cow::Borrowed(&[]))
} else {
Ok(Cow::Owned(compact_strides(shape)?))
}
}
}
}
pub unsafe fn num_elements(&self) -> Result<usize, Error> {
let shape = unsafe { self.shape()? };
validate_shape_dimensions(shape)?;
shape.iter().try_fold(1usize, |acc, &dim| {
acc.checked_mul(dim as usize)
.ok_or(Error::NumElementsOverflow)
})
}
pub unsafe fn num_bytes(&self) -> Result<usize, Error> {
let bits_per_element = (self.dtype.bits as usize)
.checked_mul(self.dtype.lanes as usize)
.ok_or(Error::NumBytesOverflow)?;
let total_bits = unsafe { self.num_elements()? }
.checked_mul(bits_per_element)
.ok_or(Error::NumBytesOverflow)?;
Ok(total_bits.div_ceil(8))
}
#[inline]
pub unsafe fn is_compact(&self) -> Result<bool, Error> {
is_compact_strides(unsafe { self.shape()? }, unsafe { self.strides()? })
}
}