@@ -10,6 +10,7 @@ use hdf5_sys::{
1010 H5L_info_t , H5L_iterate_t , H5Lcreate_hard , H5Lcreate_soft , H5Ldelete , H5Lexists ,
1111 H5Literate , H5Lmove , H5L_SAME_LOC ,
1212 } ,
13+ h5o:: H5O_type_t :: { self , H5O_TYPE_DATASET , H5O_TYPE_GROUP , H5O_TYPE_NAMED_DATATYPE } ,
1314 h5p:: { H5Pcreate , H5Pset_create_intermediate_group } ,
1415} ;
1516
@@ -177,37 +178,196 @@ impl Group {
177178 let name = to_cstring ( name) ?;
178179 Dataset :: from_id ( h5try ! ( H5Dopen2 ( self . id( ) , name. as_ptr( ) , H5P_DEFAULT ) ) )
179180 }
181+ }
180182
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+ }
188203
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+ }
190214
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+ }
192251 } )
193252 . unwrap_or ( -1 )
194253 }
195254
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 > ( ) ;
200261
201262 h5call ! ( H5Literate (
202263 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 ,
206267 callback_fn,
207268 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+ }
209360
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+ )
211371 }
212372}
213373
@@ -412,4 +572,32 @@ pub mod tests {
412572 assert_eq ! ( file. member_names( ) . unwrap( ) , vec![ "a" , "b" ] ) ;
413573 } )
414574 }
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+ }
415603}
0 commit comments