Skip to content

Commit d9587cd

Browse files
committed
Add more iterators on Group
1 parent 7a917de commit d9587cd

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_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
}

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_panic_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)