@@ -10,6 +10,7 @@ use hdf5_sys::{
10
10
H5L_info_t , H5L_iterate_t , H5Lcreate_external , H5Lcreate_hard , H5Lcreate_soft , H5Ldelete ,
11
11
H5Lexists , H5Literate , H5Lmove , H5L_SAME_LOC ,
12
12
} ,
13
+ h5o:: H5O_type_t :: { self , H5O_TYPE_DATASET , H5O_TYPE_GROUP , H5O_TYPE_NAMED_DATATYPE } ,
13
14
h5p:: { H5Pcreate , H5Pset_create_intermediate_group } ,
14
15
} ;
15
16
@@ -212,37 +213,196 @@ impl Group {
212
213
let name = to_cstring ( name) ?;
213
214
Dataset :: from_id ( h5try ! ( H5Dopen2 ( self . id( ) , name. as_ptr( ) , H5P_DEFAULT ) ) )
214
215
}
216
+ }
215
217
216
- /// Returns names of all the members in the group, non-recursively.
217
- pub fn member_names ( & self ) -> Result < Vec < String > > {
218
- extern "C" fn members_callback (
219
- _id : hid_t , name : * const c_char , _info : * const H5L_info_t , op_data : * mut c_void ,
220
- ) -> herr_t {
221
- panic:: catch_unwind ( || {
222
- let other_data: & mut Vec < String > = unsafe { & mut * ( op_data. cast :: < Vec < String > > ( ) ) } ;
218
+ pub enum TraversalOrder {
219
+ Lexicographic ,
220
+ Creation ,
221
+ }
222
+
223
+ impl From < TraversalOrder > for H5_index_t {
224
+ fn from ( v : TraversalOrder ) -> Self {
225
+ use hdf5_sys:: h5:: { H5_INDEX_CRT_ORDER , H5_INDEX_NAME } ;
226
+ match v {
227
+ TraversalOrder :: Lexicographic => H5_INDEX_NAME ,
228
+ TraversalOrder :: Creation => H5_INDEX_CRT_ORDER ,
229
+ }
230
+ }
231
+ }
232
+
233
+ pub enum IterationOrder {
234
+ Increasing ,
235
+ Decreasing ,
236
+ Native ,
237
+ }
223
238
224
- other_data. push ( string_from_cstr ( name) ) ;
239
+ impl From < IterationOrder > for H5_iter_order_t {
240
+ fn from ( v : IterationOrder ) -> Self {
241
+ use hdf5_sys:: h5:: { H5_ITER_DEC , H5_ITER_INC , H5_ITER_NATIVE } ;
242
+ match v {
243
+ IterationOrder :: Increasing => H5_ITER_INC ,
244
+ IterationOrder :: Decreasing => H5_ITER_DEC ,
245
+ IterationOrder :: Native => H5_ITER_NATIVE ,
246
+ }
247
+ }
248
+ }
225
249
226
- 0 // Continue iteration
250
+ /// Iteration methods
251
+ impl Group {
252
+ /// Visits all objects in the group
253
+ pub fn iter_visit < F , G > (
254
+ & self , mut op : F , mut val : G , order : ( IterationOrder , TraversalOrder ) ,
255
+ ) -> Result < G >
256
+ where
257
+ F : Fn ( & Self , & std:: ffi:: CStr , & H5L_info_t , & mut G ) -> bool ,
258
+ {
259
+ /// Struct used to pass a tuple
260
+ struct Vtable < ' a , F , D > {
261
+ f : & ' a mut F ,
262
+ d : & ' a mut D ,
263
+ }
264
+ // Maps a closure to a C callback
265
+ //
266
+ // This function will be called multiple times, but never concurrently
267
+ extern "C" fn callback < F , G > (
268
+ id : hid_t , name : * const c_char , info : * const H5L_info_t , op_data : * mut c_void ,
269
+ ) -> herr_t
270
+ where
271
+ F : FnMut ( & Group , & std:: ffi:: CStr , & H5L_info_t , & mut G ) -> bool ,
272
+ {
273
+ panic:: catch_unwind ( || {
274
+ let vtable = op_data. cast :: < Vtable < F , G > > ( ) ;
275
+ let vtable = unsafe { vtable. as_mut ( ) . expect ( "op_data is always non-null" ) } ;
276
+ let name = unsafe { std:: ffi:: CStr :: from_ptr ( name) } ;
277
+ let info = unsafe { info. as_ref ( ) . unwrap ( ) } ;
278
+ let handle = Handle :: try_new ( id) . unwrap ( ) ;
279
+ handle. incref ( ) ;
280
+ let group = Group :: from_handle ( handle) ;
281
+ if ( vtable. f ) ( & group, name, info, vtable. d ) {
282
+ 0
283
+ } else {
284
+ 1
285
+ }
227
286
} )
228
287
. unwrap_or ( -1 )
229
288
}
230
289
231
- let callback_fn: H5L_iterate_t = Some ( members_callback) ;
232
- let iteration_position: * mut hsize_t = & mut { 0_u64 } ;
233
- let mut result: Vec < String > = Vec :: new ( ) ;
234
- let other_data: * mut c_void = ( & mut result as * mut Vec < String > ) . cast :: < c_void > ( ) ;
290
+ let callback_fn: H5L_iterate_t = Some ( callback :: < F , G > ) ;
291
+ let iter_pos: * mut hsize_t = & mut 0_u64 ;
292
+
293
+ // Store our references on the heap
294
+ let mut vtable = Vtable { f : & mut op, d : & mut val } ;
295
+ let other_data = ( & mut vtable as * mut Vtable < _ , _ > ) . cast :: < c_void > ( ) ;
235
296
236
297
h5call ! ( H5Literate (
237
298
self . id( ) ,
238
- H5_index_t :: H5_INDEX_NAME ,
239
- H5_iter_order_t :: H5_ITER_INC ,
240
- iteration_position ,
299
+ order . 1 . into ( ) ,
300
+ order . 0 . into ( ) ,
301
+ iter_pos ,
241
302
callback_fn,
242
303
other_data
243
- ) ) ?;
304
+ ) )
305
+ . map ( |_| val)
306
+ }
307
+
308
+ fn get_all_of_type ( & self , typ : H5O_type_t ) -> Result < Vec < Handle > > {
309
+ #[ cfg( not( hdf5_1_10_3) ) ]
310
+ use hdf5_sys:: h5o:: { H5Oget_info_by_name1 , H5Oopen_by_addr } ;
311
+ #[ cfg( all( hdf5_1_10_3, not( hdf5_1_12_0) ) ) ]
312
+ use hdf5_sys:: h5o:: { H5Oget_info_by_name2 , H5Oopen_by_addr , H5O_INFO_BASIC } ;
313
+ #[ cfg( hdf5_1_12_0) ]
314
+ use hdf5_sys:: h5o:: { H5Oget_info_by_name3 , H5Oopen_by_token , H5O_INFO_BASIC } ;
315
+
316
+ let objects = vec ! [ ] ;
317
+
318
+ self . iter_visit (
319
+ |group, name, _info, objects| {
320
+ let mut infobuf = std:: mem:: MaybeUninit :: uninit ( ) ;
321
+ #[ cfg( hdf5_1_12_0) ]
322
+ if h5call ! ( H5Oget_info_by_name3 (
323
+ group. id( ) ,
324
+ name. as_ptr( ) ,
325
+ infobuf. as_mut_ptr( ) ,
326
+ H5O_INFO_BASIC ,
327
+ H5P_DEFAULT
328
+ ) )
329
+ . is_err ( )
330
+ {
331
+ return true ;
332
+ } ;
333
+ #[ cfg( all( hdf5_1_10_3, not( hdf5_1_12_0) ) ) ]
334
+ if h5call ! ( H5Oget_info_by_name2 (
335
+ group. id( ) ,
336
+ name. as_ptr( ) ,
337
+ infobuf. as_mut_ptr( ) ,
338
+ H5O_INFO_BASIC ,
339
+ H5P_DEFAULT
340
+ ) )
341
+ . is_err ( )
342
+ {
343
+ return true ;
344
+ } ;
345
+ #[ cfg( not( hdf5_1_10_3) ) ]
346
+ if h5call ! ( H5Oget_info_by_name1 (
347
+ group. id( ) ,
348
+ name. as_ptr( ) ,
349
+ infobuf. as_mut_ptr( ) ,
350
+ H5P_DEFAULT
351
+ ) )
352
+ . is_err ( )
353
+ {
354
+ return true ;
355
+ } ;
356
+ let infobuf = unsafe { infobuf. assume_init ( ) } ;
357
+ if infobuf. type_ == typ {
358
+ #[ cfg( hdf5_1_12_0) ]
359
+ if let Ok ( id) = h5call ! ( H5Oopen_by_token ( group. id( ) , infobuf. token) ) {
360
+ if let Ok ( handle) = Handle :: try_new ( id) {
361
+ objects. push ( handle) ;
362
+ }
363
+ }
364
+ #[ cfg( not( hdf5_1_12_0) ) ]
365
+ if let Ok ( id) = h5call ! ( H5Oopen_by_addr ( group. id( ) , infobuf. addr) ) {
366
+ if let Ok ( handle) = Handle :: try_new ( id) {
367
+ objects. push ( handle) ;
368
+ }
369
+ }
370
+ }
371
+ true
372
+ } ,
373
+ objects,
374
+ ( IterationOrder :: Native , TraversalOrder :: Lexicographic ) ,
375
+ )
376
+ }
377
+
378
+ /// Returns all groups in the group, non-recursively
379
+ pub fn groups ( & self ) -> Result < Vec < Self > > {
380
+ self . get_all_of_type ( H5O_TYPE_GROUP )
381
+ . map ( |vec| vec. into_iter ( ) . map ( Self :: from_handle) . collect ( ) )
382
+ }
383
+
384
+ /// Returns all datasets in the group, non-recursively
385
+ pub fn datasets ( & self ) -> Result < Vec < Dataset > > {
386
+ self . get_all_of_type ( H5O_TYPE_DATASET )
387
+ . map ( |vec| vec. into_iter ( ) . map ( Dataset :: from_handle) . collect ( ) )
388
+ }
389
+
390
+ /// Returns all named types in the group, non-recursively
391
+ pub fn datatypes ( & self ) -> Result < Vec < Datatype > > {
392
+ self . get_all_of_type ( H5O_TYPE_NAMED_DATATYPE )
393
+ . map ( |vec| vec. into_iter ( ) . map ( Datatype :: from_handle) . collect ( ) )
394
+ }
244
395
245
- Ok ( result)
396
+ /// Returns the names of all objects in the group, non-recursively.
397
+ pub fn member_names ( & self ) -> Result < Vec < String > > {
398
+ self . iter_visit (
399
+ |_, name, _, names| {
400
+ names. push ( name. to_str ( ) . unwrap ( ) . to_owned ( ) ) ;
401
+ true
402
+ } ,
403
+ vec ! [ ] ,
404
+ ( IterationOrder :: Native , TraversalOrder :: Lexicographic ) ,
405
+ )
246
406
}
247
407
}
248
408
@@ -471,4 +631,32 @@ pub mod tests {
471
631
assert_eq ! ( dset2. read_scalar:: <i32 >( ) . unwrap( ) , 13 ) ;
472
632
} )
473
633
}
634
+
635
+ #[ test]
636
+ pub fn test_iterators ( ) {
637
+ with_tmp_file ( |file| {
638
+ file. create_group ( "a" ) . unwrap ( ) ;
639
+ file. create_group ( "b" ) . unwrap ( ) ;
640
+ let group_a = file. group ( "a" ) . unwrap ( ) ;
641
+ let _group_b = file. group ( "b" ) . unwrap ( ) ;
642
+ file. new_dataset :: < u32 > ( ) . shape ( ( 10 , 20 ) ) . create ( "a/foo" ) . unwrap ( ) ;
643
+ file. new_dataset :: < u32 > ( ) . shape ( ( 10 , 20 ) ) . create ( "a/123" ) . unwrap ( ) ;
644
+ file. new_dataset :: < u32 > ( ) . shape ( ( 10 , 20 ) ) . create ( "a/bar" ) . unwrap ( ) ;
645
+
646
+ let groups = file. groups ( ) . unwrap ( ) ;
647
+ assert_eq ! ( groups. len( ) , 2 ) ;
648
+ for group in groups {
649
+ assert ! ( matches!( group. name( ) . as_ref( ) , "/a" | "/b" ) ) ;
650
+ }
651
+
652
+ let datasets = file. datasets ( ) . unwrap ( ) ;
653
+ assert_eq ! ( datasets. len( ) , 0 ) ;
654
+
655
+ let datasets = group_a. datasets ( ) . unwrap ( ) ;
656
+ assert_eq ! ( datasets. len( ) , 3 ) ;
657
+ for dataset in datasets {
658
+ assert ! ( matches!( dataset. name( ) . as_ref( ) , "/a/foo" | "/a/123" | "/a/bar" ) ) ;
659
+ }
660
+ } )
661
+ }
474
662
}
0 commit comments