Skip to main content

LayoutDescriptor

Enum LayoutDescriptor 

Source
#[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, and Morton are 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.
  • Unknown is 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
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

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

Source

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);
Source

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:

LayoutBuffer count
RowMajor, ColMajor, Strided, Tiled, Morton, Hilbert1
COO2
CSR, CSC, BlockPaged3
CSF2·rank+1 (rank-dependent; embedded in mode_order.len())
Composite0, not NonZeroU8-representable — reported as None
PrivateExtension, UnknownNone (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 + n buffers, matching the spec’s buffer_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);
Source

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());
Source

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
LayoutConstraint
Stridedstrides.len() == shape.rank()
Tiledtile_shape.len() == shape.rank(); each tile_shape[k] > 0
Mortonmorton_bits.len() == shape.rank(); shape[k] <= 2^morton_bits[k] for all static dims
Hilberthilbert_rank == shape.rank(); hilbert_rank >= 2; each static dim == 2^hilbert_order
Cooshape.rank() >= 1 (no scalar sparse tensors)
Csrshape.rank() == 2
Cscshape.rank() == 2
Csfshape.rank() >= 3; mode_order.len() == shape.rank(); mode_order is a permutation of 0..rank
Unknownalways 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());
Source

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

Source§

fn clone(&self) -> LayoutDescriptor

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for LayoutDescriptor

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Eq for LayoutDescriptor

Source§

impl Hash for LayoutDescriptor

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl PartialEq for LayoutDescriptor

Source§

fn eq(&self, other: &LayoutDescriptor) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl StructuralPartialEq for LayoutDescriptor

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.