#[non_exhaustive]pub enum LayoutDescriptor {
Show 14 variants
RowMajor,
ColMajor,
Strided(StridedLayout),
Tiled(Box<TiledLayout>),
Morton(MortonLayout),
Coo(CooLayout),
Csr(CsrLayout),
Csc(CscLayout),
Csf(CsfLayout),
BlockPaged(BlockPagedLayout),
Composite(CompositeLayout),
Hilbert(HilbertLayout),
PrivateExtension(PrivateExtensionLayout),
Unknown(UnknownLayout),
}Expand description
The memory layout of a tensor’s data buffer.
Every variant corresponds to a layout tag value defined in the Hurray spec
(docs/spec/memory-layout.md). The discriminant IS the tag byte; call
LayoutDescriptor::tag to retrieve it.
The enum is #[non_exhaustive] so that future spec versions can add new
named layouts without breaking existing match arms in downstream crates.
§Construction
RowMajor,ColMajor, andMortonare unit variants — construct them directly (LayoutDescriptor::RowMajor). No separate constructor is needed because they carry no configurable fields.- All other variants wrap a payload struct. Construct the struct via its own
new()method (which validates invariants), then wrap it. Unknownis the permissive-mode fallback and is not produced by any named-variant path.
§Examples
use hurray_core::layout::{LayoutDescriptor, StridedLayout, CooLayout};
// Unit variant — no constructor needed.
let rm = LayoutDescriptor::RowMajor;
assert_eq!(rm.tag(), 0x01);
// Strided layout with explicit strides.
let strided = LayoutDescriptor::Strided(StridedLayout::new(vec![4, 1]));
assert_eq!(strided.tag(), 0x03);
// Sparse COO layout.
let coo = LayoutDescriptor::Coo(CooLayout::new(42, true));
assert_eq!(coo.tag(), 0x06);
assert_eq!(coo.buffer_count().map(|n| n.get()), Some(2));Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
RowMajor
Row-major (C-order) layout. Tag 0x01.
ColMajor
Column-major (Fortran-order) layout. Tag 0x02.
Strided(StridedLayout)
Strided layout with explicit per-dimension strides. Tag 0x03.
Tiled(Box<TiledLayout>)
Tiled / blocked layout. Tag 0x04.
Boxed because TiledLayout is recursive (inner tiles) and would
otherwise make the enum size unbounded.
Morton(MortonLayout)
Morton (Z-order curve) layout. Tag 0x05.
Coo(CooLayout)
COO (Coordinate) sparse layout. Tag 0x06. Buffer count = 2.
Csr(CsrLayout)
CSR (Compressed Sparse Row) layout. Tag 0x07. Buffer count = 3.
Csc(CscLayout)
CSC (Compressed Sparse Column) layout. Tag 0x08. Buffer count = 3.
Csf(CsfLayout)
CSF (Compressed Sparse Fiber) layout. Tag 0x09. Buffer count = 2·rank+1.
The rank-N generalisation of CSR/CSC, storing a sparse tensor as a tree of
rank levels. Only valid for rank ≥ 3. Buffer count is rank-dependent because
each level contributes one pos and one crd buffer plus a single values buffer.
See docs/spec/layouts/csf.md.
BlockPaged(BlockPagedLayout)
Block-paged indirect layout. Tag 0x0A. Buffer count = 3 (+ optional quant buffers).
Stores a KV-cache tensor whose paged axis is divided into fixed-size pages drawn from a shared page pool, with a block table mapping logical page positions to physical page IDs. Designed for PagedAttention KV caches.
See docs/spec/layouts/block-paged.md.
Composite(CompositeLayout)
Composite / Virtual head. Tag 0x0B. Owns no data (buffer_count = 0).
The head presents a single logical shape/type_tag view over an ordered
set of member tensor descriptors bound by forward stream adjacency (see
crate::composite::CompositeTensor). This is a new addressing category,
Virtual, alongside Dense, Sparse, and Indirect.
See docs/spec/layouts/composite.md.
Hilbert(HilbertLayout)
Hilbert curve layout. Tag 0x40.
PrivateExtension(PrivateExtensionLayout)
Implementation-private extension layout. Tags 0xF0–0xFE.
Unknown(UnknownLayout)
Unrecognized layout accepted in permissive mode.
A strict-mode reader MUST NOT produce this variant. A permissive-mode reader MAY produce it for tags not covered by any named variant above, but MUST NOT dereference or interpret the associated tensor data buffer.
Implementations§
Source§impl LayoutDescriptor
impl LayoutDescriptor
Sourcepub fn tag(&self) -> u8
pub fn tag(&self) -> u8
Returns the wire tag byte for this layout descriptor.
The tag is the discriminant used in the binary encoding. It is always a
single uint8 value.
§Examples
use hurray_core::layout::{CooLayout, CsfLayout, LayoutDescriptor, UnknownLayout};
assert_eq!(LayoutDescriptor::RowMajor.tag(), 0x01);
assert_eq!(LayoutDescriptor::ColMajor.tag(), 0x02);
assert_eq!(LayoutDescriptor::Coo(CooLayout::new(0, false)).tag(), 0x06);
assert_eq!(LayoutDescriptor::Csf(CsfLayout::new(0, vec![0, 1, 2])).tag(), 0x09);
// Unknown passthrough (with a reserved tag outside the named set).
let u = LayoutDescriptor::Unknown(UnknownLayout::new(0x0C, vec![]).unwrap());
assert_eq!(u.tag(), 0x0C);Sourcepub fn buffer_count(&self) -> Option<NonZeroU8>
pub fn buffer_count(&self) -> Option<NonZeroU8>
Returns the number of data buffers this layout requires, or None if the
count is either not statically known (LayoutDescriptor::Unknown,
LayoutDescriptor::PrivateExtension) or is a virtual “known zero”
(LayoutDescriptor::Composite — use LayoutDescriptor::is_virtual to
tell the two apart).
Dense layouts always require 1 buffer. Sparse layouts require a fixed number of component buffers as defined per format:
| Layout | Buffer count |
|---|---|
| RowMajor, ColMajor, Strided, Tiled, Morton, Hilbert | 1 |
| COO | 2 |
| CSR, CSC, BlockPaged | 3 |
| CSF | 2·rank+1 (rank-dependent; embedded in mode_order.len()) |
| Composite | 0, not NonZeroU8-representable — reported as None |
| PrivateExtension, Unknown | None (genuinely unknown) |
Note: This is the layout’s minimum buffer count, not the total size of the tensor descriptor’s buffer table. Quantization-parameter buffers are counted separately and appended after the layout’s own buffers (so a quantized block-paged tensor has
3 + nbuffers, matching the spec’sbuffer_count >= 3).
§Examples
use std::num::NonZeroU8;
use hurray_core::layout::{CooLayout, CsrLayout, CsfLayout, LayoutDescriptor, UnknownLayout};
assert_eq!(LayoutDescriptor::RowMajor.buffer_count(), NonZeroU8::new(1));
assert_eq!(
LayoutDescriptor::Coo(CooLayout::new(0, false)).buffer_count(),
NonZeroU8::new(2),
);
assert_eq!(
LayoutDescriptor::Csr(CsrLayout::new(0)).buffer_count(),
NonZeroU8::new(3),
);
// CSF rank-3: 2*3+1 = 7 buffers.
assert_eq!(
LayoutDescriptor::Csf(CsfLayout::new(0, vec![0, 1, 2])).buffer_count(),
NonZeroU8::new(7),
);
let u = LayoutDescriptor::Unknown(UnknownLayout::new(0x0C, vec![]).unwrap());
assert_eq!(u.buffer_count(), None);Sourcepub fn is_virtual(&self) -> bool
pub fn is_virtual(&self) -> bool
Returns true if this layout is virtual (owns no data buffer).
Only LayoutDescriptor::Composite is virtual. This lets callers
distinguish “buffer count is None because the layout is virtual (a
known zero, per spec)” from “buffer count is None because it is not
statically known” (LayoutDescriptor::Unknown,
LayoutDescriptor::PrivateExtension) — both report None from
LayoutDescriptor::buffer_count, but only the former is genuinely zero.
§Examples
use hurray_core::layout::{CompositeLayout, CompositionRule, LayoutDescriptor};
let composite = LayoutDescriptor::Composite(
CompositeLayout::new(CompositionRule::Group, 0).unwrap(),
);
assert!(composite.is_virtual());
assert!(!LayoutDescriptor::RowMajor.is_virtual());Sourcepub fn validate_against_shape(&self, shape: &Shape) -> Result<()>
pub fn validate_against_shape(&self, shape: &Shape) -> Result<()>
Validates layout-specific constraints against the provided tensor shape.
This method is called by Layer 4 (tensor descriptor) to check that the layout descriptor is consistent with the tensor’s rank and dimension sizes. Constructors do not call this method because shape is not available at layout-descriptor construction time.
§Checked constraints
| Layout | Constraint |
|---|---|
Strided | strides.len() == shape.rank() |
Tiled | tile_shape.len() == shape.rank(); each tile_shape[k] > 0 |
Morton | morton_bits.len() == shape.rank(); shape[k] <= 2^morton_bits[k] for all static dims |
Hilbert | hilbert_rank == shape.rank(); hilbert_rank >= 2; each static dim == 2^hilbert_order |
Coo | shape.rank() >= 1 (no scalar sparse tensors) |
Csr | shape.rank() == 2 |
Csc | shape.rank() == 2 |
Csf | shape.rank() >= 3; mode_order.len() == shape.rank(); mode_order is a permutation of 0..rank |
Unknown | always Ok(()) |
§Errors
Returns Error::InvalidLayout if any constraint is violated.
§Examples
use hurray_core::{Shape, layout::{LayoutDescriptor, StridedLayout}};
let shape = Shape::new(vec![3, 4]).unwrap();
// Correct rank.
let ok = LayoutDescriptor::Strided(StridedLayout::new(vec![4, 1]));
assert!(ok.validate_against_shape(&shape).is_ok());
// Wrong rank.
let bad = LayoutDescriptor::Strided(StridedLayout::new(vec![1]));
assert!(bad.validate_against_shape(&shape).is_err());Sourcepub fn element_offset(&self, index: &[u64], shape: &Shape) -> Result<u64>
pub fn element_offset(&self, index: &[u64], shape: &Shape) -> Result<u64>
Returns the linear element offset for a multi-dimensional logical index.
Dispatches to the layout-specific addressing::ElementAddress implementation.
For multi-buffer (sparse) layouts, returns Error::LayoutRequiresMultiBuffer —
use the layout-specific addressing method instead.
§Examples
use hurray_core::layout::LayoutDescriptor;
use hurray_core::Shape;
let shape = Shape::new(vec![3, 4]).unwrap();
// Row-major: element [1, 2] → offset 1*4 + 2 = 6.
assert_eq!(LayoutDescriptor::RowMajor.element_offset(&[1, 2], &shape).unwrap(), 6);
// Column-major: element [1, 2] → offset 1*1 + 2*3 = 7.
assert_eq!(LayoutDescriptor::ColMajor.element_offset(&[1, 2], &shape).unwrap(), 7);Trait Implementations§
Source§impl Clone for LayoutDescriptor
impl Clone for LayoutDescriptor
Source§fn clone(&self) -> LayoutDescriptor
fn clone(&self) -> LayoutDescriptor
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for LayoutDescriptor
impl Debug for LayoutDescriptor
impl Eq for LayoutDescriptor
Source§impl Hash for LayoutDescriptor
impl Hash for LayoutDescriptor
Source§impl PartialEq for LayoutDescriptor
impl PartialEq for LayoutDescriptor
Source§fn eq(&self, other: &LayoutDescriptor) -> bool
fn eq(&self, other: &LayoutDescriptor) -> bool
self and other values to be equal, and is used by ==.