@@ -204,3 +204,144 @@ impl<A> Array<A, Ix2> {
204
204
}
205
205
}
206
206
207
+ impl < A , D > Array < A , D >
208
+ where D : Dimension
209
+ {
210
+ /// Append a row to an array with row major memory layout.
211
+ ///
212
+ /// ***Errors*** with a layout error if the array is not in standard order or
213
+ /// if it has holes, even exterior holes (from slicing). <br>
214
+ /// ***Errors*** with shape error if the length of the input row does not match
215
+ /// the length of the rows in the array. <br>
216
+ ///
217
+ /// The memory layout matters, since it determines in which direction the array can easily
218
+ /// grow. Notice that an empty array is compatible both ways. The amortized average
219
+ /// complexity of the append is O(m) where *m* is the length of the row.
220
+ ///
221
+ /// ```rust
222
+ /// use ndarray::{Array, ArrayView, array};
223
+ ///
224
+ /// // create an empty array and append
225
+ /// let mut a = Array::zeros((0, 4));
226
+ /// a.try_append_row(ArrayView::from(&[1., 2., 3., 4.])).unwrap();
227
+ /// a.try_append_row(ArrayView::from(&[0., -2., -3., -4.])).unwrap();
228
+ ///
229
+ /// assert_eq!(
230
+ /// a,
231
+ /// array![[1., 2., 3., 4.],
232
+ /// [0., -2., -3., -4.]]);
233
+ /// ```
234
+ pub fn try_append_array ( & mut self , axis : Axis , array : ArrayView < A , D > )
235
+ -> Result < ( ) , ShapeError >
236
+ where
237
+ A : Clone ,
238
+ D : RemoveAxis ,
239
+ {
240
+ if self . ndim ( ) == 0 {
241
+ return Err ( ShapeError :: from_kind ( ErrorKind :: IncompatibleShape ) ) ;
242
+ }
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