use std::any::Any;
use std::panic::{self, AssertUnwindSafe};
use std::ptr::addr_of_mut;
use hdf5_sys::h5::{H5_index_t, H5_iter_order_t, hsize_t};
use crate::internal_prelude::*;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum IndexType {
Name,
CreationOrder,
}
impl Default for IndexType {
fn default() -> Self {
Self::Name
}
}
impl From<IndexType> for H5_index_t {
fn from(v: IndexType) -> Self {
match v {
IndexType::Name => Self::H5_INDEX_NAME,
IndexType::CreationOrder => Self::H5_INDEX_CRT_ORDER,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum IterationOrder {
Increasing,
Decreasing,
Native,
}
impl Default for IterationOrder {
fn default() -> Self {
Self::Native
}
}
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 struct IterationCursor {
index_type: IndexType,
iteration_order: IterationOrder,
position: u64,
}
impl IterationCursor {
pub const fn start(index_type: IndexType, iteration_order: IterationOrder) -> Self {
Self { index_type, iteration_order, position: 0 }
}
#[must_use]
pub const fn skip(self, items: u64) -> Self {
Self { position: self.position + items, ..self }
}
pub const fn index_type(self) -> IndexType {
self.index_type
}
pub const fn iteration_order(self) -> IterationOrder {
self.iteration_order
}
pub const fn position(self) -> u64 {
self.position
}
}
pub(crate) type Callback<I> =
unsafe extern "C" fn(hid_t, *const c_char, *const I, *mut c_void) -> herr_t;
pub(crate) fn visit<I, T, B, F, N, C>(
cursor: IterationCursor, count: N, op: F, iterate: C,
) -> Result<Option<(B, IterationCursor)>>
where
T: for<'a> From<&'a I>,
F: FnMut(&str, T) -> Result<Option<B>>,
N: FnOnce() -> Result<u64>,
C: FnOnce(H5_index_t, H5_iter_order_t, *mut hsize_t, Callback<I>, *mut c_void) -> herr_t,
{
enum Stop<B> {
Found(B),
Error(Error),
Panic(Box<dyn Any + Send>),
}
struct OpData<B, F> {
op: F,
stop: Option<Stop<B>>,
}
unsafe extern "C" fn callback<I, T, B, F>(
_id: hid_t, name: *const c_char, info: *const I, op_data: *mut c_void,
) -> herr_t
where
T: for<'a> From<&'a I>,
F: FnMut(&str, T) -> Result<Option<B>>,
{
let Some(data) = (unsafe { op_data.cast::<OpData<B, F>>().as_mut() }) else {
return -1;
};
let visited = panic::catch_unwind(AssertUnwindSafe(|| {
assert!(!name.is_null(), "iteration: null name ptr");
let name = unsafe { std::ffi::CStr::from_ptr(name) };
let info = unsafe { info.as_ref() }.expect("iteration: null info ptr");
(data.op)(name.to_string_lossy().as_ref(), T::from(info))
}));
match visited {
Ok(Ok(None)) => 0,
Ok(Ok(Some(value))) => {
data.stop = Some(Stop::Found(value));
1
}
Ok(Err(err)) => {
data.stop = Some(Stop::Error(err));
-1
}
Err(payload) => {
data.stop = Some(Stop::Panic(payload));
-1
}
}
}
if cursor.position > 0 && cursor.position >= count()? {
return Ok(None);
}
let mut data = OpData { op, stop: None };
let mut position: hsize_t = cursor.position;
let ret = h5call!(iterate(
cursor.index_type.into(),
cursor.iteration_order.into(),
&mut position,
callback::<I, T, B, F>,
addr_of_mut!(data).cast::<c_void>()
));
match data.stop {
Some(Stop::Panic(payload)) => panic::resume_unwind(payload),
Some(Stop::Error(err)) => Err(err),
Some(Stop::Found(value)) => {
ret?;
Ok(Some((value, IterationCursor { position, ..cursor })))
}
None => {
ret?;
Ok(None)
}
}
}