Skip to content

Commit 6da8add

Browse files
committed
Clarify ArrayBase invariants and fix constructors
1 parent 245cfbb commit 6da8add

12 files changed

+699
-178
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ serde = { version = "1.0", optional = true }
3939

4040
[dev-dependencies]
4141
defmac = "0.1"
42+
quickcheck = { version = "0.7.2", default-features = false }
4243
rawpointer = "0.1"
4344

4445
[features]

src/array_serialize.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,10 @@ impl<A, S, D> Decodable for ArrayBase<S, D>
7474
let elements = try!(
7575
d.read_struct_field("data", 2, |d| {
7676
d.read_seq(|d, len| {
77-
if len != dim.size() {
77+
let dim_size = dim
78+
.size_checked()
79+
.ok_or_else(|| from_kind(ErrorKind::Overflow))?;
80+
if len != dim_size {
7881
Err(d.error("data and dimension must match in size"))
7982
} else {
8083
let mut elements = Vec::with_capacity(len);

src/arraytraits.rs

+12
Original file line numberDiff line numberDiff line change
@@ -216,11 +216,17 @@ pub const ARRAY_FORMAT_VERSION: u8 = 1u8;
216216
/// Implementation of `ArrayView::from(&S)` where `S` is a slice or slicable.
217217
///
218218
/// Create a one-dimensional read-only array view of the data in `slice`.
219+
///
220+
/// **Panics** if the slice length is greater than `isize::MAX`.
219221
impl<'a, A, Slice: ?Sized> From<&'a Slice> for ArrayView<'a, A, Ix1>
220222
where Slice: AsRef<[A]>
221223
{
222224
fn from(slice: &'a Slice) -> Self {
223225
let xs = slice.as_ref();
226+
assert!(
227+
xs.len() <= ::std::isize::MAX as usize,
228+
"Slice length must fit in `isize`.",
229+
);
224230
unsafe {
225231
Self::from_shape_ptr(xs.len(), xs.as_ptr())
226232
}
@@ -242,11 +248,17 @@ impl<'a, A, S, D> From<&'a ArrayBase<S, D>> for ArrayView<'a, A, D>
242248
/// Implementation of `ArrayViewMut::from(&mut S)` where `S` is a slice or slicable.
243249
///
244250
/// Create a one-dimensional read-write array view of the data in `slice`.
251+
///
252+
/// **Panics** if the slice length is greater than `isize::MAX`.
245253
impl<'a, A, Slice: ?Sized> From<&'a mut Slice> for ArrayViewMut<'a, A, Ix1>
246254
where Slice: AsMut<[A]>
247255
{
248256
fn from(slice: &'a mut Slice) -> Self {
249257
let xs = slice.as_mut();
258+
assert!(
259+
xs.len() <= ::std::isize::MAX as usize,
260+
"Slice length must fit in `isize`.",
261+
);
250262
unsafe {
251263
Self::from_shape_ptr(xs.len(), xs.as_mut_ptr())
252264
}

src/dimension/dimension_trait.rs

+70-13
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,27 @@ pub trait Dimension : Clone + Eq + Debug + Send + Sync + Default +
8383
self.slice().iter().fold(1, |s, &a| s * a as usize)
8484
}
8585

86-
/// Compute the size while checking for overflow.
86+
/// Compute the size (number of elements) while checking for overflow.
87+
///
88+
/// Returns `None` if the product of non-zero axis lengths overflows
89+
/// `isize`.
90+
///
91+
/// If `dim.size_checked()` returns `Some(size)`, a `Vec` is created of
92+
/// length `size`, and `strides` are created with `self.default_strides()`
93+
/// or `self.fortran_strides()`, then the invariants are met to construct
94+
/// an owned `Array` from the `Vec`, `dim`, and `strides`. (See the docs of
95+
/// `can_index_slice_not_custom` for a slightly more general case.)
8796
fn size_checked(&self) -> Option<usize> {
88-
self.slice().iter().fold(Some(1), |s, &a| s.and_then(|s_| s_.checked_mul(a)))
97+
let size_nonzero = self
98+
.slice()
99+
.iter()
100+
.filter(|&&d| d != 0)
101+
.try_fold(1usize, |acc, &d| acc.checked_mul(d))?;
102+
if size_nonzero > ::std::isize::MAX as usize {
103+
None
104+
} else {
105+
Some(self.size())
106+
}
89107
}
90108

91109
#[doc(hidden)]
@@ -109,12 +127,17 @@ pub trait Dimension : Clone + Eq + Debug + Send + Sync + Default +
109127
self.slice() == rhs.slice()
110128
}
111129

130+
/// Returns the strides for a standard layout array with the given shape.
131+
///
132+
/// If the array is non-empty, the strides result in contiguous layout; if
133+
/// the array is empty, the strides are all zeros.
112134
#[doc(hidden)]
113135
fn default_strides(&self) -> Self {
114136
// Compute default array strides
115137
// Shape (a, b, c) => Give strides (b * c, c, 1)
116-
let mut strides = self.clone();
117-
{
138+
let mut strides = Self::zeros(self.ndim());
139+
// For empty arrays, use all zero strides.
140+
if self.slice().iter().all(|&d| d != 0) {
118141
let mut it = strides.slice_mut().iter_mut().rev();
119142
// Set first element to 1
120143
while let Some(rs) = it.next() {
@@ -130,12 +153,17 @@ pub trait Dimension : Clone + Eq + Debug + Send + Sync + Default +
130153
strides
131154
}
132155

156+
/// Returns the strides for a Fortran layout array with the given shape.
157+
///
158+
/// If the array is non-empty, the strides result in contiguous layout; if
159+
/// the array is empty, the strides are all zeros.
133160
#[doc(hidden)]
134161
fn fortran_strides(&self) -> Self {
135162
// Compute fortran array strides
136163
// Shape (a, b, c) => Give strides (1, a, a * b)
137-
let mut strides = self.clone();
138-
{
164+
let mut strides = Self::zeros(self.ndim());
165+
// For empty arrays, use all zero strides.
166+
if self.slice().iter().all(|&d| d != 0) {
139167
let mut it = strides.slice_mut().iter_mut();
140168
// Set first element to 1
141169
while let Some(rs) = it.next() {
@@ -428,11 +456,22 @@ impl Dimension for Dim<[Ix; 1]> {
428456
#[inline]
429457
fn size(&self) -> usize { get!(self, 0) }
430458
#[inline]
431-
fn size_checked(&self) -> Option<usize> { Some(get!(self, 0)) }
459+
fn size_checked(&self) -> Option<usize> {
460+
let size = get!(self, 0);
461+
if size <= ::std::isize::MAX as usize {
462+
Some(size)
463+
} else {
464+
None
465+
}
466+
}
432467

433468
#[inline]
434469
fn default_strides(&self) -> Self {
435-
Ix1(1)
470+
if get!(self, 0) == 0 {
471+
Ix1(0)
472+
} else {
473+
Ix1(1)
474+
}
436475
}
437476

438477
#[inline]
@@ -533,7 +572,15 @@ impl Dimension for Dim<[Ix; 2]> {
533572
fn size_checked(&self) -> Option<usize> {
534573
let m = get!(self, 0);
535574
let n = get!(self, 1);
536-
(m as usize).checked_mul(n as usize)
575+
let size = m.checked_mul(n)?;
576+
if m <= ::std::isize::MAX as usize
577+
&& n <= ::std::isize::MAX as usize
578+
&& size <= ::std::isize::MAX as usize
579+
{
580+
Some(size)
581+
} else {
582+
None
583+
}
537584
}
538585

539586
#[inline]
@@ -548,13 +595,23 @@ impl Dimension for Dim<[Ix; 2]> {
548595

549596
#[inline]
550597
fn default_strides(&self) -> Self {
551-
// Compute default array strides
552-
// Shape (a, b, c) => Give strides (b * c, c, 1)
553-
Ix2(get!(self, 1), 1)
598+
let m = get!(self, 0);
599+
let n = get!(self, 1);
600+
if m == 0 || n == 0 {
601+
Ix2(0, 0)
602+
} else {
603+
Ix2(n, 1)
604+
}
554605
}
555606
#[inline]
556607
fn fortran_strides(&self) -> Self {
557-
Ix2(1, get!(self, 0))
608+
let m = get!(self, 0);
609+
let n = get!(self, 1);
610+
if m == 0 || n == 0 {
611+
Ix2(0, 0)
612+
} else {
613+
Ix2(1, m)
614+
}
558615
}
559616

560617
#[inline]

0 commit comments

Comments
 (0)