diff --git a/src/layout/bitset.rs b/src/layout/bitset.rs new file mode 100644 index 000000000..4b087ad35 --- /dev/null +++ b/src/layout/bitset.rs @@ -0,0 +1,271 @@ +//! Compact representations of array layouts. + +use alloc::fmt; + +// Layout is a bitset used for internal layout description of +// arrays, producers and sets of producers. +// The type is public but users don't interact with it. +#[doc(hidden)] +/// Memory layout description +#[derive(Copy, Clone)] +pub struct LayoutBitset(pub(super) u32); + +#[deprecated(since = "0.18.0", note = "Layout has been renamed to LayoutBitset")] +#[allow(dead_code)] +/// Memory layout description, deprecated. See [`LayoutBitset`] instead. +pub type Layout = LayoutBitset; + +impl LayoutBitset +{ + pub(crate) const CORDER: u32 = 0b01; + pub(crate) const FORDER: u32 = 0b10; + pub(crate) const CPREFER: u32 = 0b0100; + pub(crate) const FPREFER: u32 = 0b1000; + + #[inline(always)] + pub(crate) fn is(self, flag: u32) -> bool + { + self.0 & flag != 0 + } + + /// Return layout common to both inputs + #[inline(always)] + pub(crate) fn intersect(self, other: LayoutBitset) -> LayoutBitset + { + LayoutBitset(self.0 & other.0) + } + + /// Return a layout that simultaneously "is" what both of the inputs are + #[inline(always)] + pub(crate) fn also(self, other: LayoutBitset) -> LayoutBitset + { + LayoutBitset(self.0 | other.0) + } + + #[inline(always)] + pub(crate) fn one_dimensional() -> LayoutBitset + { + LayoutBitset::c().also(LayoutBitset::f()) + } + + #[inline(always)] + pub(crate) fn c() -> LayoutBitset + { + LayoutBitset(LayoutBitset::CORDER | LayoutBitset::CPREFER) + } + + #[inline(always)] + pub(crate) fn f() -> LayoutBitset + { + LayoutBitset(LayoutBitset::FORDER | LayoutBitset::FPREFER) + } + + #[inline(always)] + pub(crate) fn cpref() -> LayoutBitset + { + LayoutBitset(LayoutBitset::CPREFER) + } + + #[inline(always)] + pub(crate) fn fpref() -> LayoutBitset + { + LayoutBitset(LayoutBitset::FPREFER) + } + + #[inline(always)] + pub(crate) fn none() -> LayoutBitset + { + LayoutBitset(0) + } + + /// A simple "score" method which scores positive for preferring C-order, negative for F-order + /// Subject to change when we can describe other layouts + #[inline] + pub(crate) fn tendency(self) -> i32 + { + (self.is(LayoutBitset::CORDER) as i32 - self.is(LayoutBitset::FORDER) as i32) + + (self.is(LayoutBitset::CPREFER) as i32 - self.is(LayoutBitset::FPREFER) as i32) + } +} + +const LAYOUT_NAMES: &[&str] = &["C", "F", "c", "f"]; + +impl fmt::Debug for LayoutBitset +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + if self.0 == 0 { + write!(f, "Custom")? + } else { + (0..32).filter(|&i| self.is(1 << i)).try_fold((), |_, i| { + if let Some(name) = LAYOUT_NAMES.get(i) { + write!(f, "{}", name) + } else { + write!(f, "{:#x}", i) + } + })?; + }; + write!(f, " ({:#x})", self.0) + } +} + +#[cfg(test)] +mod tests +{ + use super::*; + use crate::imp_prelude::*; + use crate::NdProducer; + + type M = Array2; + type M1 = Array1; + type M0 = Array0; + + macro_rules! assert_layouts { + ($mat:expr, $($layout:ident),*) => {{ + let layout = $mat.view().layout(); + $( + assert!(layout.is(LayoutBitset::$layout), + "Assertion failed: array {:?} is not layout {}", + $mat, + stringify!($layout)); + )* + }}; + } + + macro_rules! assert_not_layouts { + ($mat:expr, $($layout:ident),*) => {{ + let layout = $mat.view().layout(); + $( + assert!(!layout.is(LayoutBitset::$layout), + "Assertion failed: array {:?} show not have layout {}", + $mat, + stringify!($layout)); + )* + }}; + } + + #[test] + fn contig_layouts() + { + let a = M::zeros((5, 5)); + let b = M::zeros((5, 5).f()); + let ac = a.view().layout(); + let af = b.view().layout(); + assert!(ac.is(LayoutBitset::CORDER) && ac.is(LayoutBitset::CPREFER)); + assert!(!ac.is(LayoutBitset::FORDER) && !ac.is(LayoutBitset::FPREFER)); + assert!(!af.is(LayoutBitset::CORDER) && !af.is(LayoutBitset::CPREFER)); + assert!(af.is(LayoutBitset::FORDER) && af.is(LayoutBitset::FPREFER)); + } + + #[test] + fn contig_cf_layouts() + { + let a = M::zeros((5, 1)); + let b = M::zeros((1, 5).f()); + assert_layouts!(a, CORDER, CPREFER, FORDER, FPREFER); + assert_layouts!(b, CORDER, CPREFER, FORDER, FPREFER); + + let a = M1::zeros(5); + let b = M1::zeros(5.f()); + assert_layouts!(a, CORDER, CPREFER, FORDER, FPREFER); + assert_layouts!(b, CORDER, CPREFER, FORDER, FPREFER); + + let a = M0::zeros(()); + assert_layouts!(a, CORDER, CPREFER, FORDER, FPREFER); + + let a = M::zeros((5, 5)); + let b = M::zeros((5, 5).f()); + let arow = a.slice(s![..1, ..]); + let bcol = b.slice(s![.., ..1]); + assert_layouts!(arow, CORDER, CPREFER, FORDER, FPREFER); + assert_layouts!(bcol, CORDER, CPREFER, FORDER, FPREFER); + + let acol = a.slice(s![.., ..1]); + let brow = b.slice(s![..1, ..]); + assert_not_layouts!(acol, CORDER, CPREFER, FORDER, FPREFER); + assert_not_layouts!(brow, CORDER, CPREFER, FORDER, FPREFER); + } + + #[test] + fn stride_layouts() + { + let a = M::zeros((5, 5)); + + { + let v1 = a.slice(s![1.., ..]).layout(); + let v2 = a.slice(s![.., 1..]).layout(); + + assert!(v1.is(LayoutBitset::CORDER) && v1.is(LayoutBitset::CPREFER)); + assert!(!v1.is(LayoutBitset::FORDER) && !v1.is(LayoutBitset::FPREFER)); + assert!(!v2.is(LayoutBitset::CORDER) && v2.is(LayoutBitset::CPREFER)); + assert!(!v2.is(LayoutBitset::FORDER) && !v2.is(LayoutBitset::FPREFER)); + } + + let b = M::zeros((5, 5).f()); + + { + let v1 = b.slice(s![1.., ..]).layout(); + let v2 = b.slice(s![.., 1..]).layout(); + + assert!(!v1.is(LayoutBitset::CORDER) && !v1.is(LayoutBitset::CPREFER)); + assert!(!v1.is(LayoutBitset::FORDER) && v1.is(LayoutBitset::FPREFER)); + assert!(!v2.is(LayoutBitset::CORDER) && !v2.is(LayoutBitset::CPREFER)); + assert!(v2.is(LayoutBitset::FORDER) && v2.is(LayoutBitset::FPREFER)); + } + } + + #[test] + fn no_layouts() + { + let a = M::zeros((5, 5)); + let b = M::zeros((5, 5).f()); + + // 2D row/column matrixes + let arow = a.slice(s![0..1, ..]); + let acol = a.slice(s![.., 0..1]); + let brow = b.slice(s![0..1, ..]); + let bcol = b.slice(s![.., 0..1]); + assert_layouts!(arow, CORDER, FORDER); + assert_not_layouts!(acol, CORDER, CPREFER, FORDER, FPREFER); + assert_layouts!(bcol, CORDER, FORDER); + assert_not_layouts!(brow, CORDER, CPREFER, FORDER, FPREFER); + + // 2D row/column matrixes - now made with insert axis + for &axis in &[Axis(0), Axis(1)] { + let arow = a.slice(s![0, ..]).insert_axis(axis); + let acol = a.slice(s![.., 0]).insert_axis(axis); + let brow = b.slice(s![0, ..]).insert_axis(axis); + let bcol = b.slice(s![.., 0]).insert_axis(axis); + assert_layouts!(arow, CORDER, FORDER); + assert_not_layouts!(acol, CORDER, CPREFER, FORDER, FPREFER); + assert_layouts!(bcol, CORDER, FORDER); + assert_not_layouts!(brow, CORDER, CPREFER, FORDER, FPREFER); + } + } + + #[test] + fn skip_layouts() + { + let a = M::zeros((5, 5)); + { + let v1 = a.slice(s![..;2, ..]).layout(); + let v2 = a.slice(s![.., ..;2]).layout(); + + assert!(!v1.is(LayoutBitset::CORDER) && v1.is(LayoutBitset::CPREFER)); + assert!(!v1.is(LayoutBitset::FORDER) && !v1.is(LayoutBitset::FPREFER)); + assert!(!v2.is(LayoutBitset::CORDER) && !v2.is(LayoutBitset::CPREFER)); + assert!(!v2.is(LayoutBitset::FORDER) && !v2.is(LayoutBitset::FPREFER)); + } + + let b = M::zeros((5, 5).f()); + { + let v1 = b.slice(s![..;2, ..]).layout(); + let v2 = b.slice(s![.., ..;2]).layout(); + + assert!(!v1.is(LayoutBitset::CORDER) && !v1.is(LayoutBitset::CPREFER)); + assert!(!v1.is(LayoutBitset::FORDER) && !v1.is(LayoutBitset::FPREFER)); + assert!(!v2.is(LayoutBitset::CORDER) && !v2.is(LayoutBitset::CPREFER)); + assert!(!v2.is(LayoutBitset::FORDER) && v2.is(LayoutBitset::FPREFER)); + } + } +} diff --git a/src/layout/layoutfmt.rs b/src/layout/layoutfmt.rs deleted file mode 100644 index f4cf3396f..000000000 --- a/src/layout/layoutfmt.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2017 bluss and ndarray developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use super::LayoutBitset; - -const LAYOUT_NAMES: &[&str] = &["C", "F", "c", "f"]; - -use std::fmt; - -impl fmt::Debug for LayoutBitset -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result - { - if self.0 == 0 { - write!(f, "Custom")? - } else { - (0..32).filter(|&i| self.is(1 << i)).try_fold((), |_, i| { - if let Some(name) = LAYOUT_NAMES.get(i) { - write!(f, "{}", name) - } else { - write!(f, "{:#x}", i) - } - })?; - }; - write!(f, " ({:#x})", self.0) - } -} diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 4c9833e23..7f549ebb2 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -1,248 +1,4 @@ -mod layoutfmt; +mod bitset; -// Layout is a bitset used for internal layout description of -// arrays, producers and sets of producers. -// The type is public but users don't interact with it. -#[doc(hidden)] -/// Memory layout description -#[derive(Copy, Clone)] -pub struct LayoutBitset(u32); - -#[deprecated(since = "0.18.0", note = "Layout has been renamed to LayoutBitset")] -#[allow(dead_code)] -/// Memory layout description, deprecated. See [`LayoutBitset`] instead. -pub type Layout = LayoutBitset; - -impl LayoutBitset -{ - pub(crate) const CORDER: u32 = 0b01; - pub(crate) const FORDER: u32 = 0b10; - pub(crate) const CPREFER: u32 = 0b0100; - pub(crate) const FPREFER: u32 = 0b1000; - - #[inline(always)] - pub(crate) fn is(self, flag: u32) -> bool - { - self.0 & flag != 0 - } - - /// Return layout common to both inputs - #[inline(always)] - pub(crate) fn intersect(self, other: LayoutBitset) -> LayoutBitset - { - LayoutBitset(self.0 & other.0) - } - - /// Return a layout that simultaneously "is" what both of the inputs are - #[inline(always)] - pub(crate) fn also(self, other: LayoutBitset) -> LayoutBitset - { - LayoutBitset(self.0 | other.0) - } - - #[inline(always)] - pub(crate) fn one_dimensional() -> LayoutBitset - { - LayoutBitset::c().also(LayoutBitset::f()) - } - - #[inline(always)] - pub(crate) fn c() -> LayoutBitset - { - LayoutBitset(LayoutBitset::CORDER | LayoutBitset::CPREFER) - } - - #[inline(always)] - pub(crate) fn f() -> LayoutBitset - { - LayoutBitset(LayoutBitset::FORDER | LayoutBitset::FPREFER) - } - - #[inline(always)] - pub(crate) fn cpref() -> LayoutBitset - { - LayoutBitset(LayoutBitset::CPREFER) - } - - #[inline(always)] - pub(crate) fn fpref() -> LayoutBitset - { - LayoutBitset(LayoutBitset::FPREFER) - } - - #[inline(always)] - pub(crate) fn none() -> LayoutBitset - { - LayoutBitset(0) - } - - /// A simple "score" method which scores positive for preferring C-order, negative for F-order - /// Subject to change when we can describe other layouts - #[inline] - pub(crate) fn tendency(self) -> i32 - { - (self.is(LayoutBitset::CORDER) as i32 - self.is(LayoutBitset::FORDER) as i32) - + (self.is(LayoutBitset::CPREFER) as i32 - self.is(LayoutBitset::FPREFER) as i32) - } -} - -#[cfg(test)] -mod tests -{ - use super::*; - use crate::imp_prelude::*; - use crate::NdProducer; - - type M = Array2; - type M1 = Array1; - type M0 = Array0; - - macro_rules! assert_layouts { - ($mat:expr, $($layout:ident),*) => {{ - let layout = $mat.view().layout(); - $( - assert!(layout.is(LayoutBitset::$layout), - "Assertion failed: array {:?} is not layout {}", - $mat, - stringify!($layout)); - )* - }}; - } - - macro_rules! assert_not_layouts { - ($mat:expr, $($layout:ident),*) => {{ - let layout = $mat.view().layout(); - $( - assert!(!layout.is(LayoutBitset::$layout), - "Assertion failed: array {:?} show not have layout {}", - $mat, - stringify!($layout)); - )* - }}; - } - - #[test] - fn contig_layouts() - { - let a = M::zeros((5, 5)); - let b = M::zeros((5, 5).f()); - let ac = a.view().layout(); - let af = b.view().layout(); - assert!(ac.is(LayoutBitset::CORDER) && ac.is(LayoutBitset::CPREFER)); - assert!(!ac.is(LayoutBitset::FORDER) && !ac.is(LayoutBitset::FPREFER)); - assert!(!af.is(LayoutBitset::CORDER) && !af.is(LayoutBitset::CPREFER)); - assert!(af.is(LayoutBitset::FORDER) && af.is(LayoutBitset::FPREFER)); - } - - #[test] - fn contig_cf_layouts() - { - let a = M::zeros((5, 1)); - let b = M::zeros((1, 5).f()); - assert_layouts!(a, CORDER, CPREFER, FORDER, FPREFER); - assert_layouts!(b, CORDER, CPREFER, FORDER, FPREFER); - - let a = M1::zeros(5); - let b = M1::zeros(5.f()); - assert_layouts!(a, CORDER, CPREFER, FORDER, FPREFER); - assert_layouts!(b, CORDER, CPREFER, FORDER, FPREFER); - - let a = M0::zeros(()); - assert_layouts!(a, CORDER, CPREFER, FORDER, FPREFER); - - let a = M::zeros((5, 5)); - let b = M::zeros((5, 5).f()); - let arow = a.slice(s![..1, ..]); - let bcol = b.slice(s![.., ..1]); - assert_layouts!(arow, CORDER, CPREFER, FORDER, FPREFER); - assert_layouts!(bcol, CORDER, CPREFER, FORDER, FPREFER); - - let acol = a.slice(s![.., ..1]); - let brow = b.slice(s![..1, ..]); - assert_not_layouts!(acol, CORDER, CPREFER, FORDER, FPREFER); - assert_not_layouts!(brow, CORDER, CPREFER, FORDER, FPREFER); - } - - #[test] - fn stride_layouts() - { - let a = M::zeros((5, 5)); - - { - let v1 = a.slice(s![1.., ..]).layout(); - let v2 = a.slice(s![.., 1..]).layout(); - - assert!(v1.is(LayoutBitset::CORDER) && v1.is(LayoutBitset::CPREFER)); - assert!(!v1.is(LayoutBitset::FORDER) && !v1.is(LayoutBitset::FPREFER)); - assert!(!v2.is(LayoutBitset::CORDER) && v2.is(LayoutBitset::CPREFER)); - assert!(!v2.is(LayoutBitset::FORDER) && !v2.is(LayoutBitset::FPREFER)); - } - - let b = M::zeros((5, 5).f()); - - { - let v1 = b.slice(s![1.., ..]).layout(); - let v2 = b.slice(s![.., 1..]).layout(); - - assert!(!v1.is(LayoutBitset::CORDER) && !v1.is(LayoutBitset::CPREFER)); - assert!(!v1.is(LayoutBitset::FORDER) && v1.is(LayoutBitset::FPREFER)); - assert!(!v2.is(LayoutBitset::CORDER) && !v2.is(LayoutBitset::CPREFER)); - assert!(v2.is(LayoutBitset::FORDER) && v2.is(LayoutBitset::FPREFER)); - } - } - - #[test] - fn no_layouts() - { - let a = M::zeros((5, 5)); - let b = M::zeros((5, 5).f()); - - // 2D row/column matrixes - let arow = a.slice(s![0..1, ..]); - let acol = a.slice(s![.., 0..1]); - let brow = b.slice(s![0..1, ..]); - let bcol = b.slice(s![.., 0..1]); - assert_layouts!(arow, CORDER, FORDER); - assert_not_layouts!(acol, CORDER, CPREFER, FORDER, FPREFER); - assert_layouts!(bcol, CORDER, FORDER); - assert_not_layouts!(brow, CORDER, CPREFER, FORDER, FPREFER); - - // 2D row/column matrixes - now made with insert axis - for &axis in &[Axis(0), Axis(1)] { - let arow = a.slice(s![0, ..]).insert_axis(axis); - let acol = a.slice(s![.., 0]).insert_axis(axis); - let brow = b.slice(s![0, ..]).insert_axis(axis); - let bcol = b.slice(s![.., 0]).insert_axis(axis); - assert_layouts!(arow, CORDER, FORDER); - assert_not_layouts!(acol, CORDER, CPREFER, FORDER, FPREFER); - assert_layouts!(bcol, CORDER, FORDER); - assert_not_layouts!(brow, CORDER, CPREFER, FORDER, FPREFER); - } - } - - #[test] - fn skip_layouts() - { - let a = M::zeros((5, 5)); - { - let v1 = a.slice(s![..;2, ..]).layout(); - let v2 = a.slice(s![.., ..;2]).layout(); - - assert!(!v1.is(LayoutBitset::CORDER) && v1.is(LayoutBitset::CPREFER)); - assert!(!v1.is(LayoutBitset::FORDER) && !v1.is(LayoutBitset::FPREFER)); - assert!(!v2.is(LayoutBitset::CORDER) && !v2.is(LayoutBitset::CPREFER)); - assert!(!v2.is(LayoutBitset::FORDER) && !v2.is(LayoutBitset::FPREFER)); - } - - let b = M::zeros((5, 5).f()); - { - let v1 = b.slice(s![..;2, ..]).layout(); - let v2 = b.slice(s![.., ..;2]).layout(); - - assert!(!v1.is(LayoutBitset::CORDER) && !v1.is(LayoutBitset::CPREFER)); - assert!(!v1.is(LayoutBitset::FORDER) && !v1.is(LayoutBitset::FPREFER)); - assert!(!v2.is(LayoutBitset::CORDER) && !v2.is(LayoutBitset::CPREFER)); - assert!(!v2.is(LayoutBitset::FORDER) && v2.is(LayoutBitset::FPREFER)); - } - } -} +#[allow(deprecated)] +pub use bitset::{Layout, LayoutBitset};