Skip to content

Commit

Permalink
Merge pull request #1132 from jturner314/const-constructors
Browse files Browse the repository at this point in the history
Make the aview0, aview1, and aview2 free functions be const fns
  • Loading branch information
bluss committed Mar 10, 2024
2 parents eb12613 + cab158f commit cd0a956
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 50 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
- stable
- beta
- nightly
- 1.51.0 # MSRV
- 1.57.0 # MSRV

name: tests/${{ matrix.rust }}
steps:
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion scripts/all-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 7 additions & 7 deletions src/aliases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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])
}

Expand Down
33 changes: 6 additions & 27 deletions src/arraytraits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]>,
Expand All @@ -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::<A>() == 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())
}
}

Expand All @@ -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::<A>() == 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)
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/dimension/dim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub struct Dim<I: ?Sized> {

impl<I> Dim<I> {
/// Private constructor and accessors for Dim
pub(crate) fn new(index: I) -> Dim<I> {
pub(crate) const fn new(index: I) -> Dim<I> {
Dim { index }
}
#[inline(always)]
Expand Down
92 changes: 83 additions & 9 deletions src/free_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -65,32 +66,105 @@ pub fn rcarr1<A: Clone>(xs: &[A]) -> ArcArray1<A> {
}

/// Create a zero-dimensional array view borrowing `x`.
pub fn aview0<A>(x: &A) -> ArrayView0<'_, A> {
unsafe { ArrayView::from_shape_ptr(Ix0(), x) }
pub const fn aview0<A>(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];
///
/// // Create a 2D array view from borrowed data
/// 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<A>(xs: &[A]) -> ArrayView1<'_, A> {
ArrayView::from(xs)
pub const fn aview1<A>(xs: &[A]) -> ArrayView1<'_, A> {
if size_of::<A>() == 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<A, const N: usize>(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<A, const N: usize>(xs: &[[A; N]]) -> ArrayView2<'_, A> {
let cols = N;
let rows = xs.len();
if size_of::<A>() == 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`.
Expand Down
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
//!
Expand Down Expand Up @@ -1450,7 +1450,7 @@ pub struct RawViewRepr<A> {

impl<A> RawViewRepr<A> {
#[inline(always)]
fn new() -> Self {
const fn new() -> Self {
RawViewRepr { ptr: PhantomData }
}
}
Expand All @@ -1467,7 +1467,7 @@ pub struct ViewRepr<A> {

impl<A> ViewRepr<A> {
#[inline(always)]
fn new() -> Self {
const fn new() -> Self {
ViewRepr { life: PhantomData }
}
}
Expand Down

0 comments on commit cd0a956

Please sign in to comment.