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