1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5pub enum MemoryLayout {
6 C,
8 Fortran,
10 Custom,
12}
13
14impl MemoryLayout {
15 #[inline]
17 #[must_use]
18 pub fn is_c_contiguous(self) -> bool {
19 self == Self::C
20 }
21
22 #[inline]
24 #[must_use]
25 pub fn is_f_contiguous(self) -> bool {
26 self == Self::Fortran
27 }
28
29 #[inline]
31 #[must_use]
32 pub fn is_custom(self) -> bool {
33 self == Self::Custom
34 }
35}
36
37impl core::fmt::Display for MemoryLayout {
38 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
39 match self {
40 Self::C => write!(f, "C"),
41 Self::Fortran => write!(f, "F"),
42 Self::Custom => write!(f, "Custom"),
43 }
44 }
45}
46
47#[cfg(feature = "std")]
53#[inline]
54pub(crate) fn classify_layout(
55 is_standard: bool,
56 shape: &[usize],
57 strides: &[isize],
58) -> MemoryLayout {
59 if is_standard {
60 MemoryLayout::C
61 } else {
62 detect_layout(shape, strides)
63 }
64}
65
66#[cfg(feature = "std")]
68pub(crate) fn detect_layout(shape: &[usize], strides: &[isize]) -> MemoryLayout {
69 if shape.is_empty() {
70 return MemoryLayout::C; }
72
73 let is_c = is_c_contiguous(shape, strides);
74 let is_f = is_f_contiguous(shape, strides);
75
76 if is_c {
77 MemoryLayout::C
78 } else if is_f {
79 MemoryLayout::Fortran
80 } else {
81 MemoryLayout::Custom
82 }
83}
84
85#[cfg(feature = "std")]
86fn is_c_contiguous(shape: &[usize], strides: &[isize]) -> bool {
87 if shape.len() != strides.len() {
88 return false;
89 }
90 let ndim = shape.len();
91 if ndim == 0 {
92 return true;
93 }
94 let mut expected: isize = 1;
95 for i in (0..ndim).rev() {
96 if shape[i] == 0 {
97 return true; }
99 if shape[i] != 1 && strides[i] != expected {
100 return false;
101 }
102 expected = strides[i] * shape[i] as isize;
103 }
104 true
105}
106
107#[cfg(feature = "std")]
108fn is_f_contiguous(shape: &[usize], strides: &[isize]) -> bool {
109 if shape.len() != strides.len() {
110 return false;
111 }
112 let ndim = shape.len();
113 if ndim == 0 {
114 return true;
115 }
116 let mut expected: isize = 1;
117 for i in 0..ndim {
118 if shape[i] == 0 {
119 return true; }
121 if shape[i] != 1 && strides[i] != expected {
122 return false;
123 }
124 expected = strides[i] * shape[i] as isize;
125 }
126 true
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn detect_c_contiguous() {
135 assert_eq!(detect_layout(&[3, 4], &[4, 1]), MemoryLayout::C);
137 }
138
139 #[test]
140 fn detect_f_contiguous() {
141 assert_eq!(detect_layout(&[3, 4], &[1, 3]), MemoryLayout::Fortran);
143 }
144
145 #[test]
146 fn detect_custom() {
147 assert_eq!(detect_layout(&[3, 4], &[8, 2]), MemoryLayout::Custom);
149 }
150
151 #[test]
152 fn detect_empty() {
153 assert_eq!(detect_layout(&[], &[]), MemoryLayout::C);
154 }
155
156 #[test]
157 fn display() {
158 assert_eq!(MemoryLayout::C.to_string(), "C");
159 assert_eq!(MemoryLayout::Fortran.to_string(), "F");
160 assert_eq!(MemoryLayout::Custom.to_string(), "Custom");
161 }
162}