pub trait TensorTrait<T>: Send + Sync{
Show 27 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 cpu_access(&self) -> CpuAccess { ... }
fn view_origin(&self) -> Option<ViewOrigin> { ... }
fn is_view(&self) -> bool { ... }
fn write_access(&self) -> CpuAccess { ... }
fn map_for_write<'a>(&self) -> Result<HostView<'a, T>>
where T: 'a { ... }
fn map_for_partial_write<'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; a direction the tensor’s
declared CpuAccess does not cover returns
Error::CpuAccessDenied.
Prefer the typed wrappers map_read /
map_write / map_mut.
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)).
Fails with Error::CpuAccessDenied on a Read- or Write-declared
tensor: use map_read to read one and
map_for_write to fill one.
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. Fails with Error::CpuAccessDenied on a Write- or
None-declared tensor.
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).
Valid only for a pass that overwrites the whole mapping; see
write_access. Fails with
Error::CpuAccessDenied on a Read- or None-declared tensor.
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); refused on the same declarations.
Sourcefn cpu_access(&self) -> CpuAccess
fn cpu_access(&self) -> CpuAccess
The CPU access declared for this tensor (see CpuAccess). A bare
backing carries no declaration of its own and reports ReadWrite.
Sourcefn view_origin(&self) -> Option<ViewOrigin>
fn view_origin(&self) -> Option<ViewOrigin>
The parent-image snapshot when this tensor is a sub-region of a larger
buffer (see ViewOrigin); a bare backing reports None.
Sourcefn is_view(&self) -> bool
fn is_view(&self) -> bool
Whether this tensor addresses part of a larger buffer (see
Tensor::is_view); a bare backing reports false.
Sourcefn write_access(&self) -> CpuAccess
fn write_access(&self) -> CpuAccess
The direction a CPU pass that overwrites every element of this tensor maps with.
A write-only map does not invalidate the CPU cache (on arm64,
DMA_BUF_SYNC_START|WRITE only orders), so a stale cache line the pass
writes only partly is written back over whatever a device left in the
rest of it. The map may therefore be Write only when the pass covers
the whole buffer: a whole tensor takes its declaration plus Write
(Write on a write-only declaration), a view, whose
rows share cache lines with bytes outside it, takes ReadWrite.
Sourcefn map_for_write<'a>(&self) -> Result<HostView<'a, T>>where
T: 'a,
fn map_for_write<'a>(&self) -> Result<HostView<'a, T>>where
T: 'a,
Map for a CPU pass that overwrites every element, in
write_access’s direction.
§Errors
Error::CpuAccessDenied when the declaration does not cover that
direction, otherwise whatever map_with returns.
Sourcefn map_for_partial_write<'a>(&self) -> Result<HostView<'a, T>>where
T: 'a,
fn map_for_partial_write<'a>(&self) -> Result<HostView<'a, T>>where
T: 'a,
Map for a CPU pass that writes only part of the buffer and keeps the
rest (a letterbox border around a device-rendered interior): always
ReadWrite, see write_access.
§Errors
Error::CpuAccessDenied unless the declaration is ReadWrite,
otherwise whatever map_with returns.
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".