use std::fmt::{self, Debug};
use std::mem;
use std::ops::Deref;
use ndarray::{Array, Array1, Array2, ArrayD, ArrayView, ArrayView1};
use ndarray::{SliceInfo, SliceOrIndex};
use hdf5_sys::h5a::{H5Aget_space, H5Aget_storage_size, H5Aget_type, H5Aread, H5Awrite};
use hdf5_sys::h5d::{H5Dget_space, H5Dget_storage_size, H5Dget_type, H5Dread, H5Dwrite};
use hdf5_sys::h5p::H5Pcreate;
use crate::internal_prelude::*;
#[derive(Debug)]
pub struct Reader<'a> {
obj: &'a Container,
conv: Conversion,
}
impl<'a> Reader<'a> {
pub fn new(obj: &'a Container) -> Self {
Self { obj, conv: Conversion::Soft }
}
pub fn conversion(mut self, conv: Conversion) -> Self {
self.conv = conv;
self
}
pub fn no_convert(mut self) -> Self {
self.conv = Conversion::NoOp;
self
}
fn read_into_buf<T: H5Type>(
&self, buf: *mut T, fspace: Option<&Dataspace>, mspace: Option<&Dataspace>,
) -> Result<()> {
let file_dtype = self.obj.dtype()?;
let mem_dtype = Datatype::from_type::<T>()?;
file_dtype.ensure_convertible(&mem_dtype, self.conv)?;
let (obj_id, tp_id) = (self.obj.id(), mem_dtype.id());
if self.obj.is_attr() {
h5try!(H5Aread(obj_id, tp_id, buf as *mut _));
} else {
let fspace_id = fspace.map_or(H5S_ALL, |f| f.id());
let mspace_id = mspace.map_or(H5S_ALL, |m| m.id());
let xfer =
PropertyList::from_id(h5call!(H5Pcreate(*crate::globals::H5P_DATASET_XFER))?)?;
crate::hl::plist::set_vlen_manager_libc(xfer.id())?;
h5try!(H5Dread(obj_id, tp_id, mspace_id, fspace_id, xfer.id(), buf as *mut _));
}
Ok(())
}
pub fn read_slice<T, S, D>(&self, slice: &SliceInfo<S, D>) -> Result<Array<T, D>>
where
T: H5Type,
S: AsRef<[SliceOrIndex]>,
D: ndarray::Dimension,
{
ensure!(!self.obj.is_attr(), "slicing cannot be used on attribute datasets");
let shape = self.obj.get_shape()?;
let slice_s: &[SliceOrIndex] = slice.as_ref();
let slice_dim = slice_s.len();
if shape.ndim() != slice_dim {
let obj_ndim = shape.ndim();
ensure!(
obj_ndim == slice_dim,
"slice dimension mismatch: dataset has {} dims, slice has {} dims",
obj_ndim,
slice_dim
);
}
if shape.ndim() == 0 {
if let Some(ndim) = D::NDIM {
let obj_ndim = 0;
ensure!(
obj_ndim == ndim,
"ndim mismatch: slice outputs dims {}, output type dims {}",
obj_ndim,
ndim
);
}
self.read()
} else {
let fspace = self.obj.space()?;
let out_shape = fspace.select_slice(slice)?;
let reduced_shape: Vec<_> = slice_s
.iter()
.zip(out_shape.iter().cloned())
.filter_map(|(slc, sz)| match slc {
SliceOrIndex::Index(_) => None,
_ => Some(sz),
})
.collect();
if let Some(ndim) = D::NDIM {
let obj_ndim = reduced_shape.len();
ensure!(
obj_ndim == ndim,
"ndim mismatch: slice outputs dims {}, output type dims {}",
obj_ndim,
ndim
);
}
let mspace = Dataspace::try_new(&out_shape, false)?;
let size = out_shape.iter().product();
let mut vec = Vec::with_capacity(size);
self.read_into_buf(vec.as_mut_ptr(), Some(&fspace), Some(&mspace))?;
unsafe {
vec.set_len(size);
}
let arr = ArrayD::from_shape_vec(reduced_shape, vec)?;
Ok(arr.into_dimensionality()?)
}
}
pub fn read<T: H5Type, D: ndarray::Dimension>(&self) -> Result<Array<T, D>> {
let shape = self.obj.get_shape()?;
if let Some(ndim) = D::NDIM {
let obj_ndim = shape.ndim();
ensure!(obj_ndim == ndim, "ndim mismatch: expected {}, got {}", ndim, obj_ndim);
}
let vec = self.read_raw()?;
let arr = ArrayD::from_shape_vec(shape, vec)?;
Ok(arr.into_dimensionality()?)
}
pub fn read_raw<T: H5Type>(&self) -> Result<Vec<T>> {
let size = self.obj.space()?.size();
let mut vec = Vec::with_capacity(size);
self.read_into_buf(vec.as_mut_ptr(), None, None).map(|_| {
unsafe {
vec.set_len(size);
};
vec
})
}
pub fn read_1d<T: H5Type>(&self) -> Result<Array1<T>> {
self.read()
}
pub fn read_slice_1d<T, S>(&self, slice: &SliceInfo<S, ndarray::Ix1>) -> Result<Array1<T>>
where
T: H5Type,
S: AsRef<[SliceOrIndex]>,
{
self.read_slice(slice)
}
pub fn read_2d<T: H5Type>(&self) -> Result<Array2<T>> {
self.read()
}
pub fn read_slice_2d<T, S>(&self, slice: &SliceInfo<S, ndarray::Ix2>) -> Result<Array2<T>>
where
T: H5Type,
S: AsRef<[SliceOrIndex]>,
{
self.read_slice(slice)
}
pub fn read_dyn<T: H5Type>(&self) -> Result<ArrayD<T>> {
self.read()
}
pub fn read_scalar<T: H5Type>(&self) -> Result<T> {
let obj_ndim = self.obj.get_shape()?.ndim();
ensure!(obj_ndim == 0, "ndim mismatch: expected scalar, got {}", obj_ndim);
let mut val = mem::MaybeUninit::<T>::uninit();
self.read_into_buf(val.as_mut_ptr(), None, None).map(|_| unsafe { val.assume_init() })
}
}
#[derive(Debug)]
pub struct Writer<'a> {
obj: &'a Container,
conv: Conversion,
}
impl<'a> Writer<'a> {
pub fn new(obj: &'a Container) -> Self {
Self { obj, conv: Conversion::Soft }
}
pub fn conversion(mut self, conv: Conversion) -> Self {
self.conv = conv;
self
}
pub fn no_convert(mut self) -> Self {
self.conv = Conversion::NoOp;
self
}
fn write_from_buf<T: H5Type>(
&self, buf: *const T, fspace: Option<&Dataspace>, mspace: Option<&Dataspace>,
) -> Result<()> {
let file_dtype = self.obj.dtype()?;
let mem_dtype = Datatype::from_type::<T>()?;
mem_dtype.ensure_convertible(&file_dtype, self.conv)?;
let (obj_id, tp_id) = (self.obj.id(), mem_dtype.id());
if self.obj.is_attr() {
h5try!(H5Awrite(obj_id, tp_id, buf as *const _));
} else {
let fspace_id = fspace.map_or(H5S_ALL, |f| f.id());
let mspace_id = mspace.map_or(H5S_ALL, |m| m.id());
h5try!(H5Dwrite(obj_id, tp_id, mspace_id, fspace_id, H5P_DEFAULT, buf as *const _));
}
Ok(())
}
pub fn write_slice<'b, A, T, S, D>(&self, arr: A, slice: &SliceInfo<S, D>) -> Result<()>
where
A: Into<ArrayView<'b, T, D>>,
T: H5Type,
S: AsRef<[SliceOrIndex]>,
D: ndarray::Dimension,
{
ensure!(!self.obj.is_attr(), "slicing cannot be used on attribute datasets");
let shape = self.obj.get_shape()?;
let slice_s: &[SliceOrIndex] = slice.as_ref();
let slice_dim = slice_s.len();
if shape.ndim() != slice_dim {
let obj_ndim = shape.ndim();
ensure!(
obj_ndim == slice_dim,
"slice dimension mismatch: dataset has {} dims, slice has {} dims",
obj_ndim,
slice_dim
);
}
if shape.ndim() == 0 {
self.write(arr)
} else {
let fspace = self.obj.space()?;
let slice_shape = fspace.select_slice(slice)?;
let view = arr.into();
let data_shape = view.shape();
let mut data_shape_hydrated = Vec::new();
let mut pos = 0;
for s in slice_s {
if let SliceOrIndex::Index(_) = s {
data_shape_hydrated.push(1);
} else {
data_shape_hydrated.push(data_shape[pos]);
pos += 1;
}
}
let mspace = Dataspace::try_new(&slice_shape, false)?;
ensure!(
view.is_standard_layout(),
"input array is not in standard layout or is not contiguous"
);
if slice_shape != data_shape_hydrated {
fail!(
"shape mismatch when writing slice: memory = {:?}, destination = {:?}",
data_shape_hydrated,
slice_shape
);
}
self.write_from_buf(view.as_ptr(), Some(&fspace), Some(&mspace))
}
}
pub fn write<'b, A, T, D>(&self, arr: A) -> Result<()>
where
A: Into<ArrayView<'b, T, D>>,
T: H5Type,
D: ndarray::Dimension,
{
let view = arr.into();
ensure!(
view.is_standard_layout(),
"input array is not in standard layout or is not contiguous"
);
let src = view.shape();
let dst = &*self.obj.get_shape()?;
if src != dst {
fail!("shape mismatch when writing: memory = {:?}, destination = {:?}", src, dst);
}
self.write_from_buf(view.as_ptr(), None, None)
}
pub fn write_raw<'b, A, T>(&self, arr: A) -> Result<()>
where
A: Into<ArrayView1<'b, T>>,
T: H5Type,
{
let view = arr.into();
ensure!(
view.is_standard_layout(),
"input array is not in standard layout or is not contiguous"
);
let src = view.len();
let dst = self.obj.get_shape()?.size();
if src != dst {
fail!("length mismatch when writing: memory = {:?}, destination = {:?}", src, dst);
}
self.write_from_buf(view.as_ptr(), None, None)
}
pub fn write_scalar<T: H5Type>(&self, val: &T) -> Result<()> {
let ndim = self.obj.get_shape()?.ndim();
ensure!(ndim == 0, "ndim mismatch: expected scalar, got {}", ndim);
self.write_from_buf(val as *const _, None, None)
}
}
#[repr(transparent)]
#[derive(Clone)]
pub struct Container(Handle);
impl ObjectClass for Container {
const NAME: &'static str = "container";
const VALID_TYPES: &'static [H5I_type_t] = &[H5I_DATASET, H5I_ATTR];
fn from_handle(handle: Handle) -> Self {
Self(handle)
}
fn handle(&self) -> &Handle {
&self.0
}
}
impl Debug for Container {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.debug_fmt(f)
}
}
impl Deref for Container {
type Target = Location;
fn deref(&self) -> &Location {
unsafe { self.transmute() }
}
}
impl Container {
pub(crate) fn is_attr(&self) -> bool {
get_id_type(self.id()) == H5I_ATTR
}
pub fn as_reader(&self) -> Reader {
Reader::new(self)
}
pub fn as_writer(&self) -> Writer {
Writer::new(self)
}
pub fn dtype(&self) -> Result<Datatype> {
if self.is_attr() {
Datatype::from_id(h5try!(H5Aget_type(self.id())))
} else {
Datatype::from_id(h5try!(H5Dget_type(self.id())))
}
}
pub fn space(&self) -> Result<Dataspace> {
if self.is_attr() {
Dataspace::from_id(h5try!(H5Aget_space(self.id())))
} else {
Dataspace::from_id(h5try!(H5Dget_space(self.id())))
}
}
#[doc(hidden)]
pub fn get_shape(&self) -> Result<Vec<Ix>> {
self.space().map(|s| s.dims())
}
pub fn shape(&self) -> Vec<Ix> {
self.space().ok().map_or_else(Vec::new, |s| s.dims())
}
pub fn ndim(&self) -> usize {
self.space().ok().map_or(0, |s| s.ndim())
}
pub fn size(&self) -> usize {
self.shape().size()
}
pub fn is_scalar(&self) -> bool {
self.ndim() == 0
}
pub fn storage_size(&self) -> u64 {
if self.is_attr() {
h5lock!(H5Aget_storage_size(self.id())) as _
} else {
h5lock!(H5Dget_storage_size(self.id())) as _
}
}
pub fn read<T: H5Type, D: ndarray::Dimension>(&self) -> Result<Array<T, D>> {
self.as_reader().read()
}
pub fn read_raw<T: H5Type>(&self) -> Result<Vec<T>> {
self.as_reader().read_raw()
}
pub fn read_1d<T: H5Type>(&self) -> Result<Array1<T>> {
self.as_reader().read_1d()
}
pub fn read_slice_1d<T, S>(&self, slice: &SliceInfo<S, ndarray::Ix1>) -> Result<Array1<T>>
where
T: H5Type,
S: AsRef<[SliceOrIndex]>,
{
self.as_reader().read_slice_1d(slice)
}
pub fn read_2d<T: H5Type>(&self) -> Result<Array2<T>> {
self.as_reader().read_2d()
}
pub fn read_slice_2d<T, S>(&self, slice: &SliceInfo<S, ndarray::Ix2>) -> Result<Array2<T>>
where
T: H5Type,
S: AsRef<[SliceOrIndex]>,
{
self.as_reader().read_slice_2d(slice)
}
pub fn read_dyn<T: H5Type>(&self) -> Result<ArrayD<T>> {
self.as_reader().read_dyn()
}
pub fn read_slice<T, S, D>(&self, slice: &SliceInfo<S, D>) -> Result<Array<T, D>>
where
T: H5Type,
S: AsRef<[SliceOrIndex]>,
D: ndarray::Dimension,
{
self.as_reader().read_slice(slice)
}
pub fn read_scalar<T: H5Type>(&self) -> Result<T> {
self.as_reader().read_scalar()
}
pub fn write<'b, A, T, D>(&self, arr: A) -> Result<()>
where
A: Into<ArrayView<'b, T, D>>,
T: H5Type,
D: ndarray::Dimension,
{
self.as_writer().write(arr)
}
pub fn write_raw<'b, A, T>(&self, arr: A) -> Result<()>
where
A: Into<ArrayView1<'b, T>>,
T: H5Type,
{
self.as_writer().write_raw(arr)
}
pub fn write_slice<'b, A, T, S, D>(&self, arr: A, slice: &SliceInfo<S, D>) -> Result<()>
where
A: Into<ArrayView<'b, T, D>>,
T: H5Type,
S: AsRef<[SliceOrIndex]>,
D: ndarray::Dimension,
{
self.as_writer().write_slice(arr, slice)
}
pub fn write_scalar<T: H5Type>(&self, val: &T) -> Result<()> {
self.as_writer().write_scalar(val)
}
}