@@ -7,14 +7,16 @@ use hdf5_sys::{
7
7
h5d:: H5Dopen2 ,
8
8
h5g:: { H5G_info_t , H5Gcreate2 , H5Gget_info , H5Gopen2 } ,
9
9
h5l:: {
10
- H5L_info_t , H5L_iterate_t , H5Lcreate_external , H5Lcreate_hard , H5Lcreate_soft , H5Ldelete ,
11
- H5Lexists , H5Literate , H5Lmove , H5L_SAME_LOC ,
10
+ H5L_info_t , H5L_iterate_t , H5L_type_t , H5Lcreate_external , H5Lcreate_hard , H5Lcreate_soft ,
11
+ H5Ldelete , H5Lexists , H5Literate , H5Lmove , H5L_SAME_LOC ,
12
12
} ,
13
13
h5p:: { H5Pcreate , H5Pset_create_intermediate_group } ,
14
+ h5t:: H5T_cset_t ,
14
15
} ;
15
16
16
17
use crate :: globals:: H5P_LINK_CREATE ;
17
18
use crate :: internal_prelude:: * ;
19
+ use crate :: { Location , LocationType } ;
18
20
19
21
/// Represents the HDF5 group object.
20
22
#[ repr( transparent) ]
@@ -212,37 +214,193 @@ impl Group {
212
214
let name = to_cstring ( name) ?;
213
215
Dataset :: from_id ( h5try ! ( H5Dopen2 ( self . id( ) , name. as_ptr( ) , H5P_DEFAULT ) ) )
214
216
}
217
+ }
215
218
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 > > ( ) ) } ;
219
+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
220
+ pub enum TraversalOrder {
221
+ Name ,
222
+ Creation ,
223
+ }
223
224
224
- other_data. push ( string_from_cstr ( name) ) ;
225
+ impl Default for TraversalOrder {
226
+ fn default ( ) -> Self {
227
+ Self :: Name
228
+ }
229
+ }
230
+
231
+ impl From < TraversalOrder > for H5_index_t {
232
+ fn from ( v : TraversalOrder ) -> Self {
233
+ match v {
234
+ TraversalOrder :: Name => Self :: H5_INDEX_NAME ,
235
+ TraversalOrder :: Creation => Self :: H5_INDEX_CRT_ORDER ,
236
+ }
237
+ }
238
+ }
239
+
240
+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
241
+ pub enum IterationOrder {
242
+ Increasing ,
243
+ Decreasing ,
244
+ Native ,
245
+ }
246
+
247
+ impl Default for IterationOrder {
248
+ fn default ( ) -> Self {
249
+ Self :: Native
250
+ }
251
+ }
225
252
226
- 0 // Continue iteration
253
+ impl From < IterationOrder > for H5_iter_order_t {
254
+ fn from ( v : IterationOrder ) -> Self {
255
+ match v {
256
+ IterationOrder :: Increasing => Self :: H5_ITER_INC ,
257
+ IterationOrder :: Decreasing => Self :: H5_ITER_DEC ,
258
+ IterationOrder :: Native => Self :: H5_ITER_NATIVE ,
259
+ }
260
+ }
261
+ }
262
+
263
+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
264
+ pub enum LinkType {
265
+ Hard ,
266
+ Soft ,
267
+ External ,
268
+ }
269
+
270
+ impl From < H5L_type_t > for LinkType {
271
+ fn from ( link_type : H5L_type_t ) -> Self {
272
+ match link_type {
273
+ H5L_type_t :: H5L_TYPE_HARD => Self :: Hard ,
274
+ H5L_type_t :: H5L_TYPE_SOFT => Self :: Soft ,
275
+ _ => Self :: External ,
276
+ }
277
+ }
278
+ }
279
+
280
+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
281
+ pub struct LinkInfo {
282
+ pub link_type : LinkType ,
283
+ pub creation_order : Option < i64 > ,
284
+ pub is_utf8 : bool ,
285
+ }
286
+
287
+ impl From < & H5L_info_t > for LinkInfo {
288
+ fn from ( link : & H5L_info_t ) -> Self {
289
+ let link_type = link. type_ . into ( ) ;
290
+ let creation_order = if link. corder_valid == 1 { Some ( link. corder ) } else { None } ;
291
+ let is_utf8 = link. cset == H5T_cset_t :: H5T_CSET_UTF8 ;
292
+ Self { link_type, creation_order, is_utf8 }
293
+ }
294
+ }
295
+
296
+ /// Iteration methods
297
+ impl Group {
298
+ /// Visits all objects in the group
299
+ pub fn iter_visit < F , G > (
300
+ & self , iteration_order : IterationOrder , traversal_order : TraversalOrder , mut val : G ,
301
+ mut op : F ,
302
+ ) -> Result < G >
303
+ where
304
+ F : Fn ( & Self , & str , LinkInfo , & mut G ) -> bool ,
305
+ {
306
+ /// Struct used to pass a tuple
307
+ struct Vtable < ' a , F , D > {
308
+ f : & ' a mut F ,
309
+ d : & ' a mut D ,
310
+ }
311
+ // Maps a closure to a C callback
312
+ //
313
+ // This function will be called multiple times, but never concurrently
314
+ extern "C" fn callback < F , G > (
315
+ id : hid_t , name : * const c_char , info : * const H5L_info_t , op_data : * mut c_void ,
316
+ ) -> herr_t
317
+ where
318
+ F : FnMut ( & Group , & str , LinkInfo , & mut G ) -> bool ,
319
+ {
320
+ panic:: catch_unwind ( || {
321
+ let vtable = op_data. cast :: < Vtable < F , G > > ( ) ;
322
+ let vtable = unsafe { vtable. as_mut ( ) . expect ( "iter_visit: null op_data ptr" ) } ;
323
+ unsafe { name. as_ref ( ) . expect ( "iter_visit: null name ptr" ) } ;
324
+ let name = unsafe { std:: ffi:: CStr :: from_ptr ( name) } ;
325
+ let info = unsafe { info. as_ref ( ) . expect ( "iter_vist: null info ptr" ) } ;
326
+ let handle = Handle :: try_new ( id) . expect ( "iter_visit: unable to create a handle" ) ;
327
+ handle. incref ( ) ;
328
+ let group = Group :: from_handle ( handle) ;
329
+ if ( vtable. f ) ( & group, name. to_string_lossy ( ) . as_ref ( ) , info. into ( ) , vtable. d ) {
330
+ 0
331
+ } else {
332
+ 1
333
+ }
227
334
} )
228
335
. unwrap_or ( -1 )
229
336
}
230
337
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 > ( ) ;
338
+ let callback_fn: H5L_iterate_t = Some ( callback :: < F , G > ) ;
339
+ let iter_pos: * mut hsize_t = & mut 0_u64 ;
340
+
341
+ // Store our references on the heap
342
+ let mut vtable = Vtable { f : & mut op, d : & mut val } ;
343
+ let other_data = ( & mut vtable as * mut Vtable < _ , _ > ) . cast :: < c_void > ( ) ;
235
344
236
345
h5call ! ( H5Literate (
237
346
self . id( ) ,
238
- H5_index_t :: H5_INDEX_NAME ,
239
- H5_iter_order_t :: H5_ITER_INC ,
240
- iteration_position ,
347
+ traversal_order . into ( ) ,
348
+ iteration_order . into ( ) ,
349
+ iter_pos ,
241
350
callback_fn,
242
351
other_data
243
- ) ) ?;
352
+ ) )
353
+ . map ( |_| val)
354
+ }
355
+
356
+ /// Visits all objects in the group using default iteration/traversal order.
357
+ pub fn iter_visit_default < F , G > ( & self , val : G , op : F ) -> Result < G >
358
+ where
359
+ F : Fn ( & Self , & str , LinkInfo , & mut G ) -> bool ,
360
+ {
361
+ self . iter_visit ( IterationOrder :: default ( ) , TraversalOrder :: default ( ) , val, op)
362
+ }
363
+
364
+ fn get_all_of_type ( & self , loc_type : LocationType ) -> Result < Vec < Location > > {
365
+ self . iter_visit_default ( vec ! [ ] , |group, name, _info, objects| {
366
+ if let Ok ( info) = group. get_info_by_name ( name) {
367
+ if info. loc_type == loc_type {
368
+ if let Ok ( loc) = group. open_by_token ( info. token ) {
369
+ objects. push ( loc) ;
370
+ return true ; // ok, object extracted and pushed
371
+ }
372
+ } else {
373
+ return true ; // ok, object is of another type, skipped
374
+ }
375
+ }
376
+ false // an error occured somewhere along the way
377
+ } )
378
+ }
379
+
380
+ /// Returns all groups in the group, non-recursively
381
+ pub fn groups ( & self ) -> Result < Vec < Self > > {
382
+ self . get_all_of_type ( LocationType :: Group )
383
+ . map ( |vec| vec. into_iter ( ) . map ( |obj| unsafe { obj. cast ( ) } ) . collect ( ) )
384
+ }
244
385
245
- Ok ( result)
386
+ /// Returns all datasets in the group, non-recursively
387
+ pub fn datasets ( & self ) -> Result < Vec < Dataset > > {
388
+ self . get_all_of_type ( LocationType :: Dataset )
389
+ . map ( |vec| vec. into_iter ( ) . map ( |obj| unsafe { obj. cast ( ) } ) . collect ( ) )
390
+ }
391
+
392
+ /// Returns all named types in the group, non-recursively
393
+ pub fn named_datatypes ( & self ) -> Result < Vec < Datatype > > {
394
+ self . get_all_of_type ( LocationType :: NamedDatatype )
395
+ . map ( |vec| vec. into_iter ( ) . map ( |obj| unsafe { obj. cast ( ) } ) . collect ( ) )
396
+ }
397
+
398
+ /// Returns the names of all objects in the group, non-recursively.
399
+ pub fn member_names ( & self ) -> Result < Vec < String > > {
400
+ self . iter_visit_default ( vec ! [ ] , |_, name, _, names| {
401
+ names. push ( name. to_owned ( ) ) ;
402
+ true
403
+ } )
246
404
}
247
405
}
248
406
@@ -471,4 +629,32 @@ pub mod tests {
471
629
assert_eq ! ( dset2. read_scalar:: <i32 >( ) . unwrap( ) , 13 ) ;
472
630
} )
473
631
}
632
+
633
+ #[ test]
634
+ pub fn test_iterators ( ) {
635
+ with_tmp_file ( |file| {
636
+ file. create_group ( "a" ) . unwrap ( ) ;
637
+ file. create_group ( "b" ) . unwrap ( ) ;
638
+ let group_a = file. group ( "a" ) . unwrap ( ) ;
639
+ let _group_b = file. group ( "b" ) . unwrap ( ) ;
640
+ file. new_dataset :: < u32 > ( ) . shape ( ( 10 , 20 ) ) . create ( "a/foo" ) . unwrap ( ) ;
641
+ file. new_dataset :: < u32 > ( ) . shape ( ( 10 , 20 ) ) . create ( "a/123" ) . unwrap ( ) ;
642
+ file. new_dataset :: < u32 > ( ) . shape ( ( 10 , 20 ) ) . create ( "a/bar" ) . unwrap ( ) ;
643
+
644
+ let groups = file. groups ( ) . unwrap ( ) ;
645
+ assert_eq ! ( groups. len( ) , 2 ) ;
646
+ for group in groups {
647
+ assert ! ( matches!( group. name( ) . as_ref( ) , "/a" | "/b" ) ) ;
648
+ }
649
+
650
+ let datasets = file. datasets ( ) . unwrap ( ) ;
651
+ assert_eq ! ( datasets. len( ) , 0 ) ;
652
+
653
+ let datasets = group_a. datasets ( ) . unwrap ( ) ;
654
+ assert_eq ! ( datasets. len( ) , 3 ) ;
655
+ for dataset in datasets {
656
+ assert ! ( matches!( dataset. name( ) . as_ref( ) , "/a/foo" | "/a/123" | "/a/bar" ) ) ;
657
+ }
658
+ } )
659
+ }
474
660
}
0 commit comments