Skip to content

Commit 90dfc6c

Browse files
SparrowLiibluss
authored andcommitted
Added memory continuity test with negative step size. Update documentation
1 parent f0dafb3 commit 90dfc6c

File tree

5 files changed

+81
-20
lines changed

5 files changed

+81
-20
lines changed

src/dimension/dimension_trait.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,8 @@ pub trait Dimension:
314314
*elt = i;
315315
}
316316
let strides = self.slice();
317-
indices.slice_mut()
317+
indices
318+
.slice_mut()
318319
.sort_by_key(|&i| (strides[i] as isize).abs());
319320
indices
320321
}

src/dimension/mod.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,7 @@ where
207207
///
208208
/// 2. The product of non-zero axis lengths must not exceed `isize::MAX`.
209209
///
210-
/// 3. For axes with length > 1, the pointer cannot move outside the
211-
/// slice. For axes with length ≤ 1, the stride can be anything.
212-
///
213-
/// 4. If the array will be empty (any axes are zero-length), the difference
210+
/// 3. If the array will be empty (any axes are zero-length), the difference
214211
/// between the least address and greatest address accessible by moving
215212
/// along all axes must be ≤ `data.len()`. (It's fine in this case to move
216213
/// one byte past the end of the slice since the pointers will be offset but
@@ -221,13 +218,19 @@ where
221218
/// `data.len()`. This and #3 ensure that all dereferenceable pointers point
222219
/// to elements within the slice.
223220
///
224-
/// 5. The strides must not allow any element to be referenced by two different
221+
/// 4. The strides must not allow any element to be referenced by two different
225222
/// indices.
226223
///
227224
/// Note that since slices cannot contain more than `isize::MAX` bytes,
228225
/// condition 4 is sufficient to guarantee that the absolute difference in
229226
/// units of `A` and in units of bytes between the least address and greatest
230227
/// address accessible by moving along all axes does not exceed `isize::MAX`.
228+
///
229+
/// Warning: This function is sufficient to check the invariants of ArrayBase only
230+
/// if the pointer to the first element of the array is chosen such that the element
231+
/// with the smallest memory address is at the start of data. (In other words, the
232+
/// pointer to the first element of the array must be computed using offset_from_ptr_to_memory
233+
/// so that negative strides are correctly handled.)
231234
pub(crate) fn can_index_slice<A, D: Dimension>(
232235
data: &[A],
233236
dim: &D,
@@ -244,7 +247,7 @@ fn can_index_slice_impl<D: Dimension>(
244247
dim: &D,
245248
strides: &D,
246249
) -> Result<(), ShapeError> {
247-
// Check condition 4.
250+
// Check condition 3.
248251
let is_empty = dim.slice().iter().any(|&d| d == 0);
249252
if is_empty && max_offset > data_len {
250253
return Err(from_kind(ErrorKind::OutOfBounds));
@@ -253,7 +256,7 @@ fn can_index_slice_impl<D: Dimension>(
253256
return Err(from_kind(ErrorKind::OutOfBounds));
254257
}
255258

256-
// Check condition 5.
259+
// Check condition 4.
257260
if !is_empty && dim_stride_overlap(dim, strides) {
258261
return Err(from_kind(ErrorKind::Unsupported));
259262
}
@@ -384,8 +387,8 @@ fn to_abs_slice(axis_len: usize, slice: Slice) -> (usize, usize, isize) {
384387

385388
/// This function computes the offset from the logically first element to the first element in
386389
/// memory of the array. The result is always <= 0.
387-
pub fn offset_from_ptr_to_memory(dim: &[Ix], strides: &[Ix]) -> isize {
388-
let offset = izip!(dim, strides).fold(0, |_offset, (d, s)| {
390+
pub fn offset_from_ptr_to_memory<D: Dimension>(dim: &D, strides: &D) -> isize {
391+
let offset = izip!(dim.slice(), strides.slice()).fold(0, |_offset, (d, s)| {
389392
if (*s as isize) < 0 {
390393
_offset + *s as isize * (*d as isize - 1)
391394
} else {

src/impl_constructors.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -460,8 +460,7 @@ where
460460
// debug check for issues that indicates wrong use of this constructor
461461
debug_assert!(dimension::can_index_slice(&v, &dim, &strides).is_ok());
462462
ArrayBase {
463-
ptr: nonnull_from_vec_data(&mut v)
464-
.offset(offset_from_ptr_to_memory(dim.slice(), strides.slice()).abs()),
463+
ptr: nonnull_from_vec_data(&mut v).offset(-offset_from_ptr_to_memory(&dim, &strides)),
465464
data: DataOwned::new(v),
466465
strides,
467466
dim,

src/impl_methods.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -154,12 +154,12 @@ where
154154

155155
/// Return an uniquely owned copy of the array.
156156
///
157-
/// If the input array is contiguous and its strides are positive, then the
158-
/// output array will have the same memory layout. Otherwise, the layout of
159-
/// the output array is unspecified. If you need a particular layout, you
160-
/// can allocate a new array with the desired memory layout and
161-
/// [`.assign()`](#method.assign) the data. Alternatively, you can collect
162-
/// an iterator, like this for a result in standard layout:
157+
/// If the input array is contiguous, then the output array will have the same
158+
/// memory layout. Otherwise, the layout of the output array is unspecified.
159+
/// If you need a particular layout, you can allocate a new array with the
160+
/// desired memory layout and [`.assign()`](#method.assign) the data.
161+
/// Alternatively, you can collectan iterator, like this for a result in
162+
/// standard layout:
163163
///
164164
/// ```
165165
/// # use ndarray::prelude::*;
@@ -1407,7 +1407,7 @@ where
14071407
S: Data,
14081408
{
14091409
if self.is_contiguous() {
1410-
let offset = offset_from_ptr_to_memory(self.dim.slice(), self.strides.slice());
1410+
let offset = offset_from_ptr_to_memory(&self.dim, &self.strides);
14111411
unsafe {
14121412
Some(slice::from_raw_parts(
14131413
self.ptr.offset(offset).as_ptr(),
@@ -1427,7 +1427,7 @@ where
14271427
{
14281428
if self.is_contiguous() {
14291429
self.ensure_unique();
1430-
let offset = offset_from_ptr_to_memory(self.dim.slice(), self.strides.slice());
1430+
let offset = offset_from_ptr_to_memory(&self.dim, &self.strides);
14311431
unsafe {
14321432
Some(slice::from_raw_parts_mut(
14331433
self.ptr.offset(offset).as_ptr(),

tests/array.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1786,6 +1786,64 @@ fn test_contiguous() {
17861786
assert!(b.as_slice_memory_order().is_some());
17871787
}
17881788

1789+
#[test]
1790+
fn test_contiguous_neg_strides() {
1791+
let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
1792+
let mut a = ArrayView::from_shape((2, 3, 2).strides((1, 4, 2)), &s).unwrap();
1793+
assert_eq!(
1794+
a,
1795+
arr3(&[[[0, 2], [4, 6], [8, 10]], [[1, 3], [5, 7], [9, 11]]])
1796+
);
1797+
assert!(a.as_slice_memory_order().is_some());
1798+
1799+
let mut b = a.slice(s![..;1, ..;-1, ..;-1]);
1800+
assert_eq!(
1801+
b,
1802+
arr3(&[[[10, 8], [6, 4], [2, 0]], [[11, 9], [7, 5], [3, 1]]])
1803+
);
1804+
assert!(b.as_slice_memory_order().is_some());
1805+
1806+
b.swap_axes(1, 2);
1807+
assert_eq!(b, arr3(&[[[10, 6, 2], [8, 4, 0]], [[11, 7, 3], [9, 5, 1]]]));
1808+
assert!(b.as_slice_memory_order().is_some());
1809+
1810+
b.invert_axis(Axis(0));
1811+
assert_eq!(b, arr3(&[[[11, 7, 3], [9, 5, 1]], [[10, 6, 2], [8, 4, 0]]]));
1812+
assert!(b.as_slice_memory_order().is_some());
1813+
1814+
let mut c = b.reversed_axes();
1815+
assert_eq!(
1816+
c,
1817+
arr3(&[[[11, 10], [9, 8]], [[7, 6], [5, 4]], [[3, 2], [1, 0]]])
1818+
);
1819+
assert!(c.as_slice_memory_order().is_some());
1820+
1821+
c.merge_axes(Axis(1), Axis(2));
1822+
assert_eq!(c, arr3(&[[[11, 10, 9, 8]], [[7, 6, 5, 4]], [[3, 2, 1, 0]]]));
1823+
assert!(c.as_slice_memory_order().is_some());
1824+
1825+
let d = b.remove_axis(Axis(1));
1826+
assert_eq!(d, arr2(&[[11, 7, 3], [10, 6, 2]]));
1827+
assert!(d.as_slice_memory_order().is_none());
1828+
1829+
let e = b.remove_axis(Axis(2));
1830+
assert_eq!(e, arr2(&[[11, 9], [10, 8]]));
1831+
assert!(e.as_slice_memory_order().is_some());
1832+
1833+
let f = e.insert_axis(Axis(2));
1834+
assert_eq!(f, arr3(&[[[11], [9]], [[10], [8]]]));
1835+
assert!(f.as_slice_memory_order().is_some());
1836+
1837+
let mut g = b.clone();
1838+
g.collapse_axis(Axis(1), 0);
1839+
assert_eq!(g, arr3(&[[[11, 7, 3]], [[10, 6, 2]]]));
1840+
assert!(g.as_slice_memory_order().is_none());
1841+
1842+
b.collapse_axis(Axis(2), 0);
1843+
assert_eq!(b, arr3(&[[[11], [9]], [[10], [8]]]));
1844+
assert!(b.as_slice_memory_order().is_some());
1845+
}
1846+
17891847
#[test]
17901848
fn test_swap() {
17911849
let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9]]);

0 commit comments

Comments
 (0)