-
Notifications
You must be signed in to change notification settings - Fork 95
Add more iterators on Group #157
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ca10fab
baab13f
edb8ff1
573a417
075efb3
6bbea30
cfa7788
9992600
d5082bd
afa5b95
514abbc
89b3e45
9de1b20
537077d
2115383
ce48e1d
4ba1386
b5a760e
ae75ef0
e5e56a7
7103de9
312fac4
2f7cc24
05477f0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,14 +7,16 @@ use hdf5_sys::{ | |
h5d::H5Dopen2, | ||
h5g::{H5G_info_t, H5Gcreate2, H5Gget_info, H5Gopen2}, | ||
h5l::{ | ||
H5L_info_t, H5L_iterate_t, H5Lcreate_external, H5Lcreate_hard, H5Lcreate_soft, H5Ldelete, | ||
H5Lexists, H5Literate, H5Lmove, H5L_SAME_LOC, | ||
H5L_info_t, H5L_iterate_t, H5L_type_t, H5Lcreate_external, H5Lcreate_hard, H5Lcreate_soft, | ||
H5Ldelete, H5Lexists, H5Literate, H5Lmove, H5L_SAME_LOC, | ||
}, | ||
h5p::{H5Pcreate, H5Pset_create_intermediate_group}, | ||
h5t::H5T_cset_t, | ||
}; | ||
|
||
use crate::globals::H5P_LINK_CREATE; | ||
use crate::internal_prelude::*; | ||
use crate::{Location, LocationType}; | ||
|
||
/// Represents the HDF5 group object. | ||
#[repr(transparent)] | ||
|
@@ -212,37 +214,193 @@ impl Group { | |
let name = to_cstring(name)?; | ||
Dataset::from_id(h5try!(H5Dopen2(self.id(), name.as_ptr(), H5P_DEFAULT))) | ||
} | ||
} | ||
|
||
/// Returns names of all the members in the group, non-recursively. | ||
pub fn member_names(&self) -> Result<Vec<String>> { | ||
extern "C" fn members_callback( | ||
_id: hid_t, name: *const c_char, _info: *const H5L_info_t, op_data: *mut c_void, | ||
) -> herr_t { | ||
panic::catch_unwind(|| { | ||
let other_data: &mut Vec<String> = unsafe { &mut *(op_data.cast::<Vec<String>>()) }; | ||
#[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
pub enum TraversalOrder { | ||
Name, | ||
Creation, | ||
} | ||
|
||
other_data.push(string_from_cstr(name)); | ||
impl Default for TraversalOrder { | ||
fn default() -> Self { | ||
Self::Name | ||
} | ||
} | ||
|
||
impl From<TraversalOrder> for H5_index_t { | ||
fn from(v: TraversalOrder) -> Self { | ||
match v { | ||
TraversalOrder::Name => Self::H5_INDEX_NAME, | ||
TraversalOrder::Creation => Self::H5_INDEX_CRT_ORDER, | ||
} | ||
} | ||
} | ||
|
||
#[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
pub enum IterationOrder { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
Increasing, | ||
Decreasing, | ||
Native, | ||
} | ||
|
||
impl Default for IterationOrder { | ||
fn default() -> Self { | ||
Self::Native | ||
} | ||
} | ||
|
||
0 // Continue iteration | ||
impl From<IterationOrder> for H5_iter_order_t { | ||
fn from(v: IterationOrder) -> Self { | ||
match v { | ||
IterationOrder::Increasing => Self::H5_ITER_INC, | ||
IterationOrder::Decreasing => Self::H5_ITER_DEC, | ||
IterationOrder::Native => Self::H5_ITER_NATIVE, | ||
} | ||
} | ||
} | ||
|
||
#[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
pub enum LinkType { | ||
Hard, | ||
Soft, | ||
External, | ||
} | ||
|
||
impl From<H5L_type_t> for LinkType { | ||
fn from(link_type: H5L_type_t) -> Self { | ||
match link_type { | ||
H5L_type_t::H5L_TYPE_HARD => Self::Hard, | ||
H5L_type_t::H5L_TYPE_SOFT => Self::Soft, | ||
_ => Self::External, | ||
} | ||
} | ||
} | ||
|
||
#[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
pub struct LinkInfo { | ||
pub link_type: LinkType, | ||
pub creation_order: Option<i64>, | ||
pub is_utf8: bool, | ||
} | ||
|
||
impl From<&H5L_info_t> for LinkInfo { | ||
fn from(link: &H5L_info_t) -> Self { | ||
let link_type = link.type_.into(); | ||
let creation_order = if link.corder_valid == 1 { Some(link.corder) } else { None }; | ||
let is_utf8 = link.cset == H5T_cset_t::H5T_CSET_UTF8; | ||
Self { link_type, creation_order, is_utf8 } | ||
} | ||
} | ||
|
||
/// Iteration methods | ||
impl Group { | ||
/// Visits all objects in the group | ||
pub fn iter_visit<F, G>( | ||
&self, iteration_order: IterationOrder, traversal_order: TraversalOrder, mut val: G, | ||
mut op: F, | ||
) -> Result<G> | ||
where | ||
F: Fn(&Self, &str, LinkInfo, &mut G) -> bool, | ||
{ | ||
/// Struct used to pass a tuple | ||
struct Vtable<'a, F, D> { | ||
f: &'a mut F, | ||
d: &'a mut D, | ||
} | ||
// Maps a closure to a C callback | ||
// | ||
// This function will be called multiple times, but never concurrently | ||
extern "C" fn callback<F, G>( | ||
id: hid_t, name: *const c_char, info: *const H5L_info_t, op_data: *mut c_void, | ||
) -> herr_t | ||
where | ||
F: FnMut(&Group, &str, LinkInfo, &mut G) -> bool, | ||
{ | ||
panic::catch_unwind(|| { | ||
let vtable = op_data.cast::<Vtable<F, G>>(); | ||
let vtable = unsafe { vtable.as_mut().expect("iter_visit: null op_data ptr") }; | ||
unsafe { name.as_ref().expect("iter_visit: null name ptr") }; | ||
let name = unsafe { std::ffi::CStr::from_ptr(name) }; | ||
let info = unsafe { info.as_ref().expect("iter_vist: null info ptr") }; | ||
let handle = Handle::try_new(id).expect("iter_visit: unable to create a handle"); | ||
handle.incref(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could probably do The only difference would be it doesn't incref the handle there, wonder why it's increfed manually here? |
||
let group = Group::from_handle(handle); | ||
if (vtable.f)(&group, name.to_string_lossy().as_ref(), info.into(), vtable.d) { | ||
0 | ||
} else { | ||
1 | ||
} | ||
}) | ||
.unwrap_or(-1) | ||
} | ||
|
||
let callback_fn: H5L_iterate_t = Some(members_callback); | ||
let iteration_position: *mut hsize_t = &mut { 0_u64 }; | ||
let mut result: Vec<String> = Vec::new(); | ||
let other_data: *mut c_void = (&mut result as *mut Vec<String>).cast::<c_void>(); | ||
let callback_fn: H5L_iterate_t = Some(callback::<F, G>); | ||
let iter_pos: *mut hsize_t = &mut 0_u64; | ||
|
||
// Store our references on the heap | ||
let mut vtable = Vtable { f: &mut op, d: &mut val }; | ||
let other_data = (&mut vtable as *mut Vtable<_, _>).cast::<c_void>(); | ||
|
||
h5call!(H5Literate( | ||
self.id(), | ||
H5_index_t::H5_INDEX_NAME, | ||
H5_iter_order_t::H5_ITER_INC, | ||
iteration_position, | ||
traversal_order.into(), | ||
iteration_order.into(), | ||
iter_pos, | ||
callback_fn, | ||
other_data | ||
))?; | ||
)) | ||
.map(|_| val) | ||
} | ||
|
||
/// Visits all objects in the group using default iteration/traversal order. | ||
pub fn iter_visit_default<F, G>(&self, val: G, op: F) -> Result<G> | ||
where | ||
F: Fn(&Self, &str, LinkInfo, &mut G) -> bool, | ||
{ | ||
self.iter_visit(IterationOrder::default(), TraversalOrder::default(), val, op) | ||
} | ||
|
||
fn get_all_of_type(&self, loc_type: LocationType) -> Result<Vec<Location>> { | ||
self.iter_visit_default(vec![], |group, name, _info, objects| { | ||
if let Ok(info) = group.get_info_by_name(name) { | ||
if info.loc_type == loc_type { | ||
if let Ok(loc) = group.open_by_token(info.token) { | ||
objects.push(loc); | ||
return true; // ok, object extracted and pushed | ||
} | ||
} else { | ||
return true; // ok, object is of another type, skipped | ||
} | ||
} | ||
false // an error occured somewhere along the way | ||
}) | ||
} | ||
|
||
/// Returns all groups in the group, non-recursively | ||
pub fn groups(&self) -> Result<Vec<Self>> { | ||
self.get_all_of_type(LocationType::Group) | ||
.map(|vec| vec.into_iter().map(|obj| unsafe { obj.cast() }).collect()) | ||
} | ||
|
||
Ok(result) | ||
/// Returns all datasets in the group, non-recursively | ||
pub fn datasets(&self) -> Result<Vec<Dataset>> { | ||
self.get_all_of_type(LocationType::Dataset) | ||
.map(|vec| vec.into_iter().map(|obj| unsafe { obj.cast() }).collect()) | ||
} | ||
|
||
/// Returns all named types in the group, non-recursively | ||
pub fn named_datatypes(&self) -> Result<Vec<Datatype>> { | ||
self.get_all_of_type(LocationType::NamedDatatype) | ||
.map(|vec| vec.into_iter().map(|obj| unsafe { obj.cast() }).collect()) | ||
} | ||
|
||
/// Returns the names of all objects in the group, non-recursively. | ||
pub fn member_names(&self) -> Result<Vec<String>> { | ||
self.iter_visit_default(vec![], |_, name, _, names| { | ||
names.push(name.to_owned()); | ||
true | ||
}) | ||
} | ||
} | ||
|
||
|
@@ -471,4 +629,32 @@ pub mod tests { | |
assert_eq!(dset2.read_scalar::<i32>().unwrap(), 13); | ||
}) | ||
} | ||
|
||
#[test] | ||
pub fn test_iterators() { | ||
with_tmp_file(|file| { | ||
file.create_group("a").unwrap(); | ||
file.create_group("b").unwrap(); | ||
let group_a = file.group("a").unwrap(); | ||
let _group_b = file.group("b").unwrap(); | ||
file.new_dataset::<u32>().shape((10, 20)).create("a/foo").unwrap(); | ||
file.new_dataset::<u32>().shape((10, 20)).create("a/123").unwrap(); | ||
file.new_dataset::<u32>().shape((10, 20)).create("a/bar").unwrap(); | ||
|
||
let groups = file.groups().unwrap(); | ||
assert_eq!(groups.len(), 2); | ||
for group in groups { | ||
assert!(matches!(group.name().as_ref(), "/a" | "/b")); | ||
} | ||
|
||
let datasets = file.datasets().unwrap(); | ||
assert_eq!(datasets.len(), 0); | ||
|
||
let datasets = group_a.datasets().unwrap(); | ||
assert_eq!(datasets.len(), 3); | ||
for dataset in datasets { | ||
assert!(matches!(dataset.name().as_ref(), "/a/foo" | "/a/123" | "/a/bar")); | ||
} | ||
}) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
?