@@ -6,6 +6,7 @@ use crate::dimension;
66use crate :: error:: { ErrorKind , ShapeError } ;
77use crate :: OwnedRepr ;
88use crate :: Zip ;
9+ use crate :: NdProducer ;
910
1011/// Methods specific to `Array0`.
1112///
@@ -204,3 +205,144 @@ impl<A> Array<A, Ix2> {
204205 }
205206}
206207
208+ impl < A , D > Array < A , D >
209+ where D : Dimension
210+ {
211+ /// Append a row to an array with row major memory layout.
212+ ///
213+ /// ***Errors*** with a layout error if the array is not in standard order or
214+ /// if it has holes, even exterior holes (from slicing). <br>
215+ /// ***Errors*** with shape error if the length of the input row does not match
216+ /// the length of the rows in the array. <br>
217+ ///
218+ /// The memory layout matters, since it determines in which direction the array can easily
219+ /// grow. Notice that an empty array is compatible both ways. The amortized average
220+ /// complexity of the append is O(m) where *m* is the length of the row.
221+ ///
222+ /// ```rust
223+ /// use ndarray::{Array, ArrayView, array};
224+ ///
225+ /// // create an empty array and append
226+ /// let mut a = Array::zeros((0, 4));
227+ /// a.try_append_row(ArrayView::from(&[1., 2., 3., 4.])).unwrap();
228+ /// a.try_append_row(ArrayView::from(&[0., -2., -3., -4.])).unwrap();
229+ ///
230+ /// assert_eq!(
231+ /// a,
232+ /// array![[1., 2., 3., 4.],
233+ /// [0., -2., -3., -4.]]);
234+ /// ```
235+ pub fn try_append_array ( & mut self , axis : Axis , array : ArrayView < A , D > )
236+ -> Result < ( ) , ShapeError >
237+ where
238+ A : Clone ,
239+ D : RemoveAxis ,
240+ {
241+ let self_axis_len = self . len_of ( axis) ;
242+ let array_axis_len = array. len_of ( axis) ;
243+
244+ let remaining_shape = self . raw_dim ( ) . remove_axis ( axis) ;
245+ let array_rem_shape = array. raw_dim ( ) . remove_axis ( axis) ;
246+
247+ if remaining_shape != array_rem_shape {
248+ return Err ( ShapeError :: from_kind ( ErrorKind :: IncompatibleShape ) ) ;
249+ }
250+
251+ let len_to_append = array. len ( ) ;
252+ if len_to_append == 0 {
253+ return Ok ( ( ) ) ;
254+ }
255+
256+ let array_shape = array. raw_dim ( ) ;
257+ let mut res_dim = self . raw_dim ( ) ;
258+ res_dim[ axis. index ( ) ] += array_shape[ axis. index ( ) ] ;
259+ let new_len = dimension:: size_of_shape_checked ( & res_dim) ?;
260+
261+ let self_is_empty = self . is_empty ( ) ;
262+
263+ // array must be empty or have `axis` as the outermost (longest stride)
264+ // axis
265+ if !( self_is_empty ||
266+ self . axes ( ) . max_by_key ( |ax| ax. stride ) . map ( |ax| ax. axis ) == Some ( axis) )
267+ {
268+ return Err ( ShapeError :: from_kind ( ErrorKind :: IncompatibleLayout ) ) ;
269+ }
270+
271+ // array must be be "full" (have no exterior holes)
272+ if self . len ( ) != self . data . len ( ) {
273+ return Err ( ShapeError :: from_kind ( ErrorKind :: IncompatibleLayout ) ) ;
274+ }
275+ let strides = if self_is_empty {
276+ // recompute strides - if the array was previously empty, it could have
277+ // zeros in strides.
278+ res_dim. default_strides ( )
279+ } else {
280+ let strides = self . strides . clone ( ) ;
281+ strides
282+ } ;
283+
284+ unsafe {
285+ // grow backing storage and update head ptr
286+ debug_assert_eq ! ( self . data. as_ptr( ) , self . as_ptr( ) ) ;
287+ self . data . reserve ( len_to_append) ;
288+ self . ptr = self . data . as_nonnull_mut ( ) ; // because we are standard order
289+
290+ // copy elements from view to the array now
291+ //
292+ // make a raw view with the new row
293+ // safe because the data was "full"
294+ let tail_ptr = self . data . as_end_nonnull ( ) ;
295+ let tail_view = RawArrayViewMut :: new ( tail_ptr, array_shape, strides. clone ( ) ) ;
296+
297+ struct SetLenOnDrop < ' a , A : ' a > {
298+ len : usize ,
299+ data : & ' a mut OwnedRepr < A > ,
300+ }
301+
302+ let mut length_guard = SetLenOnDrop {
303+ len : self . data . len ( ) ,
304+ data : & mut self . data ,
305+ } ;
306+
307+ impl < A > Drop for SetLenOnDrop < ' _ , A > {
308+ fn drop ( & mut self ) {
309+ unsafe {
310+ self . data . set_len ( self . len ) ;
311+ }
312+ }
313+ }
314+
315+ // we have a problem here XXX
316+ //
317+ // To be robust for panics and drop the right elements, we want
318+ // to fill the tail in-order, so that we can drop the right elements on
319+ // panic. Don't know how to achieve that.
320+ //
321+ // It might be easier to retrace our steps in a scope guard to drop the right
322+ // elements.. (PartialArray style).
323+ //
324+ // assign the new elements
325+ Zip :: from ( tail_view) . and ( array)
326+ . for_each ( |to, from| {
327+ to. write ( from. clone ( ) ) ;
328+ length_guard. len += 1 ;
329+ } ) ;
330+
331+ //length_guard.len += len_to_append;
332+ dbg ! ( len_to_append) ;
333+ drop ( length_guard) ;
334+
335+ // update array dimension
336+ self . strides = strides;
337+ self . dim = res_dim;
338+ dbg ! ( & self . dim) ;
339+
340+ }
341+ // multiple assertions after pointer & dimension update
342+ debug_assert_eq ! ( self . data. len( ) , self . len( ) ) ;
343+ debug_assert_eq ! ( self . len( ) , new_len) ;
344+ debug_assert ! ( self . is_standard_layout( ) ) ;
345+
346+ Ok ( ( ) )
347+ }
348+ }
0 commit comments