@@ -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,143 @@ 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+ self . strides . clone ( )
281+ } ;
282+
283+ unsafe {
284+ // grow backing storage and update head ptr
285+ debug_assert_eq ! ( self . data. as_ptr( ) , self . as_ptr( ) ) ;
286+ self . data . reserve ( len_to_append) ;
287+ self . ptr = self . data . as_nonnull_mut ( ) ; // because we are standard order
288+
289+ // copy elements from view to the array now
290+ //
291+ // make a raw view with the new row
292+ // safe because the data was "full"
293+ let tail_ptr = self . data . as_end_nonnull ( ) ;
294+ let tail_view = RawArrayViewMut :: new ( tail_ptr, array_shape, strides. clone ( ) ) ;
295+
296+ struct SetLenOnDrop < ' a , A : ' a > {
297+ len : usize ,
298+ data : & ' a mut OwnedRepr < A > ,
299+ }
300+
301+ let mut length_guard = SetLenOnDrop {
302+ len : self . data . len ( ) ,
303+ data : & mut self . data ,
304+ } ;
305+
306+ impl < A > Drop for SetLenOnDrop < ' _ , A > {
307+ fn drop ( & mut self ) {
308+ unsafe {
309+ self . data . set_len ( self . len ) ;
310+ }
311+ }
312+ }
313+
314+ // we have a problem here XXX
315+ //
316+ // To be robust for panics and drop the right elements, we want
317+ // to fill the tail in-order, so that we can drop the right elements on
318+ // panic. Don't know how to achieve that.
319+ //
320+ // It might be easier to retrace our steps in a scope guard to drop the right
321+ // elements.. (PartialArray style).
322+ //
323+ // assign the new elements
324+ Zip :: from ( tail_view) . and ( array)
325+ . for_each ( |to, from| {
326+ to. write ( from. clone ( ) ) ;
327+ length_guard. len += 1 ;
328+ } ) ;
329+
330+ //length_guard.len += len_to_append;
331+ dbg ! ( len_to_append) ;
332+ drop ( length_guard) ;
333+
334+ // update array dimension
335+ self . strides = strides;
336+ self . dim = res_dim;
337+ dbg ! ( & self . dim) ;
338+
339+ }
340+ // multiple assertions after pointer & dimension update
341+ debug_assert_eq ! ( self . data. len( ) , self . len( ) ) ;
342+ debug_assert_eq ! ( self . len( ) , new_len) ;
343+ debug_assert ! ( self . is_standard_layout( ) ) ;
344+
345+ Ok ( ( ) )
346+ }
347+ }
0 commit comments