diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7e9834087..d0a0c0118 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -34,7 +34,7 @@ jobs: - stable - beta - nightly - - 1.51.0 # MSRV + - 1.57.0 # MSRV name: tests/${{ matrix.rust }} steps: diff --git a/Cargo.toml b/Cargo.toml index a648b09bc..ae9e33f06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "ndarray" version = "0.15.6" edition = "2018" -rust-version = "1.51" +rust-version = "1.57" authors = [ "Ulrik Sverdrup \"bluss\"", "Jim Turner" diff --git a/scripts/all-tests.sh b/scripts/all-tests.sh index 81d240ba2..cbea6dba7 100755 --- a/scripts/all-tests.sh +++ b/scripts/all-tests.sh @@ -6,7 +6,7 @@ set -e FEATURES=$1 CHANNEL=$2 -if [ "$CHANNEL" = "1.51.0" ]; then +if [ "$CHANNEL" = "1.57.0" ]; then cargo update --package openblas-src --precise 0.10.5 cargo update --package openblas-build --precise 0.10.5 cargo update --package once_cell --precise 1.14.0 diff --git a/src/aliases.rs b/src/aliases.rs index d41c888a6..9a8ea8f2c 100644 --- a/src/aliases.rs +++ b/src/aliases.rs @@ -7,43 +7,43 @@ use crate::{ArcArray, Array, ArrayView, ArrayViewMut, Ix, IxDynImpl}; /// Create a zero-dimensional index #[allow(non_snake_case)] #[inline(always)] -pub fn Ix0() -> Ix0 { +pub const fn Ix0() -> Ix0 { Dim::new([]) } /// Create a one-dimensional index #[allow(non_snake_case)] #[inline(always)] -pub fn Ix1(i0: Ix) -> Ix1 { +pub const fn Ix1(i0: Ix) -> Ix1 { Dim::new([i0]) } /// Create a two-dimensional index #[allow(non_snake_case)] #[inline(always)] -pub fn Ix2(i0: Ix, i1: Ix) -> Ix2 { +pub const fn Ix2(i0: Ix, i1: Ix) -> Ix2 { Dim::new([i0, i1]) } /// Create a three-dimensional index #[allow(non_snake_case)] #[inline(always)] -pub fn Ix3(i0: Ix, i1: Ix, i2: Ix) -> Ix3 { +pub const fn Ix3(i0: Ix, i1: Ix, i2: Ix) -> Ix3 { Dim::new([i0, i1, i2]) } /// Create a four-dimensional index #[allow(non_snake_case)] #[inline(always)] -pub fn Ix4(i0: Ix, i1: Ix, i2: Ix, i3: Ix) -> Ix4 { +pub const fn Ix4(i0: Ix, i1: Ix, i2: Ix, i3: Ix) -> Ix4 { Dim::new([i0, i1, i2, i3]) } /// Create a five-dimensional index #[allow(non_snake_case)] #[inline(always)] -pub fn Ix5(i0: Ix, i1: Ix, i2: Ix, i3: Ix, i4: Ix) -> Ix5 { +pub const fn Ix5(i0: Ix, i1: Ix, i2: Ix, i3: Ix, i4: Ix) -> Ix5 { Dim::new([i0, i1, i2, i3, i4]) } /// Create a six-dimensional index #[allow(non_snake_case)] #[inline(always)] -pub fn Ix6(i0: Ix, i1: Ix, i2: Ix, i3: Ix, i4: Ix, i5: Ix) -> Ix6 { +pub const fn Ix6(i0: Ix, i1: Ix, i2: Ix, i3: Ix, i4: Ix, i5: Ix) -> Ix6 { Dim::new([i0, i1, i2, i3, i4, i5]) } diff --git a/src/arraytraits.rs b/src/arraytraits.rs index 39a82b1ae..8d44c1e72 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -307,6 +307,10 @@ pub const ARRAY_FORMAT_VERSION: u8 = 1u8; // use "raw" form instead of type aliases here so that they show up in docs /// Implementation of `ArrayView::from(&S)` where `S` is a slice or sliceable. +/// +/// **Panics** if the length of the slice overflows `isize`. (This can only +/// occur if `A` is zero-sized, because slices cannot contain more than +/// `isize::MAX` number of bytes.) impl<'a, A, Slice: ?Sized> From<&'a Slice> for ArrayView<'a, A, Ix1> where Slice: AsRef<[A]>, @@ -315,14 +319,7 @@ where /// /// **Panics** if the slice length is greater than `isize::MAX`. fn from(slice: &'a Slice) -> Self { - let xs = slice.as_ref(); - if mem::size_of::() == 0 { - assert!( - xs.len() <= ::std::isize::MAX as usize, - "Slice length must fit in `isize`.", - ); - } - unsafe { Self::from_shape_ptr(xs.len(), xs.as_ptr()) } + aview1(slice.as_ref()) } } @@ -334,25 +331,7 @@ where impl<'a, A, const N: usize> From<&'a [[A; N]]> for ArrayView<'a, A, Ix2> { /// Create a two-dimensional read-only array view of the data in `slice` fn from(xs: &'a [[A; N]]) -> Self { - let cols = N; - let rows = xs.len(); - let dim = Ix2(rows, cols); - if size_of::() == 0 { - dimension::size_of_shape_checked(&dim) - .expect("Product of non-zero axis lengths must not overflow isize."); - } else if N == 0 { - assert!( - xs.len() <= isize::MAX as usize, - "Product of non-zero axis lengths must not overflow isize.", - ); - } - - // `cols * rows` is guaranteed to fit in `isize` because we checked that it fits in - // `isize::MAX` - unsafe { - let data = slice::from_raw_parts(xs.as_ptr() as *const A, cols * rows); - ArrayView::from_shape_ptr(dim, data.as_ptr()) - } + aview2(xs) } } diff --git a/src/dimension/dim.rs b/src/dimension/dim.rs index 19075f943..3f8ff23e3 100644 --- a/src/dimension/dim.rs +++ b/src/dimension/dim.rs @@ -42,7 +42,7 @@ pub struct Dim { impl Dim { /// Private constructor and accessors for Dim - pub(crate) fn new(index: I) -> Dim { + pub(crate) const fn new(index: I) -> Dim { Dim { index } } #[inline(always)] diff --git a/src/free_functions.rs b/src/free_functions.rs index e9c3abff6..342aae980 100644 --- a/src/free_functions.rs +++ b/src/free_functions.rs @@ -10,6 +10,7 @@ use alloc::vec; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use std::mem::{forget, size_of}; +use std::ptr::NonNull; use crate::imp_prelude::*; use crate::{dimension, ArcArray1, ArcArray2}; @@ -65,14 +66,24 @@ pub fn rcarr1(xs: &[A]) -> ArcArray1 { } /// Create a zero-dimensional array view borrowing `x`. -pub fn aview0(x: &A) -> ArrayView0<'_, A> { - unsafe { ArrayView::from_shape_ptr(Ix0(), x) } +pub const fn aview0(x: &A) -> ArrayView0<'_, A> { + ArrayBase { + data: ViewRepr::new(), + // Safe because references are always non-null. + ptr: unsafe { NonNull::new_unchecked(x as *const A as *mut A) }, + dim: Ix0(), + strides: Ix0(), + } } /// Create a one-dimensional array view with elements borrowing `xs`. /// +/// **Panics** if the length of the slice overflows `isize`. (This can only +/// occur if `A` is zero-sized, because slices cannot contain more than +/// `isize::MAX` number of bytes.) +/// /// ``` -/// use ndarray::aview1; +/// use ndarray::{aview1, ArrayView1}; /// /// let data = [1.0; 1024]; /// @@ -80,17 +91,80 @@ pub fn aview0(x: &A) -> ArrayView0<'_, A> { /// let a2d = aview1(&data).into_shape((32, 32)).unwrap(); /// /// assert_eq!(a2d.sum(), 1024.0); +/// +/// // Create a const 1D array view +/// const C: ArrayView1<'static, f64> = aview1(&[1., 2., 3.]); +/// +/// assert_eq!(C.sum(), 6.); /// ``` -pub fn aview1(xs: &[A]) -> ArrayView1<'_, A> { - ArrayView::from(xs) +pub const fn aview1(xs: &[A]) -> ArrayView1<'_, A> { + if size_of::() == 0 { + assert!( + xs.len() <= isize::MAX as usize, + "Slice length must fit in `isize`.", + ); + } + ArrayBase { + data: ViewRepr::new(), + // Safe because references are always non-null. + ptr: unsafe { NonNull::new_unchecked(xs.as_ptr() as *mut A) }, + dim: Ix1(xs.len()), + strides: Ix1(1), + } } /// Create a two-dimensional array view with elements borrowing `xs`. /// -/// **Panics** if the product of non-zero axis lengths overflows `isize` (This can only occur if A -/// is zero-sized because slices cannot contain more than `isize::MAX` number of bytes). -pub fn aview2(xs: &[[A; N]]) -> ArrayView2<'_, A> { - ArrayView2::from(xs) +/// **Panics** if the product of non-zero axis lengths overflows `isize` (This +/// can only occur if A is zero-sized or if `N` is zero, because slices cannot +/// contain more than `isize::MAX` number of bytes). +/// +/// ``` +/// use ndarray::{aview2, ArrayView2}; +/// +/// let data = vec![[1., 2., 3.], [4., 5., 6.]]; +/// +/// let view = aview2(&data); +/// assert_eq!(view.sum(), 21.); +/// +/// // Create a const 2D array view +/// const C: ArrayView2<'static, f64> = aview2(&[[1., 2., 3.], [4., 5., 6.]]); +/// assert_eq!(C.sum(), 21.); +/// ``` +pub const fn aview2(xs: &[[A; N]]) -> ArrayView2<'_, A> { + let cols = N; + let rows = xs.len(); + if size_of::() == 0 { + if let Some(n_elems) = rows.checked_mul(cols) { + assert!( + rows <= isize::MAX as usize + && cols <= isize::MAX as usize + && n_elems <= isize::MAX as usize, + "Product of non-zero axis lengths must not overflow isize.", + ); + } else { + panic!("Overflow in number of elements."); + } + } else if N == 0 { + assert!( + rows <= isize::MAX as usize, + "Product of non-zero axis lengths must not overflow isize.", + ); + } + // Safe because references are always non-null. + let ptr = unsafe { NonNull::new_unchecked(xs.as_ptr() as *mut A) }; + let dim = Ix2(rows, cols); + let strides = if rows == 0 || cols == 0 { + Ix2(0, 0) + } else { + Ix2(cols, 1) + }; + ArrayBase { + data: ViewRepr::new(), + ptr, + dim, + strides, + } } /// Create a one-dimensional read-write array view with elements borrowing `xs`. diff --git a/src/lib.rs b/src/lib.rs index a651ae58c..b15b0ea88 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,7 +71,7 @@ //! needs matching memory layout to be efficient (with some exceptions). //! + Efficient floating point matrix multiplication even for very large //! matrices; can optionally use BLAS to improve it further. -//! - **Requires Rust 1.51 or later** +//! - **Requires Rust 1.57 or later** //! //! ## Crate Feature Flags //! @@ -1450,7 +1450,7 @@ pub struct RawViewRepr { impl RawViewRepr { #[inline(always)] - fn new() -> Self { + const fn new() -> Self { RawViewRepr { ptr: PhantomData } } } @@ -1467,7 +1467,7 @@ pub struct ViewRepr { impl ViewRepr { #[inline(always)] - fn new() -> Self { + const fn new() -> Self { ViewRepr { life: PhantomData } } }