Skip to content

Commit b3e1d30

Browse files
committed
Add more iterators on Group
1 parent 7b31291 commit b3e1d30

File tree

3 files changed

+216
-27
lines changed

3 files changed

+216
-27
lines changed

hdf5-sys/src/h5o.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -359,15 +359,15 @@ impl Default for H5O_token_t {
359359
#[repr(C)]
360360
#[derive(Debug, Copy, Clone)]
361361
pub struct H5O_info2_t {
362-
fileno: c_ulong,
363-
token: H5O_token_t,
364-
type_: H5O_type_t,
365-
rc: c_uint,
366-
atime: time_t,
367-
mtime: time_t,
368-
ctime: time_t,
369-
btime: time_t,
370-
num_attrs: hsize_t,
362+
pub fileno: c_ulong,
363+
pub token: H5O_token_t,
364+
pub type_: H5O_type_t,
365+
pub rc: c_uint,
366+
pub atime: time_t,
367+
pub mtime: time_t,
368+
pub ctime: time_t,
369+
pub btime: time_t,
370+
pub num_attrs: hsize_t,
371371
}
372372

373373
#[cfg(hdf5_1_12_0)]

src/hl/group.rs

+206-18
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use hdf5_sys::{
1010
H5L_info_t, H5L_iterate_t, H5Lcreate_external, H5Lcreate_hard, H5Lcreate_soft, H5Ldelete,
1111
H5Lexists, 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

@@ -212,37 +213,196 @@ impl Group {
212213
let name = to_cstring(name)?;
213214
Dataset::from_id(h5try!(H5Dopen2(self.id(), name.as_ptr(), H5P_DEFAULT)))
214215
}
216+
}
215217

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+
}
223238

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+
}
225249

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+
}
227286
})
228287
.unwrap_or(-1)
229288
}
230289

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>();
235296

236297
h5call!(H5Literate(
237298
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,
241302
callback_fn,
242303
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+
}
244395

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+
)
246406
}
247407
}
248408

@@ -471,4 +631,32 @@ pub mod tests {
471631
assert_eq!(dset2.read_scalar::<i32>().unwrap(), 13);
472632
})
473633
}
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+
}
474662
}

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#![cfg_attr(feature = "cargo-clippy", allow(clippy::unnecessary_unwrap))]
2121
#![cfg_attr(feature = "cargo-clippy", allow(clippy::unnecessary_wraps))]
2222
#![cfg_attr(feature = "cargo-clippy", allow(clippy::upper_case_acronyms))]
23+
#![cfg_attr(feature = "cargo-clippy", allow(clippy::missing_panics_doc))]
2324
#![cfg_attr(all(feature = "cargo-clippy", test), allow(clippy::cyclomatic_complexity))]
2425
#![cfg_attr(not(test), allow(dead_code))]
2526

0 commit comments

Comments
 (0)