Skip to content

Commit 7f55efd

Browse files
committed
append: Adapt memory layout automatically in append if needed
1 parent 3d9a9ba commit 7f55efd

File tree

2 files changed

+67
-9
lines changed

2 files changed

+67
-9
lines changed

src/impl_owned_array.rs

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,45 @@ impl<A, D> Array<A, D>
279279
self.data
280280
}
281281

282+
/// Create an empty array with an all-zeros shape
283+
///
284+
/// ***Panics*** if D is zero-dimensional, because it can't be empty
285+
pub(crate) fn empty() -> Array<A, D> {
286+
assert_ne!(D::NDIM, Some(0));
287+
let ndim = D::NDIM.unwrap_or(1);
288+
Array::from_shape_simple_fn(D::zeros(ndim), || unreachable!())
289+
}
290+
291+
/// Create new_array with the right layout for appending to `growing_axis`
292+
#[inline(never)]
293+
fn change_to_contig_append_layout(&mut self, growing_axis: Axis) {
294+
let ndim = self.ndim();
295+
let mut dim = self.raw_dim();
296+
297+
// The array will be created with 0 (C) or ndim-1 (F) as the biggest stride
298+
// axis. Rearrange the shape so that `growing_axis` is the biggest stride axis
299+
// afterwards.
300+
let prefer_f_layout = growing_axis == Axis(ndim - 1);
301+
if !prefer_f_layout {
302+
dim.slice_mut().swap(0, growing_axis.index());
303+
}
304+
let mut new_array = Self::uninit(dim.set_f(prefer_f_layout));
305+
if !prefer_f_layout {
306+
new_array.swap_axes(0, growing_axis.index());
307+
}
308+
309+
// self -> old_self.
310+
// dummy array -> self.
311+
// old_self elements are moved -> new_array.
312+
let old_self = std::mem::replace(self, Self::empty());
313+
old_self.move_into(new_array.view_mut());
314+
315+
// new_array -> self.
316+
unsafe {
317+
*self = new_array.assume_init();
318+
}
319+
}
320+
282321

283322
/// Append an array to the array
284323
///
@@ -360,19 +399,27 @@ impl<A, D> Array<A, D>
360399
}
361400

362401
let self_is_empty = self.is_empty();
402+
let mut incompatible_layout = false;
363403

364404
// array must be empty or have `axis` as the outermost (longest stride) axis
365405
if !self_is_empty && current_axis_len > 1 {
366406
// `axis` must be max stride axis or equal to its stride
367407
let max_stride_axis = self.axes().max_by_key(|ax| ax.stride).unwrap();
368408
if max_stride_axis.axis != axis && max_stride_axis.stride > self.stride_of(axis) {
369-
return Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout));
409+
incompatible_layout = true;
370410
}
371411
}
372412

373413
// array must be be "full" (have no exterior holes)
374414
if self.len() != self.data.len() {
375-
return Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout));
415+
incompatible_layout = true;
416+
}
417+
418+
if incompatible_layout {
419+
self.change_to_contig_append_layout(axis);
420+
// safety-check parameters after remodeling
421+
debug_assert_eq!(self_is_empty, self.is_empty());
422+
debug_assert_eq!(current_axis_len, self.len_of(axis));
376423
}
377424

378425
let strides = if self_is_empty {

tests/append.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ fn append_row() {
1818
assert_eq!(a.try_append_column(aview1(&[1.])),
1919
Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)));
2020
assert_eq!(a.try_append_column(aview1(&[1., 2.])),
21-
Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout)));
21+
Ok(()));
22+
assert_eq!(a,
23+
array![[0., 1., 2., 3., 1.],
24+
[4., 5., 6., 7., 2.]]);
2225
}
2326

2427
#[test]
@@ -28,8 +31,7 @@ fn append_row_wrong_layout() {
2831
a.try_append_row(aview1(&[4., 5., 6., 7.])).unwrap();
2932
assert_eq!(a.shape(), &[2, 4]);
3033

31-
assert_eq!(a.try_append_column(aview1(&[1., 2.])),
32-
Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout)));
34+
//assert_eq!(a.try_append_column(aview1(&[1., 2.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout)));
3335

3436
assert_eq!(a,
3537
array![[0., 1., 2., 3.],
@@ -56,7 +58,13 @@ fn append_row_error() {
5658
assert_eq!(a.try_append_column(aview1(&[1.])),
5759
Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)));
5860
assert_eq!(a.try_append_column(aview1(&[1., 2., 3.])),
59-
Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout)));
61+
Ok(()));
62+
assert_eq!(a.t(),
63+
array![[0., 0., 0.],
64+
[0., 0., 0.],
65+
[0., 0., 0.],
66+
[0., 0., 0.],
67+
[1., 2., 3.]]);
6068
}
6169

6270
#[test]
@@ -76,7 +84,11 @@ fn append_row_existing() {
7684
assert_eq!(a.try_append_column(aview1(&[1.])),
7785
Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)));
7886
assert_eq!(a.try_append_column(aview1(&[1., 2., 3.])),
79-
Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout)));
87+
Ok(()));
88+
assert_eq!(a,
89+
array![[0., 0., 0., 0., 1.],
90+
[0., 1., 2., 3., 2.],
91+
[4., 5., 6., 7., 3.]]);
8092
}
8193

8294
#[test]
@@ -87,8 +99,7 @@ fn append_row_col_len_1() {
8799
a.try_append_column(aview1(&[2., 3.])).unwrap(); // shape 2 x 2
88100
assert_eq!(a.try_append_row(aview1(&[1.])),
89101
Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)));
90-
assert_eq!(a.try_append_row(aview1(&[1., 2.])),
91-
Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout)));
102+
//assert_eq!(a.try_append_row(aview1(&[1., 2.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout)));
92103
a.try_append_column(aview1(&[4., 5.])).unwrap(); // shape 2 x 3
93104
assert_eq!(a.shape(), &[2, 3]);
94105

0 commit comments

Comments
 (0)