Skip to main content

hurray_core/layout/
strided.rs

1//! Strided layout descriptor.
2//!
3//! Tag `0x03`. Carries an explicit per-dimension stride vector.
4//! See `docs/spec/layouts/strided.md`.
5
6/// Descriptor for the strided layout.
7///
8/// The strided layout generalises row-major and column-major by allowing an
9/// arbitrary stride per dimension. Negative strides (reversed dimension) and
10/// zero strides (broadcast dimension) are both valid.
11///
12/// `strides` are in **logical elements**, not bytes.
13///
14/// # Examples
15///
16/// ```
17/// use hurray_core::layout::{LayoutDescriptor, StridedLayout};
18///
19/// // Row-major strides for a 3×4 tensor: [4, 1]
20/// let layout = LayoutDescriptor::Strided(StridedLayout::new(vec![4, 1]));
21/// assert_eq!(layout.tag(), 0x03);
22/// ```
23#[derive(Debug, Clone, PartialEq, Eq, Hash)]
24#[non_exhaustive]
25pub struct StridedLayout {
26    /// Per-dimension strides in **logical elements**.
27    ///
28    /// Positive: forward; negative: reversed; zero: broadcast (virtual) dimension.
29    /// `strides.len()` MUST equal the tensor rank (validated by
30    /// [`LayoutDescriptor::validate_against_shape`]).
31    pub strides: Vec<i64>,
32}
33
34impl StridedLayout {
35    /// Creates a new [`StridedLayout`] with the given per-dimension strides.
36    ///
37    /// No rank validation is performed here — call
38    /// [`LayoutDescriptor::validate_against_shape`] to check that
39    /// `strides.len() == shape.rank()`.
40    ///
41    /// # Examples
42    ///
43    /// ```
44    /// use hurray_core::layout::StridedLayout;
45    ///
46    /// // Reversed first dimension of a 3×4 tensor (row-major with rows reversed).
47    /// let s = StridedLayout::new(vec![-4, 1]);
48    /// assert_eq!(s.strides, &[-4, 1]);
49    /// ```
50    pub fn new(strides: Vec<i64>) -> Self {
51        Self { strides }
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58    use crate::layout::LayoutDescriptor;
59
60    #[test]
61    fn strided_tag_is_0x03() {
62        let layout = LayoutDescriptor::Strided(StridedLayout::new(vec![4, 1]));
63        assert_eq!(layout.tag(), 0x03);
64    }
65
66    #[test]
67    fn strided_buffer_count_is_1() {
68        use std::num::NonZeroU8;
69        let layout = LayoutDescriptor::Strided(StridedLayout::new(vec![4, 1]));
70        assert_eq!(layout.buffer_count(), Some(NonZeroU8::new(1).unwrap()));
71    }
72
73    #[test]
74    fn strided_allows_negative_and_zero_strides() {
75        // Negative strides (reversed dim) and zero strides (broadcast) are valid.
76        let s = StridedLayout::new(vec![-4, 0, 1]);
77        assert_eq!(s.strides, [-4, 0, 1]);
78    }
79}