@@ -10,6 +10,7 @@ use hdf5_sys::{
10
10
H5L_info_t , H5L_iterate_t , H5Lcreate_hard , H5Lcreate_soft , H5Ldelete , H5Lexists ,
11
11
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
@@ -177,37 +178,196 @@ impl Group {
177
178
let name = to_cstring ( name) ?;
178
179
Dataset :: from_id ( h5try ! ( H5Dopen2 ( self . id( ) , name. as_ptr( ) , H5P_DEFAULT ) ) )
179
180
}
181
+ }
180
182
181
- /// Returns names of all the members in the group, non-recursively.
182
- pub fn member_names ( & self ) -> Result < Vec < String > > {
183
- extern "C" fn members_callback (
184
- _id : hid_t , name : * const c_char , _info : * const H5L_info_t , op_data : * mut c_void ,
185
- ) -> herr_t {
186
- panic:: catch_unwind ( || {
187
- let other_data: & mut Vec < String > = unsafe { & mut * ( op_data. cast :: < Vec < String > > ( ) ) } ;
183
+ pub enum TraversalOrder {
184
+ Lexicographic ,
185
+ Creation ,
186
+ }
187
+
188
+ impl From < TraversalOrder > for H5_index_t {
189
+ fn from ( v : TraversalOrder ) -> Self {
190
+ use hdf5_sys:: h5:: { H5_INDEX_CRT_ORDER , H5_INDEX_NAME } ;
191
+ match v {
192
+ TraversalOrder :: Lexicographic => H5_INDEX_NAME ,
193
+ TraversalOrder :: Creation => H5_INDEX_CRT_ORDER ,
194
+ }
195
+ }
196
+ }
197
+
198
+ pub enum IterationOrder {
199
+ Increasing ,
200
+ Decreasing ,
201
+ Native ,
202
+ }
188
203
189
- other_data. push ( string_from_cstr ( name) ) ;
204
+ impl From < IterationOrder > for H5_iter_order_t {
205
+ fn from ( v : IterationOrder ) -> Self {
206
+ use hdf5_sys:: h5:: { H5_ITER_DEC , H5_ITER_INC , H5_ITER_NATIVE } ;
207
+ match v {
208
+ IterationOrder :: Increasing => H5_ITER_INC ,
209
+ IterationOrder :: Decreasing => H5_ITER_DEC ,
210
+ IterationOrder :: Native => H5_ITER_NATIVE ,
211
+ }
212
+ }
213
+ }
190
214
191
- 0 // Continue iteration
215
+ /// Iteration methods
216
+ impl Group {
217
+ /// Visits all objects in the group
218
+ pub fn iter_visit < F , G > (
219
+ & self , mut op : F , mut val : G , order : ( IterationOrder , TraversalOrder ) ,
220
+ ) -> Result < G >
221
+ where
222
+ F : Fn ( & Self , & std:: ffi:: CStr , & H5L_info_t , & mut G ) -> bool ,
223
+ {
224
+ /// Struct used to pass a tuple
225
+ struct Vtable < ' a , F , D > {
226
+ f : & ' a mut F ,
227
+ d : & ' a mut D ,
228
+ }
229
+ // Maps a closure to a C callback
230
+ //
231
+ // This function will be called multiple times, but never concurrently
232
+ extern "C" fn callback < F , G > (
233
+ id : hid_t , name : * const c_char , info : * const H5L_info_t , op_data : * mut c_void ,
234
+ ) -> herr_t
235
+ where
236
+ F : FnMut ( & Group , & std:: ffi:: CStr , & H5L_info_t , & mut G ) -> bool ,
237
+ {
238
+ panic:: catch_unwind ( || {
239
+ let vtable = op_data. cast :: < Vtable < F , G > > ( ) ;
240
+ let vtable = unsafe { vtable. as_mut ( ) . expect ( "op_data is always non-null" ) } ;
241
+ let name = unsafe { std:: ffi:: CStr :: from_ptr ( name) } ;
242
+ let info = unsafe { info. as_ref ( ) . unwrap ( ) } ;
243
+ let handle = Handle :: try_new ( id) . unwrap ( ) ;
244
+ handle. incref ( ) ;
245
+ let group = Group :: from_handle ( handle) ;
246
+ if ( vtable. f ) ( & group, name, info, vtable. d ) {
247
+ 0
248
+ } else {
249
+ 1
250
+ }
192
251
} )
193
252
. unwrap_or ( -1 )
194
253
}
195
254
196
- let callback_fn: H5L_iterate_t = Some ( members_callback) ;
197
- let iteration_position: * mut hsize_t = & mut { 0_u64 } ;
198
- let mut result: Vec < String > = Vec :: new ( ) ;
199
- let other_data: * mut c_void = ( & mut result as * mut Vec < String > ) . cast :: < c_void > ( ) ;
255
+ let callback_fn: H5L_iterate_t = Some ( callback :: < F , G > ) ;
256
+ let iter_pos: * mut hsize_t = & mut 0_u64 ;
257
+
258
+ // Store our references on the heap
259
+ let mut vtable = Vtable { f : & mut op, d : & mut val } ;
260
+ let other_data = ( & mut vtable as * mut Vtable < _ , _ > ) . cast :: < c_void > ( ) ;
200
261
201
262
h5call ! ( H5Literate (
202
263
self . id( ) ,
203
- H5_index_t :: H5_INDEX_NAME ,
204
- H5_iter_order_t :: H5_ITER_INC ,
205
- iteration_position ,
264
+ order . 1 . into ( ) ,
265
+ order . 0 . into ( ) ,
266
+ iter_pos ,
206
267
callback_fn,
207
268
other_data
208
- ) ) ?;
269
+ ) )
270
+ . map ( |_| val)
271
+ }
272
+
273
+ fn get_all_of_type ( & self , typ : H5O_type_t ) -> Result < Vec < Handle > > {
274
+ #[ cfg( not( hdf5_1_10_3) ) ]
275
+ use hdf5_sys:: h5o:: { H5Oget_info_by_name1 , H5Oopen_by_addr } ;
276
+ #[ cfg( all( hdf5_1_10_3, not( hdf5_1_12_0) ) ) ]
277
+ use hdf5_sys:: h5o:: { H5Oget_info_by_name2 , H5Oopen_by_addr , H5O_INFO_BASIC } ;
278
+ #[ cfg( hdf5_1_12_0) ]
279
+ use hdf5_sys:: h5o:: { H5Oget_info_by_name3 , H5Oopen_by_token , H5O_INFO_BASIC } ;
280
+
281
+ let objects = vec ! [ ] ;
282
+
283
+ self . iter_visit (
284
+ |group, name, _info, objects| {
285
+ let mut infobuf = std:: mem:: MaybeUninit :: uninit ( ) ;
286
+ #[ cfg( hdf5_1_12_0) ]
287
+ if h5call ! ( H5Oget_info_by_name3 (
288
+ group. id( ) ,
289
+ name. as_ptr( ) ,
290
+ infobuf. as_mut_ptr( ) ,
291
+ H5O_INFO_BASIC ,
292
+ H5P_DEFAULT
293
+ ) )
294
+ . is_err ( )
295
+ {
296
+ return true ;
297
+ } ;
298
+ #[ cfg( all( hdf5_1_10_3, not( hdf5_1_12_0) ) ) ]
299
+ if h5call ! ( H5Oget_info_by_name2 (
300
+ group. id( ) ,
301
+ name. as_ptr( ) ,
302
+ infobuf. as_mut_ptr( ) ,
303
+ H5O_INFO_BASIC ,
304
+ H5P_DEFAULT
305
+ ) )
306
+ . is_err ( )
307
+ {
308
+ return true ;
309
+ } ;
310
+ #[ cfg( not( hdf5_1_10_3) ) ]
311
+ if h5call ! ( H5Oget_info_by_name1 (
312
+ group. id( ) ,
313
+ name. as_ptr( ) ,
314
+ infobuf. as_mut_ptr( ) ,
315
+ H5P_DEFAULT
316
+ ) )
317
+ . is_err ( )
318
+ {
319
+ return true ;
320
+ } ;
321
+ let infobuf = unsafe { infobuf. assume_init ( ) } ;
322
+ if infobuf. type_ == typ {
323
+ #[ cfg( hdf5_1_12_0) ]
324
+ if let Ok ( id) = h5call ! ( H5Oopen_by_token ( group. id( ) , infobuf. token) ) {
325
+ if let Ok ( handle) = Handle :: try_new ( id) {
326
+ objects. push ( handle) ;
327
+ }
328
+ }
329
+ #[ cfg( not( hdf5_1_12_0) ) ]
330
+ if let Ok ( id) = h5call ! ( H5Oopen_by_addr ( group. id( ) , infobuf. addr) ) {
331
+ if let Ok ( handle) = Handle :: try_new ( id) {
332
+ objects. push ( handle) ;
333
+ }
334
+ }
335
+ }
336
+ true
337
+ } ,
338
+ objects,
339
+ ( IterationOrder :: Native , TraversalOrder :: Lexicographic ) ,
340
+ )
341
+ }
342
+
343
+ /// Returns all groups in the group, non-recursively
344
+ pub fn groups ( & self ) -> Result < Vec < Self > > {
345
+ self . get_all_of_type ( H5O_TYPE_GROUP )
346
+ . map ( |vec| vec. into_iter ( ) . map ( Self :: from_handle) . collect ( ) )
347
+ }
348
+
349
+ /// Returns all datasets in the group, non-recursively
350
+ pub fn datasets ( & self ) -> Result < Vec < Dataset > > {
351
+ self . get_all_of_type ( H5O_TYPE_DATASET )
352
+ . map ( |vec| vec. into_iter ( ) . map ( Dataset :: from_handle) . collect ( ) )
353
+ }
354
+
355
+ /// Returns all named types in the group, non-recursively
356
+ pub fn datatypes ( & self ) -> Result < Vec < Datatype > > {
357
+ self . get_all_of_type ( H5O_TYPE_NAMED_DATATYPE )
358
+ . map ( |vec| vec. into_iter ( ) . map ( Datatype :: from_handle) . collect ( ) )
359
+ }
209
360
210
- Ok ( result)
361
+ /// Returns the names of all objects in the group, non-recursively.
362
+ pub fn member_names ( & self ) -> Result < Vec < String > > {
363
+ self . iter_visit (
364
+ |_, name, _, names| {
365
+ names. push ( name. to_str ( ) . unwrap ( ) . to_owned ( ) ) ;
366
+ true
367
+ } ,
368
+ vec ! [ ] ,
369
+ ( IterationOrder :: Native , TraversalOrder :: Lexicographic ) ,
370
+ )
211
371
}
212
372
}
213
373
@@ -412,4 +572,32 @@ pub mod tests {
412
572
assert_eq ! ( file. member_names( ) . unwrap( ) , vec![ "a" , "b" ] ) ;
413
573
} )
414
574
}
575
+
576
+ #[ test]
577
+ pub fn test_iterators ( ) {
578
+ with_tmp_file ( |file| {
579
+ file. create_group ( "a" ) . unwrap ( ) ;
580
+ file. create_group ( "b" ) . unwrap ( ) ;
581
+ let group_a = file. group ( "a" ) . unwrap ( ) ;
582
+ let _group_b = file. group ( "b" ) . unwrap ( ) ;
583
+ file. new_dataset :: < u32 > ( ) . shape ( ( 10 , 20 ) ) . create ( "a/foo" ) . unwrap ( ) ;
584
+ file. new_dataset :: < u32 > ( ) . shape ( ( 10 , 20 ) ) . create ( "a/123" ) . unwrap ( ) ;
585
+ file. new_dataset :: < u32 > ( ) . shape ( ( 10 , 20 ) ) . create ( "a/bar" ) . unwrap ( ) ;
586
+
587
+ let groups = file. groups ( ) . unwrap ( ) ;
588
+ assert_eq ! ( groups. len( ) , 2 ) ;
589
+ for group in groups {
590
+ assert ! ( matches!( group. name( ) . as_ref( ) , "/a" | "/b" ) ) ;
591
+ }
592
+
593
+ let datasets = file. datasets ( ) . unwrap ( ) ;
594
+ assert_eq ! ( datasets. len( ) , 0 ) ;
595
+
596
+ let datasets = group_a. datasets ( ) . unwrap ( ) ;
597
+ assert_eq ! ( datasets. len( ) , 3 ) ;
598
+ for dataset in datasets {
599
+ assert ! ( matches!( dataset. name( ) . as_ref( ) , "/a/foo" | "/a/123" | "/a/bar" ) ) ;
600
+ }
601
+ } )
602
+ }
415
603
}
0 commit comments