Skip to content

Commit 46c24e2

Browse files
authored
Merge pull request #157 from mulimoen/feature/iteration
Add more iterators on Group
2 parents 3ccbe03 + 05477f0 commit 46c24e2

File tree

6 files changed

+431
-39
lines changed

6 files changed

+431
-39
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
but must read the entire attribute at once).
5050
- Added support in `hdf5-sys` for the new functions in `hdf5` `1.10.6` and `1.10.7`.
5151
- Added support for creating external links on a `Group` with `link_external`.
52+
- Added `Location` methods: `get_info`, `get_info_by_name`, `loc_type`, and `open_by_token`.
53+
- Added `Group` methods: `iter_visit`, `iter_visit_default`, `get_all_of_type`, `datasets`, `groups`, and `named_datatypes`.
5254

5355
### Changed
5456

hdf5-sys/src/h5o.rs

+26-15
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ pub use {
99
};
1010
#[cfg(hdf5_1_12_0)]
1111
pub use {H5O_info2_t as H5O_info_t, H5O_iterate2_t as H5O_iterate_t};
12+
#[cfg(not(hdf5_1_10_3))]
13+
pub use {
14+
H5Oget_info1 as H5Oget_info, H5Oget_info_by_idx1 as H5Oget_info_by_idx,
15+
H5Oget_info_by_name1 as H5Oget_info_by_name, H5Ovisit1 as H5Ovisit,
16+
H5Ovisit_by_name1 as H5Ovisit_by_name,
17+
};
1218

1319
use crate::internal_prelude::*;
1420

@@ -201,19 +207,24 @@ pub type H5O_mcdt_search_cb_t =
201207

202208
#[cfg(not(hdf5_1_10_3))]
203209
extern "C" {
204-
pub fn H5Oget_info(loc_id: hid_t, oinfo: *mut H5O_info1_t) -> herr_t;
205-
pub fn H5Oget_info_by_name(
210+
#[link_name = "H5Oget_info"]
211+
pub fn H5Oget_info1(loc_id: hid_t, oinfo: *mut H5O_info1_t) -> herr_t;
212+
#[link_name = "H5Oget_info_by_name"]
213+
pub fn H5Oget_info_by_name1(
206214
loc_id: hid_t, name: *const c_char, oinfo: *mut H5O_info1_t, lapl_id: hid_t,
207215
) -> herr_t;
208-
pub fn H5Oget_info_by_idx(
216+
#[link_name = "H5Oget_info_by_idx"]
217+
pub fn H5Oget_info_by_idx1(
209218
loc_id: hid_t, group_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t,
210219
n: hsize_t, oinfo: *mut H5O_info1_t, lapl_id: hid_t,
211220
) -> herr_t;
212-
pub fn H5Ovisit(
221+
#[link_name = "H5Ovisit"]
222+
pub fn H5Ovisit1(
213223
obj_id: hid_t, idx_type: H5_index_t, order: H5_iter_order_t, op: H5O_iterate1_t,
214224
op_data: *mut c_void,
215225
) -> herr_t;
216-
pub fn H5Ovisit_by_name(
226+
#[link_name = "H5Ovisit_by_name"]
227+
pub fn H5Ovisit_by_name1(
217228
loc_id: hid_t, obj_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t,
218229
op: H5O_iterate1_t, op_data: *mut c_void, lapl_id: hid_t,
219230
) -> herr_t;
@@ -353,7 +364,7 @@ extern "C" {
353364
pub const H5O_MAX_TOKEN_SIZE: usize = 16;
354365

355366
#[repr(C)]
356-
#[derive(Debug, Copy, Clone)]
367+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
357368
#[cfg(hdf5_1_12_0)]
358369
pub struct H5O_token_t {
359370
__data: [u8; H5O_MAX_TOKEN_SIZE],
@@ -370,15 +381,15 @@ impl Default for H5O_token_t {
370381
#[repr(C)]
371382
#[derive(Debug, Copy, Clone)]
372383
pub struct H5O_info2_t {
373-
fileno: c_ulong,
374-
token: H5O_token_t,
375-
type_: H5O_type_t,
376-
rc: c_uint,
377-
atime: time_t,
378-
mtime: time_t,
379-
ctime: time_t,
380-
btime: time_t,
381-
num_attrs: hsize_t,
384+
pub fileno: c_ulong,
385+
pub token: H5O_token_t,
386+
pub type_: H5O_type_t,
387+
pub rc: c_uint,
388+
pub atime: time_t,
389+
pub mtime: time_t,
390+
pub ctime: time_t,
391+
pub btime: time_t,
392+
pub num_attrs: hsize_t,
382393
}
383394

384395
#[cfg(hdf5_1_12_0)]

src/hl.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ pub use self::{
2424
dataspace::Dataspace,
2525
datatype::{Conversion, Datatype},
2626
file::{File, FileBuilder, OpenMode},
27-
group::Group,
28-
location::Location,
27+
group::{Group, LinkInfo, LinkType},
28+
location::{Location, LocationInfo, LocationToken, LocationType},
2929
object::Object,
3030
plist::PropertyList,
3131
};

src/hl/group.rs

+206-20
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@ use hdf5_sys::{
77
h5d::H5Dopen2,
88
h5g::{H5G_info_t, H5Gcreate2, H5Gget_info, H5Gopen2},
99
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,
1212
},
1313
h5p::{H5Pcreate, H5Pset_create_intermediate_group},
14+
h5t::H5T_cset_t,
1415
};
1516

1617
use crate::globals::H5P_LINK_CREATE;
1718
use crate::internal_prelude::*;
19+
use crate::{Location, LocationType};
1820

1921
/// Represents the HDF5 group object.
2022
#[repr(transparent)]
@@ -212,37 +214,193 @@ impl Group {
212214
let name = to_cstring(name)?;
213215
Dataset::from_id(h5try!(H5Dopen2(self.id(), name.as_ptr(), H5P_DEFAULT)))
214216
}
217+
}
215218

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

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

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+
}
227334
})
228335
.unwrap_or(-1)
229336
}
230337

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

236345
h5call!(H5Literate(
237346
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,
241350
callback_fn,
242351
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+
}
244385

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+
})
246404
}
247405
}
248406

@@ -471,4 +629,32 @@ pub mod tests {
471629
assert_eq!(dset2.read_scalar::<i32>().unwrap(), 13);
472630
})
473631
}
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+
}
474660
}

0 commit comments

Comments
 (0)