pub trait TensorTrait<T>: Send + Sync{
Show 21 methods
// Required methods
fn new(shape: &[usize], name: Option<&str>) -> Result<Self>
where Self: Sized;
fn from_fd(fd: OwnedFd, shape: &[usize], name: Option<&str>) -> Result<Self>
where Self: Sized;
fn clone_fd(&self) -> Result<OwnedFd>;
fn memory(&self) -> TensorMemory;
fn name(&self) -> String;
fn shape(&self) -> &[usize];
fn reshape(&mut self, shape: &[usize]) -> Result<()>;
fn capacity_bytes(&self) -> usize;
fn set_logical_shape(&mut self, shape: &[usize]) -> Result<()>;
fn map_with<'a>(&self, access: CpuAccess) -> Result<HostView<'a, T>>
where T: 'a;
fn buffer_identity(&self) -> &BufferIdentity;
// Provided methods
fn len(&self) -> usize { ... }
fn is_empty(&self) -> bool { ... }
fn size(&self) -> usize { ... }
fn map<'a>(&self) -> Result<HostView<'a, T>>
where T: 'a { ... }
fn map_read<'a>(&self) -> Result<HostView<'a, T>>
where T: 'a { ... }
fn map_write<'a>(&self) -> Result<HostView<'a, T>>
where T: 'a { ... }
fn map_mut<'a>(&self) -> Result<HostView<'a, T>>
where T: 'a { ... }
fn try_map_with<'a>(&self, access: CpuAccess) -> Result<HostView<'a, T>>
where T: 'a { ... }
fn try_map<'a>(&self) -> Result<HostView<'a, T>>
where T: 'a { ... }
fn view(&self, offset_bytes: usize, shape: &[usize]) -> Result<Self>
where Self: Sized { ... }
}Expand description
The operations every memory backend implements.
This is the seam that lets Tensor<T> hide which backend is in play.
The implementors are DmaTensor (Linux DMA-BUF), IoSurfaceTensor
(macOS/iOS), AHardwareBufferTensor (Android), ShmTensor, MemTensor,
and PboTensor; TensorStorage<T> dispatches to whichever is active.
Backend types are not public — allocate a Tensor<T> or TensorDyn
and call these methods through it.
Import the trait to get shape, size, map, clone_fd,
buffer_identity, and the zero-copy sub-region view on a tensor value.
Tensor::view and Tensor::batch route through TensorTrait::view, so
each backend’s identity-sharing rule lives in exactly one place.
Required Methods§
Sourcefn new(shape: &[usize], name: Option<&str>) -> Result<Self>where
Self: Sized,
fn new(shape: &[usize], name: Option<&str>) -> Result<Self>where
Self: Sized,
Create a new tensor with the given shape and optional name. If no name is given, a random name will be generated.
Sourcefn from_fd(fd: OwnedFd, shape: &[usize], name: Option<&str>) -> Result<Self>where
Self: Sized,
fn from_fd(fd: OwnedFd, shape: &[usize], name: Option<&str>) -> Result<Self>where
Self: Sized,
Import an existing buffer as a tensor, taking ownership of its file descriptor. The buffer is adopted in place — no bytes are copied.
The backend is detected, not chosen: the fd already belongs to a
buffer type, and this call’s job is to recognize which. On Linux that
is decided by the fd’s filesystem magic, which is stable UAPI
(include/uapi/linux/magic.h):
| Filesystem | Magic | Resulting TensorMemory |
|---|---|---|
dma_buf | DMA_BUF_MAGIC (0x444d4142) | TensorMemory::DmaBuf |
tmpfs (/dev/shm and memfd) | TMPFS_MAGIC (0x01021994) | TensorMemory::Shm |
| anything else | — | rejected — see Errors |
Both supported types are identified positively. An unrecognized filesystem is an error, never a fallback to shared memory: the wrong branch does not fail loudly (a DMA-BUF is mmap-able, so it would import as a perfectly functional tensor that merely isn’t DMA, and a pipe would import as a zero-length one), so guessing would trade a clear error here for silent loss of zero-copy far downstream.
The device number is deliberately not consulted. dma_buf files
live on an internal kernel mount whose st_dev comes from
get_anon_bdev() — an IDA shared with every other anonymous
pseudo-filesystem and allocated in boot order — so the minor a
DMA-BUF lands on varies by kernel build and is not part of any ABI.
On non-Linux Unix (macOS/iOS/Android) there is no fd-based DMA import;
the fd is always adopted as TensorMemory::Shm.
§Arguments
fd- Owned descriptor for the buffer to import. Ownership transfers to the returned tensor and the fd is closed on drop; passclone_fdoutput to keep your own handle.shape- Logical shape to interpret the buffer with. Must describe no more elements than the buffer holds.name- Optional name; a random one is generated whenNone.
§Returns
A tensor sharing the imported buffer’s memory, whose
memory() reports the detected backend.
§Errors
Error::UnknownBufferType- the fd is on a filesystem that is neitherdma_bufnortmpfs, so its buffer type cannot be determined. Carries the observedfstatfsmagic as au32, normalized so it can be looked up inmagic.hdirectly on both 32- and 64-bit targets. Typical causes: a regular file, a pipe or socket, or aMFD_HUGETLBmemfd (hugetlbfs, not tmpfs). Linux only.Error::UnknownDeviceType- the fd’sst_devmajor is non-zero, i.e. it lives on a real block device rather than an anonymous or in-memory filesystem. Linux only.Error::InvalidSize-shapeis empty or describes zero elements.Error::NixError-fstat,fstatfs, ormmapfailed on the descriptor.
§Examples
use edgefirst_tensor::{Tensor, TensorMemory, TensorTrait};
let src = Tensor::<u8>::new(&[480, 640, 3], Some(TensorMemory::DmaBuf), None)?;
// Round-tripping a DMA-BUF fd preserves the backend — the import is
// still zero-copy, and still eligible for GPU/NPU paths.
let imported = Tensor::<u8>::from_fd(src.clone_fd()?, src.shape(), None)?;
assert_eq!(imported.memory(), TensorMemory::DmaBuf);Sourcefn memory(&self) -> TensorMemory
fn memory(&self) -> TensorMemory
Get the memory type of this tensor.
Sourcefn reshape(&mut self, shape: &[usize]) -> Result<()>
fn reshape(&mut self, shape: &[usize]) -> Result<()>
Reshape this tensor to the given shape. The total number of elements must remain the same.
Sourcefn capacity_bytes(&self) -> usize
fn capacity_bytes(&self) -> usize
Bytes of the underlying allocation (>= the current logical size()).
Sourcefn set_logical_shape(&mut self, shape: &[usize]) -> Result<()>
fn set_logical_shape(&mut self, shape: &[usize]) -> Result<()>
Set the logical shape to any shape whose byte size fits the allocation
capacity, without the equal-size constraint of reshape.
Sourcefn map_with<'a>(&self, access: CpuAccess) -> Result<HostView<'a, T>>where
T: 'a,
fn map_with<'a>(&self, access: CpuAccess) -> Result<HostView<'a, T>>where
T: 'a,
Map the tensor into memory with the given access direction and return a TensorMap for accessing the data.
access selects the platform mapping mode (read-only IOSurface
lock, dma-buf sync direction, AHardwareBuffer lock usage) and the
map’s mutability: a map obtained with CpuAccess::Read rejects
as_mut_slice. CpuAccess::None is not a mappable direction
and returns Error::InvalidArgument.
Prefer the typed wrappers map_read /
map_write / map_mut.
Map for CPU access.
Like pin_host, 'a bounds how long the backing
allocation lives rather than borrowing the tensor: the returned view
shares ownership through the pin’s keepalive, so a map can coexist with
a &mut on the same tensor.
§Windows D3D11 textures: a write-only map of a partial window
A CpuAccess::Write map is not refreshed from the texture first, so
the bytes the caller leaves untouched are undefined – the documented
contract a whole-tensor map can meet and a sub-view cannot, since the
window spans its own rows only.
A tensor allocated CpuAccess::Write (no staging texture) publishes
only the window’s own rows on unmap, so a sub-view – a
view, or a shape narrowed with set_logical_shape
– is refused only when its window is not a whole, pitch-aligned row
range (a plain-offset view always is; an arbitrary
byte offset from set_plane_offset might not be), which fails with
Error::InvalidArgument.
A tensor allocated CpuAccess::Read or CpuAccess::ReadWrite
(staging texture) has no such per-window publish: unmapping always
copies the whole staging texture back, so a write-only sub-view is
refused with Error::PartialWriteRequiresReadWrite, naming
CpuAccess::ReadWrite as the same window with the refresh that
makes the untouched rows well-defined – and, unlike the
CpuAccess::Write case above, that retry always succeeds.
Sourcefn buffer_identity(&self) -> &BufferIdentity
fn buffer_identity(&self) -> &BufferIdentity
Get the buffer identity for cache keying and liveness tracking.
Provided Methods§
Sourcefn map<'a>(&self) -> Result<HostView<'a, T>>where
T: 'a,
fn map<'a>(&self) -> Result<HostView<'a, T>>where
T: 'a,
Map the tensor read-write (equivalent to
map_with(CpuAccess::ReadWrite) — the historical map()
behavior).
Sourcefn map_read<'a>(&self) -> Result<HostView<'a, T>>where
T: 'a,
fn map_read<'a>(&self) -> Result<HostView<'a, T>>where
T: 'a,
Map the tensor for CPU reading only. The returned map rejects
as_mut_slice; on macOS this takes the read-only IOSurface lock
(skips the unlock flush), on Linux the dma-buf read-direction
sync.
Sourcefn map_write<'a>(&self) -> Result<HostView<'a, T>>where
T: 'a,
fn map_write<'a>(&self) -> Result<HostView<'a, T>>where
T: 'a,
Map the tensor for CPU writing (fill-only: reading through a write map may see write-combined memory — do not read the slice).
Sourcefn map_mut<'a>(&self) -> Result<HostView<'a, T>>where
T: 'a,
fn map_mut<'a>(&self) -> Result<HostView<'a, T>>where
T: 'a,
Map the tensor read-write (alias of map with the
intent spelled out).
Sourcefn try_map_with<'a>(&self, access: CpuAccess) -> Result<HostView<'a, T>>where
T: 'a,
fn try_map_with<'a>(&self, access: CpuAccess) -> Result<HostView<'a, T>>where
T: 'a,
Non-blocking map_with: returns
Err(Error::IoError(WouldBlock)) while a GPU copy the map depends on
is still in flight, and makes progress on a retry.
Backings with no such copy (host memory, shared memory, dma-buf,
IOSurface, AHardwareBuffer, PBO) alias map_with, so a caller can use
this unconditionally and only ever see WouldBlock where the wait is
real – today the Windows D3D11 texture’s staging refresh.
A retry loop must yield or sleep between attempts. On the WARP software adapter the CPU threads are the GPU, so a tight loop starves the very copy it is waiting for.
CpuAccess::Write is refused on a Windows D3D11 texture tensor whose
window is shorter than its backing, exactly as it is for
map_with; see that method.
Sourcefn try_map<'a>(&self) -> Result<HostView<'a, T>>where
T: 'a,
fn try_map<'a>(&self) -> Result<HostView<'a, T>>where
T: 'a,
Non-blocking map: read-write, and WouldBlock rather
than a stall. See try_map_with.
Sourcefn view(&self, offset_bytes: usize, shape: &[usize]) -> Result<Self>where
Self: Sized,
fn view(&self, offset_bytes: usize, shape: &[usize]) -> Result<Self>where
Self: Sized,
Create a zero-copy sub-region view of this backing that shares the
underlying allocation and BufferIdentity.
The window is [offset_bytes, offset_bytes + shape.product() * size_of::<T>()) measured from this tensor’s own logical start, so a
sub-view of a sub-view composes by adding offsets. Sharing the parent’s
identity is the contract that lets identity-keyed caches (e.g. the GL
EGLImage import cache) treat offset-distinct windows as one buffer rather
than unrelated allocations — view must never mint a fresh identity.
Defaults to Error::NotImplemented; every backend that supports
sub-views overrides it (Mem, Shm, Linux DMA, macOS IOSurface, Pbo).
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".