use std::fmt::{self, Debug};
use std::ops::Deref;
use std::ptr;
#[allow(deprecated)]
use hdf5_sys::h5s::H5Sencode1;
use hdf5_sys::h5s::{
H5S_class_t, H5Scopy, H5Screate, H5Screate_simple, H5Sdecode, H5Sget_select_npoints,
H5Sget_simple_extent_dims, H5Sget_simple_extent_ndims, H5Sget_simple_extent_npoints,
H5Sget_simple_extent_type, H5Sselect_valid, H5S_UNLIMITED,
};
use crate::hl::extents::{Extent, Extents, Ix};
use crate::hl::selection::RawSelection;
use crate::internal_prelude::*;
#[repr(transparent)]
#[derive(Clone)]
pub struct Dataspace(Handle);
impl ObjectClass for Dataspace {
const NAME: &'static str = "dataspace";
const VALID_TYPES: &'static [H5I_type_t] = &[H5I_DATASPACE];
fn from_handle(handle: Handle) -> Self {
Self(handle)
}
fn handle(&self) -> &Handle {
&self.0
}
fn short_repr(&self) -> Option<String> {
if let Ok(e) = self.extents() {
Some(format!("{}", e))
} else {
Some("(invalid)".into())
}
}
}
impl Debug for Dataspace {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.debug_fmt(f)
}
}
impl Deref for Dataspace {
type Target = Object;
fn deref(&self) -> &Object {
unsafe { self.transmute() }
}
}
unsafe fn get_shape(space_id: hid_t) -> Result<Vec<Ix>> {
let ndim = h5check(H5Sget_simple_extent_ndims(space_id))? as usize;
let mut dims = vec![0; ndim];
h5check(H5Sget_simple_extent_dims(space_id, dims.as_mut_ptr(), ptr::null_mut()))?;
Ok(dims.into_iter().map(|x| x as _).collect())
}
unsafe fn get_simple_extents(space_id: hid_t) -> Result<SimpleExtents> {
let ndim = h5check(H5Sget_simple_extent_ndims(space_id))? as usize;
let (mut dims, mut maxdims) = (vec![0; ndim], vec![0; ndim]);
h5check(H5Sget_simple_extent_dims(space_id, dims.as_mut_ptr(), maxdims.as_mut_ptr()))?;
let mut extents = Vec::with_capacity(ndim);
for i in 0..ndim {
let (dim, max) = (dims[i] as _, maxdims[i]);
let max = if max == H5S_UNLIMITED { None } else { Some(max as _) };
extents.push(Extent::new(dim, max));
}
Ok(SimpleExtents::from_vec(extents))
}
impl Dataspace {
pub fn try_new<T: Into<Extents>>(extents: T) -> Result<Self> {
Self::from_extents(&extents.into())
}
pub fn copy(&self) -> Self {
Self::from_id(h5lock!(H5Scopy(self.id()))).unwrap_or_else(|_| Self::invalid())
}
pub fn ndim(&self) -> usize {
h5call!(H5Sget_simple_extent_ndims(self.id())).unwrap_or(0) as _
}
pub fn shape(&self) -> Vec<Ix> {
h5lock!(get_shape(self.id())).unwrap_or_default()
}
pub fn maxdims(&self) -> Vec<Option<Ix>> {
self.extents().unwrap_or(Extents::Null).maxdims()
}
pub fn is_resizable(&self) -> bool {
self.maxdims().iter().any(Option::is_none)
}
pub fn is_null(&self) -> bool {
h5lock!(H5Sget_simple_extent_type(self.id())) == H5S_class_t::H5S_NULL
}
pub fn is_scalar(&self) -> bool {
h5lock!(H5Sget_simple_extent_type(self.id())) == H5S_class_t::H5S_SCALAR
}
pub fn is_simple(&self) -> bool {
h5lock!(H5Sget_simple_extent_type(self.id())) == H5S_class_t::H5S_SIMPLE
}
pub fn is_valid(&self) -> bool {
h5lock!(H5Sselect_valid(self.id())) > 0
}
pub fn size(&self) -> usize {
match h5lock!(H5Sget_simple_extent_type(self.id())) {
H5S_class_t::H5S_SIMPLE => {
h5call!(H5Sget_simple_extent_npoints(self.id())).unwrap_or(0) as _
}
H5S_class_t::H5S_SCALAR => 1,
_ => 0,
}
}
#[allow(deprecated)]
pub fn encode(&self) -> Result<Vec<u8>> {
let mut len: size_t = 0;
h5lock!({
h5try!(H5Sencode1(self.id(), ptr::null_mut(), &mut len as *mut _));
let mut buf = vec![0_u8; len];
h5try!(H5Sencode1(self.id(), buf.as_mut_ptr().cast(), &mut len as *mut _));
Ok(buf)
})
}
pub fn decode<T>(buf: T) -> Result<Self>
where
T: AsRef<[u8]>,
{
h5lock!(Self::from_id(h5try!(H5Sdecode(buf.as_ref().as_ptr().cast()))))
}
fn from_extents(extents: &Extents) -> Result<Self> {
h5lock!(Self::from_id(match extents {
Extents::Null => H5Screate(H5S_class_t::H5S_NULL),
Extents::Scalar => H5Screate(H5S_class_t::H5S_SCALAR),
Extents::Simple(ref e) => {
let (mut dims, mut maxdims) = (vec![], vec![]);
for extent in e.iter() {
dims.push(extent.dim as _);
maxdims.push(extent.max.map_or(H5S_UNLIMITED, |x| x as _));
}
H5Screate_simple(e.ndim() as _, dims.as_ptr(), maxdims.as_ptr())
}
}))
}
#[allow(clippy::match_wildcard_for_single_variants)]
pub fn extents(&self) -> Result<Extents> {
h5lock!(match H5Sget_simple_extent_type(self.id()) {
H5S_class_t::H5S_NULL => Ok(Extents::Null),
H5S_class_t::H5S_SCALAR => Ok(Extents::Scalar),
H5S_class_t::H5S_SIMPLE => get_simple_extents(self.id()).map(Extents::Simple),
extent_type => fail!("Invalid extents type: {}", extent_type as c_int),
})
}
pub fn selection_size(&self) -> usize {
h5call!(H5Sget_select_npoints(self.id())).ok().map_or(0, |x| x as _)
}
#[doc(hidden)]
pub fn select_raw<S: Into<RawSelection>>(&self, raw_sel: S) -> Result<Self> {
let raw_sel = raw_sel.into();
sync(|| unsafe {
let space = self.copy();
raw_sel.apply_to_dataspace(space.id())?;
ensure!(space.is_valid(), "Invalid selection, out of extents");
Ok(space)
})
}
pub fn select<S: Into<Selection>>(&self, selection: S) -> Result<Self> {
let raw_sel = selection.into().into_raw(&self.shape())?;
self.select_raw(raw_sel)
}
#[doc(hidden)]
pub fn get_raw_selection(&self) -> Result<RawSelection> {
sync(|| unsafe { RawSelection::extract_from_dataspace(self.id()) })
}
pub fn get_selection(&self) -> Result<Selection> {
let raw_sel = self.get_raw_selection()?;
Selection::from_raw(raw_sel)
}
}
#[cfg(test)]
mod tests {
use hdf5_sys::h5i::H5I_INVALID_HID;
use super::Dataspace;
use crate::internal_prelude::*;
#[test]
fn test_dataspace_err() {
assert_err!(Dataspace::from_id(H5I_INVALID_HID), "Invalid handle id");
}
#[test]
fn test_dataspace_null() -> Result<()> {
let space = Dataspace::try_new(Extents::Null)?;
assert_eq!(space.ndim(), 0);
assert_eq!(space.shape(), vec![]);
assert_eq!(space.maxdims(), vec![]);
assert_eq!(space.size(), 0);
assert!(space.is_null());
assert_eq!(space.extents()?, Extents::Null);
Ok(())
}
#[test]
fn test_dataspace_scalar() -> Result<()> {
let space = Dataspace::try_new(())?;
assert_eq!(space.ndim(), 0);
assert_eq!(space.shape(), vec![]);
assert_eq!(space.maxdims(), vec![]);
assert_eq!(space.size(), 1);
assert!(space.is_scalar());
assert_eq!(space.extents()?, Extents::Scalar);
Ok(())
}
#[test]
fn test_dataspace_simple() -> Result<()> {
let space = Dataspace::try_new(123)?;
assert_eq!(space.ndim(), 1);
assert_eq!(space.shape(), vec![123]);
assert_eq!(space.maxdims(), vec![Some(123)]);
assert_eq!(space.size(), 123);
assert!(space.is_simple());
assert_eq!(space.extents()?, Extents::simple(123));
assert!(!space.is_resizable());
let space = Dataspace::try_new((5, 6..=10, 7..))?;
assert_eq!(space.ndim(), 3);
assert_eq!(space.shape(), vec![5, 6, 7]);
assert_eq!(space.maxdims(), vec![Some(5), Some(10), None]);
assert_eq!(space.size(), 210);
assert!(space.is_simple());
assert_eq!(space.extents()?, Extents::simple((5, 6..=10, 7..)));
assert!(space.is_resizable());
Ok(())
}
#[test]
fn test_dataspace_copy() -> Result<()> {
let space = Dataspace::try_new((5, 6..=10, 7..))?;
let space_copy = space.copy();
assert!(space_copy.is_valid());
assert_eq!(space_copy.ndim(), space.ndim());
assert_eq!(space_copy.shape(), space.shape());
assert_eq!(space_copy.maxdims(), space.maxdims());
Ok(())
}
#[test]
fn test_dataspace_encode() -> Result<()> {
let space = Dataspace::try_new((5, 6..=10, 7..))?;
let encoded = space.encode()?;
let decoded = Dataspace::decode(&encoded)?;
assert_eq!(decoded.extents().unwrap(), space.extents().unwrap());
Ok(())
}
#[test]
fn test_dataspace_repr() -> Result<()> {
assert_eq!(&format!("{:?}", Dataspace::try_new(Extents::Null)?), "<HDF5 dataspace: null>");
assert_eq!(&format!("{:?}", Dataspace::try_new(())?), "<HDF5 dataspace: scalar>");
assert_eq!(&format!("{:?}", Dataspace::try_new(123)?), "<HDF5 dataspace: (123,)>");
assert_eq!(
&format!("{:?}", Dataspace::try_new((5, 6..=10, 7..))?),
"<HDF5 dataspace: (5, 6..=10, 7..)>"
);
Ok(())
}
}