use super::{Borrowed, Copied, Error, storage::try_copy};
use crate::{ManagedTensorBase, OpaqueContext, allocation::fixed};
#[derive(Debug, Clone, Copy)]
pub struct Fixed<const N: usize, Shape, Strides> {
shape: Shape,
strides: Strides,
}
impl<const N: usize, Shape, Strides> Fixed<N, Shape, Strides> {
pub fn new(shape: Shape, strides: Strides) -> Self {
Self { shape, strides }
}
}
pub struct PreparedFixed<M, const N: usize, Shape, Strides>
where
M: ManagedTensorBase,
Shape: fixed::Storage<N>,
Strides: fixed::Storage<N>,
{
allocation: fixed::Allocation<M, N, Shape, Strides>,
shape: *mut i64,
strides: *mut i64,
}
impl<M, const N: usize, Shape, Strides> PreparedFixed<M, N, Shape, Strides>
where
M: ManagedTensorBase,
Shape: fixed::Storage<N>,
Strides: fixed::Storage<N>,
{
pub fn initialize<C: OpaqueContext>(self, ctx: C) -> fixed::Initialized<M, N, Shape, Strides> {
let Self {
allocation,
shape,
strides,
} = self;
let mut initialized = allocation.initialize(ctx);
initialized.tensor_mut().shape = shape;
initialized.tensor_mut().strides = strides;
initialized
}
}
#[doc(hidden)]
pub trait FixedPart<const N: usize> {
type Storage: fixed::Storage<N>;
fn write(
self,
storage: &mut <Self::Storage as fixed::Storage<N>>::Value,
) -> Result<*mut i64, usize>;
}
#[doc(hidden)]
pub trait OwnedFixedPart<const N: usize>: FixedPart<N> {}
impl<T, const N: usize> FixedPart<N> for Copied<[T; N]>
where
T: Copy + TryInto<i64> + 'static,
{
type Storage = fixed::Copied;
fn write(self, storage: &mut [i64; N]) -> Result<*mut i64, usize> {
unsafe { try_copy(&self.0, storage.as_mut_ptr())? };
Ok(storage.as_mut_ptr())
}
}
impl<T, const N: usize> OwnedFixedPart<N> for Copied<[T; N]> where T: Copy + TryInto<i64> + 'static {}
impl<T, const N: usize> FixedPart<N> for Copied<&[T; N]>
where
T: Copy + TryInto<i64> + 'static,
{
type Storage = fixed::Copied;
fn write(self, storage: &mut [i64; N]) -> Result<*mut i64, usize> {
unsafe { try_copy(self.0, storage.as_mut_ptr())? };
Ok(storage.as_mut_ptr())
}
}
impl<T, const N: usize> OwnedFixedPart<N> for Copied<&[T; N]> where T: Copy + TryInto<i64> + 'static {}
impl<const N: usize> FixedPart<N> for Borrowed<&[i64; N]> {
type Storage = fixed::Borrowed;
fn write(self, _: &mut ()) -> Result<*mut i64, usize> {
Ok(self.0.as_ptr().cast_mut())
}
}
impl<const N: usize, Shape, Strides> Fixed<N, Shape, Strides>
where
Shape: FixedPart<N>,
Strides: FixedPart<N>,
{
fn prepare_inner<M>(
self,
) -> Result<PreparedFixed<M, N, Shape::Storage, Strides::Storage>, Error>
where
M: ManagedTensorBase,
{
let mut allocation =
fixed::Allocation::<M, N, Shape::Storage, Strides::Storage>::allocate()?;
let shape = self
.shape
.write(allocation.shape_storage_mut())
.map_err(|axis| Error::ShapeValueOverflow { axis })?;
let strides = self
.strides
.write(allocation.strides_storage_mut())
.map_err(|axis| Error::StrideValueOverflow { axis })?;
Ok(PreparedFixed {
allocation,
shape,
strides,
})
}
pub unsafe fn prepare_unchecked<M>(
self,
) -> Result<PreparedFixed<M, N, Shape::Storage, Strides::Storage>, Error>
where
M: ManagedTensorBase,
{
self.prepare_inner()
}
}
impl<const N: usize, Shape, Strides> Fixed<N, Shape, Strides>
where
Shape: OwnedFixedPart<N>,
Strides: OwnedFixedPart<N>,
{
pub fn prepare<M>(self) -> Result<PreparedFixed<M, N, Shape::Storage, Strides::Storage>, Error>
where
M: ManagedTensorBase,
{
self.prepare_inner()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ffi::DLManagedTensor;
#[test]
fn copied_metadata_uses_inline_arrays() {
let prepared = Fixed::new(Copied([2_u32, 3]), Copied([3_isize, 1]))
.prepare::<DLManagedTensor>()
.unwrap();
let initialized = prepared.initialize(Box::new(()));
let tensor = unsafe { initialized.finish() };
assert_eq!(tensor.validate().unwrap().shape(), &[2, 3]);
assert_eq!(tensor.validate().unwrap().strides().unwrap(), &[3, 1]);
}
#[test]
fn borrowed_shape_allocates_only_strides() {
let shape = [2_i64, 3];
let prepared = unsafe {
Fixed::new(Borrowed(&shape), Copied([3_i64, 1]))
.prepare_unchecked::<DLManagedTensor>()
.unwrap()
};
let initialized = prepared.initialize(Box::new(()));
let tensor = unsafe { initialized.finish() };
assert_eq!(tensor.validate().unwrap().shape(), &shape);
assert_eq!(tensor.validate().unwrap().strides().unwrap(), &[3, 1]);
}
}