Skip to content

Commit

Permalink
Assume slices can't contain > isize::MAX bytes
Browse files Browse the repository at this point in the history
  • Loading branch information
jturner314 committed Nov 18, 2018
1 parent ea54458 commit 9435c8f
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 60 deletions.
21 changes: 13 additions & 8 deletions src/arraytraits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use std::hash;
use std::iter::FromIterator;
use std::iter::IntoIterator;
use std::mem;
use std::ops::{
Index,
IndexMut,
Expand Down Expand Up @@ -223,10 +224,12 @@ impl<'a, A, Slice: ?Sized> From<&'a Slice> for ArrayView<'a, A, Ix1>
{
fn from(slice: &'a Slice) -> Self {
let xs = slice.as_ref();
assert!(
xs.len() <= ::std::isize::MAX as usize,
"Slice length must fit in `isize`.",
);
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())
}
Expand Down Expand Up @@ -255,10 +258,12 @@ impl<'a, A, Slice: ?Sized> From<&'a mut Slice> for ArrayViewMut<'a, A, Ix1>
{
fn from(slice: &'a mut Slice) -> Self {
let xs = slice.as_mut();
assert!(
xs.len() <= ::std::isize::MAX as usize,
"Slice length must fit in `isize`.",
);
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_mut_ptr())
}
Expand Down
67 changes: 23 additions & 44 deletions src/dimension/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,11 @@ pub fn dim_stride_overlap<D: Dimension>(dim: &D, strides: &D) -> bool {
/// lengths does not exceed `isize::MAX`.
///
/// If `size_of_checked_shape(dim)` returns `Ok(size)`, the data buffer is a
/// `Vec` of length `size`, and `strides` are created with
/// slice or `Vec` of length `size`, and `strides` are created with
/// `self.default_strides()` or `self.fortran_strides()`, then the invariants
/// are met to construct an array from the data buffer, `dim`, and `strides`.
/// (The data buffer being a `Vec` guarantees that it contains no more than
/// `isize::MAX` bytes.)
/// (The data buffer being a slice or `Vec` guarantees that it contains no more
/// than `isize::MAX` bytes.)
pub fn size_of_shape_checked<D: Dimension>(dim: &D) -> Result<usize, ShapeError> {
let size_nonzero = dim
.slice()
Expand All @@ -94,13 +94,9 @@ pub fn size_of_shape_checked<D: Dimension>(dim: &D) -> Result<usize, ShapeError>
///
/// To meet the invariants,
///
/// 1. The offset in units of `A` and in units of bytes between the least
/// address and greatest address accessible by moving along all axes must
/// not exceed `isize::MAX`.
/// 1. The product of non-zero axis lengths must not exceed `isize::MAX`.
///
/// 2. The product of non-zero axis lengths must not exceed `isize::MAX`.
///
/// 3. The result of `dim.size()` (assuming no overflow) must be less than or
/// 2. The result of `dim.size()` (assuming no overflow) must be less than or
/// equal to the length of the slice.
///
/// (Since `dim.default_strides()` and `dim.fortran_strides()` always return
Expand All @@ -112,31 +108,17 @@ pub fn size_of_shape_checked<D: Dimension>(dim: &D) -> Result<usize, ShapeError>
/// difference between the least address and greatest address accessible by
/// moving along all axes is ≤ the length of the slice.)
///
/// Note that if `data` is a slice of a `Vec<A>`, conditions 2 and 3 are
/// sufficient to guarantee condition 1 because `Vec` never allocates more than
/// `isize::MAX` bytes.
/// Note that since slices cannot contain more than `isize::MAX` bytes,
/// conditions 2 and 3 are sufficient to guarantee that the offset in units of
/// `A` and in units of bytes between the least address and greatest address
/// accessible by moving along all axes does not exceed `isize::MAX`.
pub fn can_index_slice_not_custom<A, D: Dimension>(data: &[A], dim: &D) -> Result<(), ShapeError> {
// Condition 2 and 1a.
// Condition 1.
let len = size_of_shape_checked(dim)?;

// Calculate offset in units of `A` between the least address and greatest
// address accessible by moving along all axes.
let max_offset = len.saturating_sub(1);
// Calcaulte offset in units of bytes between the least address and
// greatest address accessible by moving along all axes.
let max_offset_bytes = max_offset
.checked_mul(mem::size_of::<A>())
.ok_or_else(|| from_kind(ErrorKind::Overflow))?;

// Condition 1b.
if max_offset_bytes > isize::MAX as usize {
return Err(from_kind(ErrorKind::Overflow));
}
// Condition 3.
// Condition 2.
if len > data.len() {
return Err(from_kind(ErrorKind::OutOfBounds));
}

Ok(())
}

Expand Down Expand Up @@ -200,17 +182,13 @@ where
///
/// 1. The ndim of `dim` and `strides` must be the same.
///
/// 2. The absolute difference in units of `A` and in units of bytes between
/// the least address and greatest address accessible by moving along all axes
/// must not exceed `isize::MAX`.
///
/// 3. The product of non-zero axis lengths must not exceed `isize::MAX`.
/// 2. The product of non-zero axis lengths must not exceed `isize::MAX`.
///
/// 4. For axes with length > 1, the stride must be nonnegative. This is
/// 3. For axes with length > 1, the stride must be nonnegative. This is
/// necessary to make sure the pointer cannot move backwards outside the
/// slice. For axes with length ≤ 1, the stride can be anything.
///
/// 5. If the array will be empty (any axes are zero-length), the difference
/// 4. If the array will be empty (any axes are zero-length), the difference
/// between the least address and greatest address accessible by moving
/// along all axes must be ≤ `data.len()`. (It's fine in this case to move
/// one byte past the end of the slice since the pointers will be offset but
Expand All @@ -221,19 +199,20 @@ where
/// `data.len()`. This and #4 ensure that all dereferenceable pointers point
/// to elements within the slice.
///
/// 6. The strides must not allow any element to be referenced by two different
/// 5. The strides must not allow any element to be referenced by two different
/// indices.
///
/// Note that if `data` is a slice of a `Vec<A>`, conditions 4 and 5 are
/// sufficient to guarantee condition 2 because `Vec` never allocates more than
/// `isize::MAX` bytes.
/// Note that since slices cannot contain more than `isize::MAX` bytes,
/// condition 4 is sufficient to guarantee that the absolute difference in
/// units of `A` and in units of bytes between the least address and greatest
/// address accessible by moving along all axes does not exceed `isize::MAX`.
pub fn can_index_slice<A, D: Dimension>(data: &[A], dim: &D, strides: &D)
-> Result<(), ShapeError>
{
// Check conditions 1, 2, and 3.
// Check conditions 1 and 2 and calculate `max_offset`.
let max_offset = max_abs_offset_check_overflow::<A, _>(dim, strides)?;

// Check condition 5.
// Check condition 4.
let is_empty = dim.slice().iter().any(|&d| d == 0);
if is_empty && max_offset > data.len() {
return Err(from_kind(ErrorKind::OutOfBounds));
Expand All @@ -242,15 +221,15 @@ pub fn can_index_slice<A, D: Dimension>(data: &[A], dim: &D, strides: &D)
return Err(from_kind(ErrorKind::OutOfBounds));
}

// Check condition 4.
// Check condition 3.
for (&d, &s) in izip!(dim.slice(), strides.slice()) {
let s = s as isize;
if d > 1 && s < 0 {
return Err(from_kind(ErrorKind::Unsupported));
}
}

// Check condition 6.
// Check condition 5.
if !is_empty && dim_stride_overlap(dim, strides) {
return Err(from_kind(ErrorKind::Unsupported));
}
Expand Down
34 changes: 26 additions & 8 deletions src/free_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,22 @@ pub fn aview1<A>(xs: &[A]) -> ArrayView1<A> {

/// Create a two-dimensional array view with elements borrowing `xs`.
///
/// **Panics** if the product of non-zero axis lengths or the numer of bytes in
/// the array overflow `isize`.
/// **Panics** if the product of non-zero axis lengths overflows `isize`. (This
/// can only occur when `V` is zero-sized.)
pub fn aview2<A, V: FixedInitializer<Elem=A>>(xs: &[V]) -> ArrayView2<A> {
let cols = V::len();
let rows = xs.len();
let data = unsafe { slice::from_raw_parts(xs.as_ptr() as *const A, cols * rows) };
ArrayView::from_shape(Ix2(rows, cols), data).unwrap()
let dim = Ix2(rows, cols);
if size_of::<V>() == 0 {
dimension::size_of_shape_checked(&dim)
.expect("Product of non-zero axis lengths must not overflow isize.");
}
// `row`, `col`, and `row * col` are guaranteed to fit in `isize` because
// slices contain ≤ `isize::MAX` bytes and we've checked the ZST case.
unsafe {
let data = slice::from_raw_parts(xs.as_ptr() as *const A, cols * rows);
ArrayView::from_shape_ptr(dim, data.as_ptr())
}
}

/// Create a one-dimensional read-write array view with elements borrowing `xs`.
Expand All @@ -123,8 +132,8 @@ pub fn aview_mut1<A>(xs: &mut [A]) -> ArrayViewMut1<A> {

/// Create a two-dimensional read-write array view with elements borrowing `xs`.
///
/// **Panics** if the product of non-zero axis lengths or the numer of bytes in
/// the array overflow `isize`.
/// **Panics** if the product of non-zero axis lengths overflows `isize`. (This
/// can only occur when `V` is zero-sized.)
///
/// # Example
///
Expand All @@ -149,8 +158,17 @@ pub fn aview_mut1<A>(xs: &mut [A]) -> ArrayViewMut1<A> {
pub fn aview_mut2<A, V: FixedInitializer<Elem=A>>(xs: &mut [V]) -> ArrayViewMut2<A> {
let cols = V::len();
let rows = xs.len();
let data = unsafe { slice::from_raw_parts_mut(xs.as_mut_ptr() as *mut A, cols * rows) };
ArrayViewMut::from_shape(Ix2(rows, cols), data).unwrap()
let dim = Ix2(rows, cols);
if size_of::<V>() == 0 {
dimension::size_of_shape_checked(&dim)
.expect("Product of non-zero axis lengths must not overflow isize.");
}
// `row`, `col`, and `row * col` are guaranteed to fit in `isize` because
// slices contain ≤ `isize::MAX` bytes and we've checked the ZST case.
unsafe {
let data = slice::from_raw_parts_mut(xs.as_mut_ptr() as *mut A, cols * rows);
ArrayViewMut::from_shape_ptr(dim, data.as_mut_ptr())
}
}

/// Fixed-size array used for array initialization
Expand Down

0 comments on commit 9435c8f

Please sign in to comment.