diff --git a/src/dimension/broadcast.rs b/src/dimension/broadcast.rs index dc1513f04..6470dd58b 100644 --- a/src/dimension/broadcast.rs +++ b/src/dimension/broadcast.rs @@ -34,6 +34,57 @@ where Ok(out) } +/// Return new stride when trying to grow `from` into shape `to` +/// +/// Broadcasting works by returning a "fake stride" where elements +/// to repeat are in axes with 0 stride, so that several indexes point +/// to the same element. +/// +/// **Note:** Cannot be used for mutable iterators, since repeating +/// elements would create aliasing pointers. +pub(crate) fn upcast(to: &D, from: &E, stride: &E) -> Option { + // Make sure the product of non-zero axis lengths does not exceed + // `isize::MAX`. This is the only safety check we need to perform + // because all the other constraints of `ArrayBase` are guaranteed + // to be met since we're starting from a valid `ArrayBase`. + let _ = size_of_shape_checked(to).ok()?; + + let mut new_stride = to.clone(); + // begin at the back (the least significant dimension) + // size of the axis has to either agree or `from` has to be 1 + if to.ndim() < from.ndim() { + return None; + } + + { + let mut new_stride_iter = new_stride.slice_mut().iter_mut().rev(); + for ((er, es), dr) in from + .slice() + .iter() + .rev() + .zip(stride.slice().iter().rev()) + .zip(new_stride_iter.by_ref()) + { + /* update strides */ + if *dr == *er { + /* keep stride */ + *dr = *es; + } else if *er == 1 { + /* dead dimension, zero stride */ + *dr = 0 + } else { + return None; + } + } + + /* set remaining strides to zero */ + for dr in new_stride_iter { + *dr = 0; + } + } + Some(new_stride) +} + pub trait DimMax { /// The resulting dimension type after broadcasting. type Output: Dimension; diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 115cd2d71..ad9e11685 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -22,7 +22,7 @@ use crate::dimension::{ abs_index, axes_of, do_slice, merge_axes, move_min_stride_axis_to_last, offset_from_low_addr_ptr_to_logical_ptr, size_of_shape_checked, stride_offset, Axes, }; -use crate::dimension::broadcast::co_broadcast; +use crate::dimension::broadcast::{co_broadcast, upcast}; use crate::dimension::reshape_dim; use crate::error::{self, ErrorKind, ShapeError, from_kind}; use crate::math_cell::MathCell; @@ -2036,56 +2036,6 @@ where E: IntoDimension, S: Data, { - /// Return new stride when trying to grow `from` into shape `to` - /// - /// Broadcasting works by returning a "fake stride" where elements - /// to repeat are in axes with 0 stride, so that several indexes point - /// to the same element. - /// - /// **Note:** Cannot be used for mutable iterators, since repeating - /// elements would create aliasing pointers. - fn upcast(to: &D, from: &E, stride: &E) -> Option { - // Make sure the product of non-zero axis lengths does not exceed - // `isize::MAX`. This is the only safety check we need to perform - // because all the other constraints of `ArrayBase` are guaranteed - // to be met since we're starting from a valid `ArrayBase`. - let _ = size_of_shape_checked(to).ok()?; - - let mut new_stride = to.clone(); - // begin at the back (the least significant dimension) - // size of the axis has to either agree or `from` has to be 1 - if to.ndim() < from.ndim() { - return None; - } - - { - let mut new_stride_iter = new_stride.slice_mut().iter_mut().rev(); - for ((er, es), dr) in from - .slice() - .iter() - .rev() - .zip(stride.slice().iter().rev()) - .zip(new_stride_iter.by_ref()) - { - /* update strides */ - if *dr == *er { - /* keep stride */ - *dr = *es; - } else if *er == 1 { - /* dead dimension, zero stride */ - *dr = 0 - } else { - return None; - } - } - - /* set remaining strides to zero */ - for dr in new_stride_iter { - *dr = 0; - } - } - Some(new_stride) - } let dim = dim.into_dimension(); // Note: zero strides are safe precisely because we return an read-only view diff --git a/src/impl_views/methods.rs b/src/impl_views/methods.rs new file mode 100644 index 000000000..a34247165 --- /dev/null +++ b/src/impl_views/methods.rs @@ -0,0 +1,34 @@ +// Copyright 2014-2016 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 crate::imp_prelude::*; +use crate::dimension::IntoDimension; +use crate::dimension::broadcast::upcast; + +impl<'a, A, D> ArrayView<'a, A, D> +where + D: Dimension, +{ + /// Broadcasts an `ArrayView`. See [`ArrayBase::broadcast`]. + /// + /// This is a specialized version of [`ArrayBase::broadcast`] that transfers + /// the view's lifetime to the output. + pub fn broadcast_ref(&self, dim: E) -> Option> + where + E: IntoDimension, + { + let dim = dim.into_dimension(); + + // Note: zero strides are safe precisely because we return an read-only view + let broadcast_strides = match upcast(&dim, &self.dim, &self.strides) { + Some(st) => st, + None => return None, + }; + unsafe { Some(ArrayView::new(self.ptr, dim, broadcast_strides)) } + } +} diff --git a/src/impl_views/mod.rs b/src/impl_views/mod.rs index 487cc3cb2..fda58242a 100644 --- a/src/impl_views/mod.rs +++ b/src/impl_views/mod.rs @@ -1,6 +1,7 @@ mod constructors; mod conversions; mod indexing; +mod methods; mod splitting; pub use constructors::*;