diff --git a/hdf5-sys/src/h5.rs b/hdf5-sys/src/h5.rs index 61e58e157..bd255f702 100644 --- a/hdf5-sys/src/h5.rs +++ b/hdf5-sys/src/h5.rs @@ -1,3 +1,4 @@ +//! General purpose library functions use std::mem; pub use self::H5_index_t::*; diff --git a/hdf5-sys/src/h5a.rs b/hdf5-sys/src/h5a.rs index 82f81643a..7356ee32b 100644 --- a/hdf5-sys/src/h5a.rs +++ b/hdf5-sys/src/h5a.rs @@ -1,3 +1,4 @@ +//! Creating and manipulating HDF5 attributes use std::mem; use crate::internal_prelude::*; diff --git a/hdf5-sys/src/h5ac.rs b/hdf5-sys/src/h5ac.rs index 846a3339c..da1ba2217 100644 --- a/hdf5-sys/src/h5ac.rs +++ b/hdf5-sys/src/h5ac.rs @@ -1,3 +1,4 @@ +//! Cache functions use std::mem; use crate::internal_prelude::*; diff --git a/hdf5-sys/src/h5c.rs b/hdf5-sys/src/h5c.rs index 7eb8dc37d..8df09e70b 100644 --- a/hdf5-sys/src/h5c.rs +++ b/hdf5-sys/src/h5c.rs @@ -1,3 +1,4 @@ +//! Cache functionality pub use self::H5C_cache_decr_mode::*; pub use self::H5C_cache_flash_incr_mode::*; pub use self::H5C_cache_incr_mode::*; diff --git a/hdf5-sys/src/h5d.rs b/hdf5-sys/src/h5d.rs index 8098ca052..49584e60f 100644 --- a/hdf5-sys/src/h5d.rs +++ b/hdf5-sys/src/h5d.rs @@ -1,3 +1,4 @@ +//! Creating and manipulating scientific datasets pub use self::H5D_alloc_time_t::*; pub use self::H5D_fill_time_t::*; pub use self::H5D_fill_value_t::*; diff --git a/hdf5-sys/src/h5e.rs b/hdf5-sys/src/h5e.rs index 52a0510ff..f6bcbdf0d 100644 --- a/hdf5-sys/src/h5e.rs +++ b/hdf5-sys/src/h5e.rs @@ -1,3 +1,4 @@ +//! Functions for handling errors that occur within HDF5 use std::mem; pub use self::H5E_direction_t::*; diff --git a/hdf5-sys/src/h5f.rs b/hdf5-sys/src/h5f.rs index e471fd0e7..b0625c100 100644 --- a/hdf5-sys/src/h5f.rs +++ b/hdf5-sys/src/h5f.rs @@ -1,3 +1,4 @@ +//! Creating and manipulating HDF5 files use std::mem; pub use self::H5F_close_degree_t::*; diff --git a/hdf5-sys/src/h5fd.rs b/hdf5-sys/src/h5fd.rs index 610881c53..40b61930d 100644 --- a/hdf5-sys/src/h5fd.rs +++ b/hdf5-sys/src/h5fd.rs @@ -1,3 +1,4 @@ +//! File drivers use std::mem; pub use self::H5FD_file_image_op_t::*; diff --git a/hdf5-sys/src/h5g.rs b/hdf5-sys/src/h5g.rs index 261a2f78b..dc3e25b51 100644 --- a/hdf5-sys/src/h5g.rs +++ b/hdf5-sys/src/h5g.rs @@ -1,3 +1,4 @@ +//! Creating and manipulating groups of objects inside an HDF5 file use std::mem; pub use self::H5G_storage_type_t::*; diff --git a/hdf5-sys/src/h5i.rs b/hdf5-sys/src/h5i.rs index c46834d10..6fcbbbd58 100644 --- a/hdf5-sys/src/h5i.rs +++ b/hdf5-sys/src/h5i.rs @@ -1,3 +1,4 @@ +//! Manipulating object identifiers and object names pub use self::H5I_type_t::*; use crate::internal_prelude::*; diff --git a/hdf5-sys/src/h5l.rs b/hdf5-sys/src/h5l.rs index 7d1ebd812..e82b4277c 100644 --- a/hdf5-sys/src/h5l.rs +++ b/hdf5-sys/src/h5l.rs @@ -1,3 +1,4 @@ +//! Creating and manipulating links within an HDF5 group use std::mem; pub use self::H5L_type_t::*; diff --git a/hdf5-sys/src/h5mm.rs b/hdf5-sys/src/h5mm.rs index 7702099e7..8a0e861a8 100644 --- a/hdf5-sys/src/h5mm.rs +++ b/hdf5-sys/src/h5mm.rs @@ -1,3 +1,4 @@ +//! Memory managment use crate::internal_prelude::*; pub type H5MM_allocate_t = diff --git a/hdf5-sys/src/h5o.rs b/hdf5-sys/src/h5o.rs index 0a54cc1b7..8f998b5e2 100644 --- a/hdf5-sys/src/h5o.rs +++ b/hdf5-sys/src/h5o.rs @@ -1,3 +1,4 @@ +//! Manipulating objects in an HDF5 file use std::mem; pub use self::H5O_mcdt_search_ret_t::*; diff --git a/hdf5-sys/src/h5p.rs b/hdf5-sys/src/h5p.rs index 4d1e3229e..3df24b2ce 100644 --- a/hdf5-sys/src/h5p.rs +++ b/hdf5-sys/src/h5p.rs @@ -1,3 +1,4 @@ +//! Creating and manipulating property lists to control HDF5 library behaviour use crate::internal_prelude::*; use crate::h5ac::H5AC_cache_config_t; diff --git a/hdf5-sys/src/h5pl.rs b/hdf5-sys/src/h5pl.rs index 8fe3fba03..e693f6a97 100644 --- a/hdf5-sys/src/h5pl.rs +++ b/hdf5-sys/src/h5pl.rs @@ -1,3 +1,4 @@ +//! Programmatically controlling dynamically loaded plugins use crate::internal_prelude::*; #[cfg(hdf5_1_8_15)] diff --git a/hdf5-sys/src/h5r.rs b/hdf5-sys/src/h5r.rs index 28b14f09d..5a819627a 100644 --- a/hdf5-sys/src/h5r.rs +++ b/hdf5-sys/src/h5r.rs @@ -1,3 +1,4 @@ +//! Creating and manipulating references to specific objects and data regions in an HDF5 file pub use self::H5R_type_t::*; #[cfg(not(hdf5_1_10_0))] pub use H5Rdereference1 as H5Rdereference; diff --git a/hdf5-sys/src/h5s.rs b/hdf5-sys/src/h5s.rs index 9f306abd7..bb9b87f81 100644 --- a/hdf5-sys/src/h5s.rs +++ b/hdf5-sys/src/h5s.rs @@ -1,3 +1,4 @@ +//! Creating and manipulating dataspaces in which to store elements of a dataset pub use self::H5S_class_t::*; pub use self::H5S_sel_type::*; pub use self::H5S_seloper_t::*; diff --git a/hdf5-sys/src/h5t.rs b/hdf5-sys/src/h5t.rs index c9675e92e..d7368550d 100644 --- a/hdf5-sys/src/h5t.rs +++ b/hdf5-sys/src/h5t.rs @@ -1,3 +1,4 @@ +//! Creating and manipulating datatypes which describe elements of a dataset use std::mem; pub use self::H5T_bkg_t::*; diff --git a/hdf5-sys/src/h5vl.rs b/hdf5-sys/src/h5vl.rs index 5119b0226..e3ba5455d 100644 --- a/hdf5-sys/src/h5vl.rs +++ b/hdf5-sys/src/h5vl.rs @@ -1,3 +1,4 @@ +//! Using the Virtual Object Layer #![cfg(hdf5_1_12_0)] use crate::internal_prelude::*; diff --git a/hdf5-sys/src/h5z.rs b/hdf5-sys/src/h5z.rs index 6da1b98df..823a91599 100644 --- a/hdf5-sys/src/h5z.rs +++ b/hdf5-sys/src/h5z.rs @@ -1,3 +1,4 @@ +//! Configuring filters that process data during I/O operation use std::mem; pub use self::H5Z_EDC_t::*; diff --git a/hdf5-sys/src/lib.rs b/hdf5-sys/src/lib.rs index a5a61782e..990fb70f4 100644 --- a/hdf5-sys/src/lib.rs +++ b/hdf5-sys/src/lib.rs @@ -1,3 +1,4 @@ +//! Rust bindings to the `hdf5` library for reading and writing data to and from storage #![allow(non_camel_case_types, non_snake_case, dead_code, deprecated)] #![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal))] #![cfg_attr(feature = "cargo-clippy", allow(clippy::missing_safety_doc))] diff --git a/src/hl/container.rs b/src/hl/container.rs index 27be501d1..c166391a0 100644 --- a/src/hl/container.rs +++ b/src/hl/container.rs @@ -1,3 +1,4 @@ +use std::convert::TryInto; use std::fmt::{self, Debug}; use std::mem; use std::ops::Deref; @@ -67,12 +68,13 @@ impl<'a> Reader<'a> { pub fn read_slice(&self, selection: S) -> Result> where T: H5Type, - S: Into, + S: TryInto, + Error: From, D: ndarray::Dimension, { ensure!(!self.obj.is_attr(), "Slicing cannot be used on attribute datasets"); - let selection = selection.into(); + let selection = selection.try_into()?; let obj_space = self.obj.space()?; let out_shape = selection.out_shape(&obj_space.shape())?; @@ -147,7 +149,8 @@ impl<'a> Reader<'a> { pub fn read_slice_1d(&self, selection: S) -> Result> where T: H5Type, - S: Into, + S: TryInto, + Error: From, { self.read_slice(selection) } @@ -164,7 +167,8 @@ impl<'a> Reader<'a> { pub fn read_slice_2d(&self, selection: S) -> Result> where T: H5Type, - S: Into, + S: TryInto, + Error: From, { self.read_slice(selection) } @@ -236,12 +240,13 @@ impl<'a> Writer<'a> { where A: Into>, T: H5Type, - S: Into, + S: TryInto, + Error: From, D: ndarray::Dimension, { ensure!(!self.obj.is_attr(), "Slicing cannot be used on attribute datasets"); - let selection = selection.into(); + let selection = selection.try_into()?; let obj_space = self.obj.space()?; let out_shape = selection.out_shape(&obj_space.shape())?; @@ -343,6 +348,7 @@ impl<'a> Writer<'a> { #[repr(transparent)] #[derive(Clone)] +/// An object which can be read or written to. pub struct Container(Handle); impl ObjectClass for Container { @@ -467,7 +473,8 @@ impl Container { pub fn read_slice_1d(&self, selection: S) -> Result> where T: H5Type, - S: Into, + S: TryInto, + Error: From, { self.as_reader().read_slice_1d(selection) } @@ -484,7 +491,8 @@ impl Container { pub fn read_slice_2d(&self, selection: S) -> Result> where T: H5Type, - S: Into, + S: TryInto, + Error: From, { self.as_reader().read_slice_2d(selection) } @@ -502,7 +510,8 @@ impl Container { pub fn read_slice(&self, selection: S) -> Result> where T: H5Type, - S: Into, + S: TryInto, + Error: From, D: ndarray::Dimension, { self.as_reader().read_slice(selection) @@ -548,7 +557,8 @@ impl Container { where A: Into>, T: H5Type, - S: Into, + S: TryInto, + Error: From, D: ndarray::Dimension, { self.as_writer().write_slice(arr, selection) diff --git a/src/hl/selection.rs b/src/hl/selection.rs index 5f959b63d..60567064a 100644 --- a/src/hl/selection.rs +++ b/src/hl/selection.rs @@ -1,4 +1,5 @@ use std::borrow::Cow; +use std::convert::{TryFrom, TryInto}; use std::fmt::{self, Display}; use std::mem; use std::ops::{Deref, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}; @@ -108,15 +109,6 @@ fn check_coords(coords: &Array2, shape: &[Ix]) -> Result<()> { Ok(()) } -#[inline] -fn abs_index(len: usize, index: isize) -> isize { - if index < 0 { - (len as isize) + index - } else { - index - } -} - #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct RawSlice { pub start: Ix, @@ -227,30 +219,98 @@ impl RawSelection { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +/// A selector of a one-dimensional array +/// +/// The following examples will use an array of 11 elements +/// to illustrate the various selections. The active elements +/// are marked with an `s`. +/// ```text +/// // An array of 11 elements +/// x x x x x x x x x x x +/// ``` +/// +/// ```text +/// Index(4) +/// _ _ _ _ s _ _ _ _ _ _ +/// ``` +/// ```text +/// Slice { start: 0, step: 3, end: 4, block: 1 } +/// s _ _ s _ _ _ _ _ _ _ +/// ``` +/// ```text +/// SliceTo { start: 2, step: 3, end: 8, block: 1 } +/// _ _ s _ _ s _ _ _ _ _ +/// ``` +/// ```text +/// SliceCount { start: 1, step: 3, count: 2, block: 1 } +/// _ s _ _ s _ _ s _ _ _ +/// ``` +/// ```text +/// Unlimited { start: 0, step: 3, block: 1 } +/// s _ _ s _ _ s _ _ s _ +/// ``` +/// ```text +/// Unlimited { start: 2, step: 3, block: 1 } +/// _ _ s _ _ s _ _ s _ _ +/// ``` +/// ```text +/// Unlimited { start: 0, step: 4, block: 2 } +/// s s _ _ s s _ _ s s _ +/// ``` +/// +/// See also [`this hdf5 tutorial`](https://support.hdfgroup.org/HDF5/Tutor/select.html) +/// for more information on hyperslab selections. +#[derive(Clone, Copy, Debug, Eq)] pub enum SliceOrIndex { - Index(isize), - Slice { start: isize, step: isize, end: Option, block: bool }, - Unlimited { start: isize, step: isize, block: bool }, + /// A single index + Index(Ix), + /// Up to the given index + SliceTo { start: Ix, step: Ix, end: Ix, block: Ix }, + /// The given count of elements + SliceCount { start: Ix, step: Ix, count: Ix, block: Ix }, + /// An unlimited hyperslab + Unlimited { start: Ix, step: Ix, block: Ix }, } -impl SliceOrIndex { - pub fn to_unlimited(self) -> Result { - Ok(match self { - Self::Index(_) => fail!("Cannot make index selection unlimited"), - Self::Slice { end: Some(_), .. } => { - fail!("Cannot make bounded slice unlimited") +impl PartialEq for SliceOrIndex { + fn eq(&self, other: &Self) -> bool { + use SliceOrIndex::{Index, SliceCount, SliceTo, Unlimited}; + match (self, other) { + (Index(s), Index(o)) => s == o, + ( + SliceTo { start: sstart, step: sstep, end: send, block: sblock }, + SliceTo { start: ostart, step: ostep, end: oend, block: oblock }, + ) => (sstart == ostart) & (sstep == ostep) & (send == oend) & (sblock == oblock), + ( + SliceCount { start: sstart, step: sstep, count: scount, block: sblock }, + SliceCount { start: ostart, step: ostep, count: ocount, block: oblock }, + ) => (sstart == ostart) & (sstep == ostep) & (scount == ocount) & (sblock == oblock), + ( + Unlimited { start: sstart, step: sstep, block: sblock }, + Unlimited { start: ostart, step: ostep, block: oblock }, + ) => (sstart == ostart) & (sstep == ostep) & (sblock == oblock), + ( + SliceTo { start: sstart, step: sstep, end: _, block: sblock }, + SliceCount { start: ostart, step: ostep, count: ocount, block: oblock }, + ) => { + if (sstart != ostart) | (sstep != ostep) | (sblock != oblock) { + return false; + } + self.count().unwrap() == *ocount } - Self::Slice { start, step, end: None, block } => Self::Unlimited { start, step, block }, - Self::Unlimited { .. } => self, - }) + (SliceCount { .. }, SliceTo { .. }) => other == self, + _ => false, + } } +} - pub fn to_block(self) -> Result { +impl SliceOrIndex { + pub fn to_unlimited(self) -> Result { Ok(match self { - Self::Index(_) => fail!("Cannot make index selection block-like"), - Self::Slice { start, step, end, .. } => Self::Slice { start, step, end, block: true }, - Self::Unlimited { start, step, .. } => Self::Unlimited { start, step, block: true }, + Self::Index(_) => fail!("Cannot make index selection unlimited"), + Self::SliceTo { start, step, block, .. } + | Self::SliceCount { start, step, block, .. } + | Self::Unlimited { start, step, block } => Self::Unlimited { start, step, block }, }) } @@ -259,44 +319,240 @@ impl SliceOrIndex { } pub fn is_slice(self) -> bool { - matches!(self, Self::Slice { .. }) + matches!(self, Self::SliceTo { .. } | Self::SliceCount { .. } | Self::Unlimited { .. }) } pub fn is_unlimited(self) -> bool { matches!(self, Self::Unlimited { .. }) } + + fn set_blocksize(self, blocksize: Ix) -> Result { + Ok(match self { + Self::Index(_) => fail!("Cannot set blocksize for index selection"), + Self::SliceTo { start, step, end, .. } => { + Self::SliceTo { start, step, end, block: blocksize } + } + Self::SliceCount { start, step, count, .. } => { + Self::SliceCount { start, step, count, block: blocksize } + } + Self::Unlimited { start, step, .. } => { + Self::Unlimited { start, step, block: blocksize } + } + }) + } + + /// Number of elements contained in the `SliceOrIndex` + fn count(self) -> Option { + use SliceOrIndex::{Index, SliceCount, SliceTo, Unlimited}; + match self { + Index(_) => Some(1), + SliceTo { start, step, end, block } => { + Some((start + block.saturating_sub(1)..end).step_by(step).count()) + } + SliceCount { count, .. } => Some(count), + Unlimited { .. } => None, + } + } } -#[allow(clippy::fallible_impl_from)] -impl> From for SliceOrIndex { - fn from(slice: T) -> Self { - match slice.into() { - ndarray::SliceInfoElem::Index(index) => Self::Index(index), +impl TryFrom for SliceOrIndex { + type Error = Error; + fn try_from(slice: ndarray::SliceInfoElem) -> Result { + Ok(match slice { + ndarray::SliceInfoElem::Index(index) => match Ix::try_from(index) { + Err(_) => fail!("Index must be non-negative"), + Ok(index) => Self::Index(index), + }, ndarray::SliceInfoElem::Slice { start, end, step } => { - Self::Slice { start, step, end, block: false } + let start = + Ix::try_from(start).map_err(|_| Error::from("Index must be non-negative"))?; + let step = + Ix::try_from(step).map_err(|_| Error::from("Step must be non-negative"))?; + let end = end.map(|end| { + Ix::try_from(end).map_err(|_| Error::from("End must be non-negative")) + }); + match end { + Some(Ok(end)) => Self::SliceTo { start, step, end, block: 1 }, + None => Self::Unlimited { start, step, block: 1 }, + Some(Err(e)) => return Err(e), + } } - ndarray::SliceInfoElem::NewAxis => panic!("ndarray NewAxis can not be mapped to hdf5"), + ndarray::SliceInfoElem::NewAxis => fail!("ndarray NewAxis can not be mapped to hdf5"), + }) + } +} + +impl TryFrom for Hyperslab { + type Error = Error; + fn try_from(slice: ndarray::SliceInfoElem) -> Result { + Ok(vec![slice.try_into()?].into()) + } +} + +impl TryFrom for Selection { + type Error = Error; + fn try_from(slice: ndarray::SliceInfoElem) -> Result { + Hyperslab::try_from(slice).map(Into::into) + } +} + +impl From for SliceOrIndex { + fn from(_r: RangeFull) -> Self { + Self::Unlimited { start: 0, step: 1, block: 1 } + } +} + +impl TryFrom for SliceOrIndex { + type Error = std::num::TryFromIntError; + fn try_from(slice: ndarray::Slice) -> Result { + let ndarray::Slice { start, end, step } = slice; + let start = start.try_into()?; + let step = step.try_into()?; + let end = end.map(|end| end.try_into()); + match end { + Some(Ok(end)) => Ok(Self::SliceTo { start, end, step, block: 1 }), + None => Ok(Self::Unlimited { start, step, block: 1 }), + Some(Err(e)) => Err(e), } } } +impl From for SliceOrIndex { + fn from(val: usize) -> Self { + Self::Index(val as _) + } +} + +impl From for Hyperslab { + fn from(slice: usize) -> Self { + (slice,).into() + } +} + +impl From for Selection { + fn from(slice: usize) -> Self { + Hyperslab::from(slice).into() + } +} + +impl From> for SliceOrIndex { + fn from(val: Range) -> Self { + Self::SliceTo { start: val.start as _, step: 1, end: val.end, block: 1 } + } +} + +impl From> for Hyperslab { + fn from(val: Range) -> Self { + vec![val.into()].into() + } +} + +impl From> for Selection { + fn from(val: Range) -> Self { + Hyperslab::from(val).into() + } +} + +impl From> for SliceOrIndex { + fn from(val: RangeToInclusive) -> Self { + let end = val.end + 1; + Self::SliceTo { start: 0, step: 1, end, block: 1 } + } +} + +impl From> for Hyperslab { + fn from(val: RangeToInclusive) -> Self { + vec![val.into()].into() + } +} + +impl From> for Selection { + fn from(val: RangeToInclusive) -> Self { + Hyperslab::from(val).into() + } +} + +impl From> for SliceOrIndex { + fn from(val: RangeFrom) -> Self { + Self::Unlimited { start: val.start, step: 1, block: 1 } + } +} + +impl From> for Hyperslab { + fn from(val: RangeFrom) -> Self { + vec![val.into()].into() + } +} + +impl From> for Selection { + fn from(val: RangeFrom) -> Self { + Hyperslab::from(val).into() + } +} + +impl From> for SliceOrIndex { + fn from(val: RangeInclusive) -> Self { + Self::SliceTo { start: *val.start(), step: 1, end: *val.end() + 1, block: 1 } + } +} + +impl From> for Hyperslab { + fn from(val: RangeInclusive) -> Self { + vec![val.into()].into() + } +} + +impl From> for Selection { + fn from(val: RangeInclusive) -> Self { + Hyperslab::from(val).into() + } +} + +impl From> for SliceOrIndex { + fn from(val: RangeTo) -> Self { + Self::SliceTo { start: 0, step: 1, end: val.end, block: 1 } + } +} + +impl From> for Hyperslab { + fn from(val: RangeTo) -> Self { + vec![val.into()].into() + } +} + +impl From> for Selection { + fn from(val: RangeTo) -> Self { + Hyperslab::from(val).into() + } +} + impl Display for SliceOrIndex { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Self::Index(index) => write!(f, "{}", index)?, - Self::Slice { start, end, step, block } => { + Self::SliceTo { start, end, step, block } => { if start != 0 { write!(f, "{}", start)?; } write!(f, "..")?; - if let Some(end) = end { - write!(f, "{}", end)?; + write!(f, "{}", end)?; + if step != 1 { + write!(f, ";{}", step)?; } + if block != 1 { + write!(f, "(Bx{})", block)?; + } + } + Self::SliceCount { start, step, count, block } => { + if start != 0 { + write!(f, "{}", start)?; + } + write!(f, "+{}", count)?; if step != 1 { write!(f, ";{}", step)?; } - if block { - write!(f, "(B)")?; + if block != 1 { + write!(f, "(Bx{})", block)?; } } Self::Unlimited { start, step, block } => { @@ -308,8 +564,8 @@ impl Display for SliceOrIndex { if step != 1 { write!(f, ";{}", step)?; } - if block { - write!(f, "(B)")?; + if block != 1 { + write!(f, "(Bx{})", block)?; } } } @@ -318,6 +574,11 @@ impl Display for SliceOrIndex { } #[derive(Clone, Debug, PartialEq, Eq)] +/// A descriptor of a selection of an N-dimensional array. +/// +/// The Hyperslab consists of [`slices`](SliceOrIndex) in N dimensions, +/// spanning an N-dimensional hypercube. This type is used as a [`selector`](Selection) +/// for retrieving and putting data to a [`Container`](Container). pub struct Hyperslab { dims: Vec, } @@ -327,6 +588,10 @@ impl Hyperslab { hyper.into() } + pub fn try_new>(hyper: T) -> Result { + hyper.try_into() + } + pub fn is_unlimited(&self) -> bool { self.iter().any(|&s| s.is_unlimited()) } @@ -336,10 +601,7 @@ impl Hyperslab { } pub fn set_unlimited(&self, axis: usize) -> Result { - let unlim = self.unlimited_axis(); - if unlim.is_some() && unlim != Some(axis) { - fail!("The hyperslab already has one unlimited axis"); - } else if axis < self.len() { + if axis < self.len() { let mut hyper = self.clone(); hyper.dims[axis] = hyper.dims[axis].to_unlimited()?; Ok(hyper) @@ -348,14 +610,11 @@ impl Hyperslab { } } - pub fn set_block(&self, axis: usize) -> Result { - if axis < self.len() { - let mut hyper = self.clone(); - hyper.dims[axis] = hyper.dims[axis].to_block()?; - Ok(hyper) - } else { - fail!("Invalid axis for changing the slice to block-like: {}", axis); - } + pub fn set_block(&self, axis: usize, blocksize: Ix) -> Result { + ensure!(axis < self.len(), "Invalid axis for changing the slice to block-like: {}", axis); + let mut hyper = self.clone(); + hyper.dims[axis] = hyper.dims[axis].set_blocksize(blocksize)?; + Ok(hyper) } #[doc(hidden)] @@ -363,8 +622,8 @@ impl Hyperslab { let shape = shape.as_ref(); let ndim = shape.len(); ensure!(self.len() == ndim, "Slice ndim ({}) != shape ndim ({})", self.len(), ndim); - let n_unlimited: usize = self.iter().map(|s| s.is_unlimited() as usize).sum(); - ensure!(n_unlimited <= 1, "Expected at most 1 unlimited dimension, got {}", n_unlimited); + //let n_unlimited: usize = self.iter().map(|s| s.is_unlimited() as usize).sum(); + //ensure!(n_unlimited <= 1, "Expected at most 1 unlimited dimension, got {}", n_unlimited); let hyper = RawHyperslab::from( self.iter() .zip(shape) @@ -379,29 +638,19 @@ impl Hyperslab { #[allow(clippy::needless_pass_by_value)] pub fn from_raw(hyper: RawHyperslab) -> Result { let mut dims = vec![]; - for (i, slice) in hyper.iter().enumerate() { - let block = if slice.block == 1 { - false - } else if slice.block == slice.step { - true - } else { - fail!("Unsupported block/step for axis {}: {}/{}", i, slice.block, slice.step); - }; + for (axis, slice) in hyper.iter().enumerate() { + ensure!(slice.step >= slice.block, "Blocks can not overlap (axis: {})", axis); dims.push(match slice.count { - Some(count) => SliceOrIndex::Slice { - start: slice.start as _, - step: slice.step as _, - end: Some( - (slice.start - + if count == 0 { 0 } else { (count - 1) * slice.step + slice.block }) - as _, - ), - block, + Some(count) => SliceOrIndex::SliceCount { + start: slice.start, + step: slice.step, + count, + block: slice.block, }, None => SliceOrIndex::Unlimited { - start: slice.start as _, - step: slice.step as _, - block, + start: slice.start, + step: slice.step, + block: slice.block, }, }); } @@ -441,45 +690,65 @@ impl From for Hyperslab { } } -impl From> for Hyperslab +impl TryFrom for Hyperslab { + type Error = Error; + fn try_from(slice: ndarray::Slice) -> Result { + Ok(vec![SliceOrIndex::try_from(slice).map_err(|_| Error::from("Invalid slice"))?].into()) + } +} + +impl TryFrom> for Hyperslab where T: AsRef<[ndarray::SliceInfoElem]>, Din: ndarray::Dimension, Dout: ndarray::Dimension, { - fn from(slice: ndarray::SliceInfo) -> Self { - slice.deref().as_ref().iter().copied().map(Into::into).collect::>().into() + type Error = Error; + fn try_from(slice: ndarray::SliceInfo) -> Result { + slice + .deref() + .as_ref() + .iter() + .copied() + .map(TryInto::try_into) + .collect::>>() + .map(Into::into) } } +/// Turns `SliceOrIndex` into real dimensions given `dim` as the maximum dimension fn slice_info_to_raw(axis: usize, slice: &SliceOrIndex, dim: Ix) -> Result { let err_msg = || format!("out of bounds for axis {} with size {}", axis, dim); let (start, step, count, block) = match *slice { SliceOrIndex::Index(index) => { - let idx = abs_index(dim, index); - ensure!(idx >= 0 && idx < dim as _, "Index {} {}", index, err_msg()); - (idx as _, 1, Some(1), 1) + ensure!(index < dim, "Index {} {}", index, err_msg()); + (index, 1, 1, 1) } - SliceOrIndex::Slice { start, step, end, block } => { + SliceOrIndex::SliceTo { start, step, end, block } => { ensure!(step >= 1, "Slice stride {} < 1 for axis {}", step, axis); - let s = abs_index(dim, start); - ensure!(s >= 0 && s <= dim as _, "Slice start {} {}", start, err_msg()); - let end = end.unwrap_or(dim as _); - let e = abs_index(dim, end); - ensure!(e >= 0 && e <= dim as _, "Slice end {} {}", end, err_msg()); - let block = if block { step } else { 1 }; - let count = if e < s + block { 0 } else { 1 + (e - s - block) / step }; - (s as _, step as _, Some(count as _), block as _) + ensure!(start <= dim, "Slice start {} {}", start, err_msg()); + ensure!(end <= dim, "Slice end {} {}", end, err_msg()); + ensure!(step > 0, "Stride {} {}", step, err_msg()); + let count = slice.count().unwrap(); + (start, step, count, block) } - SliceOrIndex::Unlimited { start, step, block } => { + SliceOrIndex::SliceCount { start, step, count, block } => { ensure!(step >= 1, "Slice stride {} < 1 for axis {}", step, axis); - let s = abs_index(dim, start); - ensure!(s >= 0 && s <= dim as _, "Slice start {} {}", start, err_msg()); - let block = if block { step } else { 1 }; - (s as _, step as _, None, block as _) + ensure!(start <= dim as _, "Slice start {} {}", start, err_msg()); + let end = start + block.saturating_sub(1) + step * count.saturating_sub(1); + ensure!(end <= dim, "Slice end {} {}", end, err_msg()); + (start, step, count, block) + } + SliceOrIndex::Unlimited { start, step, block } => { + // Replace infinite slice with one limited by the current dimension + return slice_info_to_raw( + axis, + &SliceOrIndex::SliceTo { start, step, end: dim, block }, + dim, + ); } }; - Ok(RawSlice { start, step, count, block }) + Ok(RawSlice { start, step, count: Some(count), block }) } impl Display for Hyperslab { @@ -500,6 +769,7 @@ impl Display for Hyperslab { } #[derive(Clone, Debug, PartialEq, Eq)] +/// A selection used for reading and writing to a [`Container`](Container). pub enum Selection { All, Points(Array2), @@ -517,6 +787,10 @@ impl Selection { selection.into() } + pub fn try_new>(selection: T) -> Result { + selection.try_into() + } + #[doc(hidden)] pub fn into_raw>(self, shape: S) -> Result { let shape = shape.as_ref(); @@ -669,14 +943,22 @@ impl From for Selection { } } -impl From> for Selection +impl TryFrom for Selection { + type Error = Error; + fn try_from(slice: ndarray::Slice) -> Result { + Hyperslab::try_from(slice).map(|x| x.into()) + } +} + +impl TryFrom> for Selection where T: AsRef<[ndarray::SliceInfoElem]>, Din: ndarray::Dimension, Dout: ndarray::Dimension, { - fn from(slice: ndarray::SliceInfo) -> Self { - Hyperslab::from(slice).into() + type Error = Error; + fn try_from(slice: ndarray::SliceInfo) -> Result { + Hyperslab::try_from(slice).map(Into::into) } } @@ -686,14 +968,13 @@ impl From> for Selection { } } -#[allow(clippy::fallible_impl_from)] impl From> for Selection { fn from(points: Array1) -> Self { let n = points.len(); Self::Points(if n == 0 { Array2::zeros((0, 0)) } else { - points.into_shape((n, 1)).unwrap().into_dimensionality().unwrap() + points.insert_axis(ndarray::Axis(1)) }) } } @@ -734,27 +1015,17 @@ impl From<&[Ix]> for Selection { } } -macro_rules! impl_fixed { - () => (); - - ($head:expr, $($tail:expr,)*) => ( - impl From<[Ix; $head]> for Selection { - fn from(points: [Ix; $head]) -> Self { - points.as_ref().into() - } - } - - impl From<&[Ix; $head]> for Selection { - fn from(points: &[Ix; $head]) -> Self { - points.as_ref().into() - } - } - - impl_fixed! { $($tail,)* } - ) +impl From<[Ix; N]> for Selection { + fn from(points: [Ix; N]) -> Self { + points.as_ref().into() + } } -impl_fixed! { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, } +impl From<&[Ix; N]> for Selection { + fn from(points: &[Ix; N]) -> Self { + points.as_ref().into() + } +} macro_rules! impl_tuple { () => (); @@ -785,42 +1056,6 @@ macro_rules! impl_tuple { impl_tuple! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, } -macro_rules! impl_slice_scalar { - ($tp:ty) => { - impl From<$tp> for Hyperslab { - fn from(slice: $tp) -> Self { - (slice,).into() - } - } - - impl From<$tp> for Selection { - fn from(slice: $tp) -> Self { - Hyperslab::from(slice).into() - } - } - }; -} - -impl_slice_scalar!(isize); -impl_slice_scalar!(usize); -impl_slice_scalar!(i32); -impl_slice_scalar!(ndarray::Slice); -impl_slice_scalar!(ndarray::SliceInfoElem); - -macro_rules! impl_range_scalar { - ($index:ty) => { - impl_slice_scalar!(Range<$index>); - impl_slice_scalar!(RangeFrom<$index>); - impl_slice_scalar!(RangeInclusive<$index>); - impl_slice_scalar!(RangeTo<$index>); - impl_slice_scalar!(RangeToInclusive<$index>); - }; -} - -impl_range_scalar!(isize); -impl_range_scalar!(usize); -impl_range_scalar!(i32); - #[cfg(test)] mod test { use ndarray::{arr1, arr2, s, Array2}; @@ -831,52 +1066,63 @@ mod test { }; use crate::internal_prelude::*; + #[test] + fn count() { + use SliceOrIndex::*; + assert_eq!(Unlimited { start: 0, step: 1, block: 1 }.count(), None); + assert_eq!(Index(23412).count(), Some(1)); + assert_eq!(SliceCount { start: 0, step: 1431, count: 41, block: 4 }.count(), Some(41)); + assert_eq!(SliceTo { start: 0, step: 1, end: 1, block: 1 }.count(), Some(1)); + assert_eq!(SliceTo { start: 0, step: 10, end: 1, block: 1 }.count(), Some(1)); + assert_eq!(SliceTo { start: 0, step: 1, end: 10, block: 1 }.count(), Some(10)); + assert_eq!(SliceTo { start: 0, step: 10, end: 10, block: 1 }.count(), Some(1)); + assert_eq!(SliceTo { start: 0, step: 9, end: 10, block: 1 }.count(), Some(2)); + assert_eq!(SliceTo { start: 0, step: 9, end: 10, block: 2 }.count(), Some(1)); + assert_eq!(SliceTo { start: 1, step: 3, end: 6, block: 1 }.count(), Some(2)); + } + #[test] fn test_slice_or_index_impl() -> Result<()> { + use std::convert::TryFrom; let s = SliceOrIndex::from(2); assert_eq!(s, Index(2)); assert!(s.is_index()); assert!(!s.is_slice()); assert!(!s.is_unlimited()); assert_err!(s.to_unlimited(), "Cannot make index selection unlimited"); - assert_err!(s.to_block(), "Cannot make index selection block-like"); + assert_err!(s.set_blocksize(2), "Cannot set blocksize for index selection"); let s = SliceOrIndex::from(2..=5); - assert_eq!(s, Slice { start: 2, step: 1, end: Some(6), block: false }); + assert_eq!(s, SliceTo { start: 2, step: 1, end: 6, block: 1 }); assert!(!s.is_index()); assert!(s.is_slice()); assert!(!s.is_unlimited()); - assert_err!(s.to_unlimited(), "Cannot make bounded slice unlimited"); - assert_eq!(s.to_block()?, Slice { start: 2, step: 1, end: Some(6), block: true }); + assert_eq!(s.to_unlimited().unwrap(), Unlimited { start: 2, step: 1, block: 1 }); + assert_eq!(s.set_blocksize(4)?, SliceTo { start: 2, step: 1, end: 6, block: 4 }); assert_eq!( - SliceOrIndex::from(*s![1..2;3].get(0).unwrap()), - Slice { start: 1, step: 3, end: Some(2), block: false } + SliceOrIndex::try_from(*s![1..2;3].get(0).unwrap()).unwrap(), + SliceTo { start: 1, step: 3, end: 2, block: 1 } ); let s = SliceOrIndex::from(3..).to_unlimited()?; - assert_eq!(s, Unlimited { start: 3, step: 1, block: false }); + assert_eq!(s, Unlimited { start: 3, step: 1, block: 1 }); assert!(!s.is_index()); - assert!(!s.is_slice()); + assert!(s.is_slice()); assert!(s.is_unlimited()); assert_eq!(s.to_unlimited()?, s); - assert_eq!(s.to_block()?, Unlimited { start: 3, step: 1, block: true }); for (s, f) in &[ - (Index(-1), "-1"), - (Slice { start: 0, step: 1, end: None, block: false }, ".."), - (Slice { start: 0, step: 1, end: None, block: true }, "..(B)"), - (Slice { start: -1, step: 3, end: None, block: false }, "-1..;3"), - (Slice { start: -1, step: 1, end: None, block: true }, "-1..(B)"), - (Slice { start: 0, step: 1, end: Some(5), block: false }, "..5"), - (Slice { start: 0, step: 3, end: Some(5), block: true }, "..5;3(B)"), - (Slice { start: -1, step: 1, end: Some(5), block: false }, "-1..5"), - (Slice { start: -1, step: 1, end: Some(5), block: true }, "-1..5(B)"), - (Unlimited { start: 0, step: 1, block: false }, "..∞"), - (Unlimited { start: 0, step: 3, block: true }, "..∞;3(B)"), - (Unlimited { start: -1, step: 1, block: false }, "-1..∞"), - (Unlimited { start: -1, step: 3, block: true }, "-1..∞;3(B)"), + (Unlimited { start: 0, step: 1, block: 1 }, "..∞"), + (Unlimited { start: 0, step: 1, block: 2 }, "..∞(Bx2)"), + (SliceTo { start: 0, step: 1, end: 5, block: 1 }, "..5"), + (SliceTo { start: 0, step: 3, end: 5, block: 2 }, "..5;3(Bx2)"), + (Unlimited { start: 0, step: 1, block: 1 }, "..∞"), + (Unlimited { start: 0, step: 3, block: 2 }, "..∞;3(Bx2)"), + (SliceCount { start: 1, step: 3, count: 5, block: 1 }, "1+5;3"), + (SliceCount { start: 0, step: 3, count: 5, block: 2 }, "+5;3(Bx2)"), + (SliceCount { start: 1, step: 3, count: 5, block: 2 }, "1+5;3(Bx2)"), ] { - assert_eq!(format!("{}", s), f.to_owned()); + assert_eq!(&format!("{}", s), f); } Ok(()) @@ -886,8 +1132,8 @@ mod test { fn test_selection_hyperslab_new() { macro_rules! check { ($hs1:expr, $hs2:expr) => { - assert_eq!(Hyperslab::new($hs1).as_ref().to_owned(), $hs2); - let s = Selection::new($hs1); + assert_eq!(Hyperslab::try_new($hs1).unwrap().as_ref().to_owned(), $hs2); + let s = Selection::try_new($hs1).unwrap(); assert_eq!(s, Selection::new(Hyperslab::new($hs2))); assert_eq!(s, Selection::Hyperslab(Hyperslab::new($hs2))); }; @@ -895,38 +1141,18 @@ mod test { check!((), vec![]); check!(Index(2), vec![Index(2)]); - check!( - s![-1, 2..;3, ..4], - vec![ - Index(-1), - Slice { start: 2, step: 3, end: None, block: false }, - Slice { start: 0, step: 1, end: Some(4), block: false }, - ] - ); - - check!( - ndarray::Slice::new(-1, None, 2), - vec![Slice { start: -1, step: 2, end: None, block: false }] - ); check!(ndarray::SliceInfoElem::Index(10), vec![Index(10)]); - - check!(-1, vec![Index(-1)]); - check!(-1..2, vec![Slice { start: -1, step: 1, end: Some(2), block: false }]); - check!(-1..=2, vec![Slice { start: -1, step: 1, end: Some(3), block: false }]); - check!(3.., vec![Slice { start: 3, step: 1, end: None, block: false }]); - check!(..-1, vec![Slice { start: 0, step: 1, end: Some(-1), block: false }]); - check!(..=-1, vec![Slice { start: 0, step: 1, end: None, block: false }]); - - check!( - (-1..2, Index(-1)), - vec![Slice { start: -1, step: 1, end: Some(2), block: false }, Index(-1)] - ); + check!(3.., vec![Unlimited { start: 3, step: 1, block: 1 }]); assert_eq!( Hyperslab::new(..).as_ref().to_owned(), - vec![Slice { start: 0, step: 1, end: None, block: false }] + vec![Unlimited { start: 0, step: 1, block: 1 }] ); assert_eq!(Selection::new(..), Selection::All); + + use std::convert::TryFrom; + assert!(Selection::try_from(s![-1, 2..;3, ..4]).is_err()); + assert!(Selection::try_from(ndarray::Slice::new(-1, None, 2)).is_err()); } #[test] @@ -958,20 +1184,20 @@ mod test { #[test] fn test_hyperslab_impl() -> Result<()> { - let h = Hyperslab::new(s![0, 1..10, 2..;3]); + let h = Hyperslab::try_new(s![0, 1..10, 2..;3])?; assert_eq!( h.as_ref().to_owned(), vec![ Index(0), - Slice { start: 1, step: 1, end: Some(10), block: false }, - Slice { start: 2, step: 3, end: None, block: false }, + SliceTo { start: 1, step: 1, end: 10, block: 1 }, + Unlimited { start: 2, step: 3, block: 1 }, ] ); - assert!(!h.is_unlimited()); - assert!(h.unlimited_axis().is_none()); + assert!(h.is_unlimited()); + assert_eq!(h.unlimited_axis(), Some(2)); assert_err!(h.set_unlimited(0), "Cannot make index selection unlimited"); - assert_err!(h.set_unlimited(1), "Cannot make bounded slice unlimited"); + h.set_unlimited(1)?; assert_err!(h.set_unlimited(3), "Invalid axis for making hyperslab unlimited: 3"); let u = h.set_unlimited(2)?; assert!(u.is_unlimited()); @@ -980,34 +1206,34 @@ mod test { u.as_ref().to_owned(), vec![ Index(0), - Slice { start: 1, step: 1, end: Some(10), block: false }, - Unlimited { start: 2, step: 3, block: false }, + SliceTo { start: 1, step: 1, end: 10, block: 1 }, + Unlimited { start: 2, step: 3, block: 1 }, ] ); - assert_err!(u.set_unlimited(1), "The hyperslab already has one unlimited axis"); + u.set_unlimited(1).unwrap(); assert_eq!(u.set_unlimited(2)?, u); - assert_err!(u.set_block(0), "Cannot make index selection block-like"); - assert_err!(u.set_block(3), "Invalid axis for changing the slice to block-like: 3"); - let b = u.set_block(1)?; + assert_err!(u.set_block(0, 1), "Cannot set blocksize for index selection"); + assert_err!(u.set_block(3, 1), "Invalid axis for changing the slice to block-like: 3"); + let b = u.set_block(1, 2)?; assert_eq!( b.as_ref().to_owned(), vec![ Index(0), - Slice { start: 1, step: 1, end: Some(10), block: true }, - Unlimited { start: 2, step: 3, block: false }, + SliceTo { start: 1, step: 1, end: 10, block: 2 }, + Unlimited { start: 2, step: 3, block: 1 }, ] ); - let b = b.set_block(2)?; + let b = b.set_block(2, 2)?; assert_eq!( b.as_ref().to_owned(), vec![ Index(0), - Slice { start: 1, step: 1, end: Some(10), block: true }, - Unlimited { start: 2, step: 3, block: true }, + SliceTo { start: 1, step: 1, end: 10, block: 2 }, + Unlimited { start: 2, step: 3, block: 2 }, ] ); - assert_eq!(b.set_block(1)?.set_block(2)?, b); + assert_eq!(b.set_block(1, 2)?.set_block(2, 2)?, b); Ok(()) } @@ -1055,7 +1281,7 @@ mod test { #[test] fn test_selection_hyperslab_impl() { - let s = Selection::new(s![1, 2..;2]); + let s = Selection::try_new(s![1, 2..;2]).unwrap(); assert_eq!(s, s); assert!(!s.is_all() && s.is_hyperslab() && !s.is_points() && !s.is_none()); assert_ne!(s, Selection::new(..)); @@ -1063,64 +1289,49 @@ mod test { assert_eq!(s.in_ndim(), Some(2)); assert_eq!(s.out_ndim(), Some(1)); assert_eq!(s.out_shape(&[10, 20]).unwrap(), &[9]); - assert_eq!(format!("{}", Selection::new(s![1])), "(1,)"); + assert_eq!(format!("{}", Selection::try_new(s![1]).unwrap()), "(1,)"); assert_eq!(format!("{}", Selection::new(())), "()"); - let h = Hyperslab::new(s![1, 2..;3, ..4, 5..]).set_unlimited(1).unwrap(); - assert_eq!(format!("{}", h), "(1, 2..∞;3, ..4, 5..)"); + let h = Hyperslab::try_new(s![1, 2..;3, ..4, 5..]).unwrap().set_unlimited(1).unwrap(); + assert_eq!(format!("{}", h), "(1, 2..∞;3, ..4, 5..∞)"); let s = Selection::new(h); - assert_eq!(format!("{}", s), "(1, 2..∞;3, ..4, 5..)"); - assert_err!(s.out_shape(&[2, 3, 4, 5]), "Unable to get the shape for unlimited hyperslab"); + assert_eq!(format!("{}", s), "(1, 2..∞;3, ..4, 5..∞)"); + assert_eq!(s.out_shape(&[2, 3, 4, 5]).unwrap(), &[1, 4, 0]); } #[test] fn test_hyperslab_into_from_raw_err() { - fn check, S: AsRef<[Ix]>>(hyper: H, shape: S, err: &str) { - assert_err!(hyper.into().into_raw(shape.as_ref()), err); + use std::convert::TryInto; + #[track_caller] + fn check, S: AsRef<[Ix]>>(hyper: H, shape: S, err: &str) + where + H::Error: std::fmt::Debug, + { + let hyper = hyper.try_into().unwrap(); + assert_err!(hyper.into_raw(shape.as_ref()), err); } - check( - Hyperslab::new(vec![ - Unlimited { start: 0, step: 1, block: false }, - Unlimited { start: 0, step: 1, block: false }, - ]), - &[1, 2], - "Expected at most 1 unlimited dimension, got 2", - ); - check(s![1, 2], &[1, 2, 3], "Slice ndim (2) != shape ndim (3)"); - - check(s![0, ..;-1], &[1, 2], "Slice stride -1 < 1 for axis 1"); - + assert!(Hyperslab::try_new(s![0, ..;-1]).is_err()); check(s![0, 0], &[0, 1], "Index 0 out of bounds for axis 0 with size 0"); check(s![.., 1], &[0, 1], "Index 1 out of bounds for axis 1 with size 1"); - check(s![-3], &[2], "Index -3 out of bounds for axis 0 with size 2"); + assert!(Hyperslab::try_new(s![-3]).is_err()); check(s![2], &[2], "Index 2 out of bounds for axis 0 with size 2"); check(s![0, 3..], &[1, 2], "Slice start 3 out of bounds for axis 1 with size 2"); - check(s![-2..;2, 0], &[1, 2], "Slice start -2 out of bounds for axis 0 with size 1"); + assert!(Hyperslab::try_new(s![-2..;2, 0]).is_err()); check(s![0, ..=3], &[1, 2], "Slice end 4 out of bounds for axis 1 with size 2"); - check(s![..-3;2, 0], &[1, 2], "Slice end -3 out of bounds for axis 0 with size 1"); + assert!(Hyperslab::try_new(s![-2..;2, 0]).is_err()); check( - (0, Unlimited { start: 0, step: -1, block: true }), - &[1, 2], - "Slice stride -1 < 1 for axis 1", - ); - check( - (0, Unlimited { start: 3, step: 1, block: false }), + (0, Unlimited { start: 3, step: 1, block: 1 }), &[1, 2], "Slice start 3 out of bounds for axis 1 with size 2", ); - check( - (Unlimited { start: -2, step: 2, block: true }, 0), - &[1, 2], - "Slice start -2 out of bounds for axis 0 with size 1", - ); assert_err!( Hyperslab::from_raw(vec![RawSlice::new(0, 2, Some(1), 3)].into()), - "Unsupported block/step for axis 0: 3/2" + "Blocks can not overlap (axis: 0)" ); } @@ -1134,48 +1345,51 @@ mod test { #[test] fn test_hyperslab_into_from_raw() -> Result<()> { + use std::convert::TryInto; fn check( shape: S, hyper: H, exp_raw_hyper: RH, exp_raw_sel: Option, exp_hyper2: H2, exp_sel2: Option, - ) -> Result<()> - where + ) where S: AsRef<[Ix]>, - H: Into, + H: TryInto, + H::Error: std::fmt::Debug, RH: Into, RS: Into, - H2: Into, - S2: Into, + H2: TryInto, + H2::Error: std::fmt::Debug, + S2: TryInto, + S2::Error: std::fmt::Debug, { let shape = shape.as_ref(); - let hyper = hyper.into(); + let hyper = hyper.try_into().unwrap(); let exp_raw_hyper = exp_raw_hyper.into(); let exp_raw_sel = exp_raw_sel.map(Into::into).unwrap_or(exp_raw_hyper.clone().into()); - let exp_hyper2 = exp_hyper2.into(); - let exp_sel2 = exp_sel2.map(Into::into).unwrap_or(exp_hyper2.clone().into()); + let exp_hyper2 = exp_hyper2.try_into().unwrap(); + let exp_sel2 = exp_sel2 + .map(|x| TryInto::try_into(x).unwrap()) + .unwrap_or(exp_hyper2.clone().try_into().unwrap()); - let raw_hyper = hyper.clone().into_raw(shape)?; + let raw_hyper = hyper.clone().into_raw(shape).unwrap(); assert_eq!(raw_hyper, exp_raw_hyper); let sel = Selection::new(hyper.clone()); - let raw_sel = sel.clone().into_raw(shape)?; + let raw_sel = sel.clone().into_raw(shape).unwrap(); assert_eq!(raw_sel, exp_raw_sel); - let hyper2 = Hyperslab::from_raw(raw_hyper.clone())?; + let hyper2 = Hyperslab::from_raw(raw_hyper.clone()).unwrap(); assert_eq!(hyper2, exp_hyper2); - let sel2 = Selection::from_raw(raw_sel.clone())?; + let sel2 = Selection::from_raw(raw_sel.clone()).unwrap(); assert_eq!(sel2, exp_sel2); - let raw_hyper2 = hyper2.clone().into_raw(shape)?; + let raw_hyper2 = hyper2.clone().into_raw(shape).unwrap(); assert_eq!(raw_hyper2, raw_hyper); - let raw_sel2 = sel2.clone().into_raw(shape)?; + let raw_sel2 = sel2.clone().into_raw(shape).unwrap(); assert_eq!(raw_sel2, raw_sel); - - Ok(()) } - check(&[], (), vec![], Some(RawSelection::All), (), Some(Selection::All))?; + check(&[], (), vec![], Some(RawSelection::All), (), Some(Selection::All)); check( &[5, 5, 5], @@ -1184,7 +1398,7 @@ mod test { Some(RawSelection::All), s![..5, ..5, ..5], Some(Selection::All), - )?; + ); check( &[0; 6], @@ -1200,61 +1414,22 @@ mod test { Some(RawSelection::None), s![..0, ..0, ..0, ..0, ..0, ..0;2], Some(Selection::new(&[])), - )?; - - check( - &[3; 10], - s![.., ..;2, 1.., 1..;2, -3..=1, -3..=-1;2, ..=-1, ..=-1;3, 0..-1, 2..=-1], - vec![ - RawSlice::new(0, 1, Some(3), 1), - RawSlice::new(0, 2, Some(2), 1), - RawSlice::new(1, 1, Some(2), 1), - RawSlice::new(1, 2, Some(1), 1), - RawSlice::new(0, 1, Some(2), 1), - RawSlice::new(0, 2, Some(2), 1), - RawSlice::new(0, 1, Some(3), 1), - RawSlice::new(0, 3, Some(1), 1), - RawSlice::new(0, 1, Some(2), 1), - RawSlice::new(2, 1, Some(1), 1), - ], - None as Option, - s![..3, ..3;2, 1..3, 1..2;2, ..=1, ..3;2, ..3, ..1;3, 0..2, 2..3], - None as Option, - )?; - - check( - &[10; 4], - s![-5.., -10, 1..-1;2, 1], - vec![ - RawSlice::new(5, 1, Some(5), 1), - RawSlice::new(0, 1, Some(1), 1), - RawSlice::new(1, 2, Some(4), 1), - RawSlice::new(1, 1, Some(1), 1), - ], - None as Option, - s![5..10, 0..1, 1..8;2, 1..2], - None as Option, - )?; + ); - check( - &[10; 3], - Hyperslab::new(s![-5..;2, -10, 1..-3;3]) - .set_unlimited(0)? - .set_block(0)? - .set_block(2)?, - vec![ - RawSlice::new(5, 2, None, 2), - RawSlice::new(0, 1, Some(1), 1), - RawSlice::new(1, 3, Some(2), 3), - ], - None as Option, - Hyperslab::new(s![5..;2, 0..1, 1..7;3]).set_unlimited(0)?.set_block(0)?.set_block(2)?, - None as Option, - )?; + assert!(Hyperslab::try_new( + s![.., ..;2, 1.., 1..;2, -3..=1, -3..=-1;2, ..=-1, ..=-1;3, 0..-1, 2..=-1] + ) + .is_err()); + assert!(Hyperslab::try_new( + s![.., ..;2, 1.., 1..;2, -3..=1, -3..=-1;2, ..=-1, ..=-1;3, 0..-1, 2..=-1] + ) + .is_err()); + assert!(Hyperslab::try_new(s![-5.., -10, 1..-1;2, 1],).is_err()); + assert!(Hyperslab::try_new(s![5..10, 0..1, 1..8;2, 1..2],).is_ok()); check( &[7; 7], - Hyperslab::new(s![1..2;3, 1..3;3, 1..4;3, 1..5;3, 1..6;3, 1..7;3, ..7;3]), + Hyperslab::try_new(s![1..2;3, 1..3;3, 1..4;3, 1..5;3, 1..6;3, 1..7;3, ..7;3])?, vec![ RawSlice::new(1, 3, Some(1), 1), RawSlice::new(1, 3, Some(1), 1), @@ -1265,42 +1440,24 @@ mod test { RawSlice::new(0, 3, Some(3), 1), ], None as Option, - Hyperslab::new(s![1..2;3, 1..2;3, 1..2;3, 1..5;3, 1..5;3, 1..5;3, ..7;3]), - None as Option, - )?; - - check( - &[7; 4], - Hyperslab::new(s![1..4;3, 1..5;3, 1..6;3, 1..7;3]) - .set_block(0)? - .set_block(1)? - .set_block(2)? - .set_block(3)?, - vec![ - RawSlice::new(1, 3, Some(1), 3), - RawSlice::new(1, 3, Some(1), 3), - RawSlice::new(1, 3, Some(1), 3), - RawSlice::new(1, 3, Some(2), 3), - ], - None as Option, - Hyperslab::new(s![1..4;3, 1..4;3, 1..4;3, 1..7;3]) - .set_block(0)? - .set_block(1)? - .set_block(2)? - .set_block(3)?, + Hyperslab::try_new(s![1..2;3, 1..2;3, 1..2;3, 1..5;3, 1..5;3, 1..5;3, ..7;3])?, None as Option, - )?; + ); Ok(()) } #[test] fn test_in_out_shape_ndim() -> Result<()> { - fn check, E: AsRef<[Ix]>>( + use std::convert::TryInto; + fn check, E: AsRef<[Ix]>>( sel: S, exp_in_ndim: Option, exp_out_shape: E, exp_out_ndim: Option, - ) -> Result<()> { + ) -> Result<()> + where + S::Error: std::fmt::Debug, + { let in_shape = [7, 8]; - let sel = sel.into(); + let sel = sel.try_into().unwrap(); assert_eq!(sel.in_ndim(), exp_in_ndim); let out_shape = sel.out_shape(&in_shape)?; let out_ndim = sel.out_ndim(); @@ -1322,10 +1479,10 @@ mod test { check(s![1, 2..;2], Some(2), [3], Some(1))?; check(s![1..;3, 2], Some(2), [2], Some(1))?; check(s![1..;3, 2..;2], Some(2), [2, 3], Some(2))?; - check(Hyperslab::new(s![1, 2..;2]).set_block(1)?, Some(2), [6], Some(1))?; - check(Hyperslab::new(s![1..;3, 2]).set_block(0)?, Some(2), [6], Some(1))?; + check(Hyperslab::try_new(s![1, 2..;2])?.set_block(1, 6)?, Some(2), [6], Some(1))?; + check(Hyperslab::try_new(s![1..;3, 2])?.set_block(0, 6)?, Some(2), [6], Some(1))?; check( - Hyperslab::new(s![1..;3, 2..;2]).set_block(0)?.set_block(1)?, + Hyperslab::try_new(s![1..;3, 2..;2])?.set_block(0, 6)?.set_block(1, 6)?, Some(2), [6, 6], Some(2), @@ -1345,19 +1502,22 @@ mod test { #[test] fn test_selection_into_from_raw() -> Result<()> { + use std::convert::TryInto; fn check( shape: Sh, sel: S, exp_raw_sel: RS, exp_sel2: Option, ) -> Result<()> where Sh: AsRef<[Ix]>, - S: Into, + S: TryInto, + S::Error: std::fmt::Debug, RS: Into, - S2: Into, + S2: TryInto, + S2::Error: std::fmt::Debug, { let shape = shape.as_ref(); - let sel = sel.into(); + let sel = sel.try_into().unwrap(); let exp_raw_sel = exp_raw_sel.into(); - let exp_sel2 = exp_sel2.map_or(sel.clone(), Into::into); + let exp_sel2 = exp_sel2.map_or(sel.clone(), |x| x.try_into().unwrap()); let raw_sel = sel.clone().into_raw(shape)?; assert_eq!(raw_sel, exp_raw_sel); @@ -1381,7 +1541,6 @@ mod test { None as Option, )?; check(&[5, 6], s![1..1;2, 3], RawSelection::None, Some(&[]))?; - check(&[5, 6], s![-5.., 0..], RawSelection::All, Some(..))?; check( &[5, 6], s![1..;2, 3], diff --git a/tests/test_plist.rs b/tests/test_plist.rs index fe2fa22ab..11cfc858f 100644 --- a/tests/test_plist.rs +++ b/tests/test_plist.rs @@ -785,8 +785,9 @@ fn test_dcpl_virtual_map() -> hdf5::Result<()> { let pl = DCB::new() .layout(Layout::Virtual) .virtual_map("foo", "bar", (3, 4..), (.., 1..), (10..=20, 10), (..3, 7..)) - .virtual_map("x", "y", 100, 91.., 12, Hyperslab::new(s![2..;3]).set_block(0)?) - .finish()?; + .virtual_map("x", "y", 100, 96.., 12, Hyperslab::try_new(s![2..;3])?) + .finish() + .unwrap(); let expected = vec![ VirtualMapping { src_filename: "foo".into(), @@ -800,9 +801,9 @@ fn test_dcpl_virtual_map() -> hdf5::Result<()> { src_filename: "x".into(), src_dataset: "y".into(), src_extents: 100.into(), - src_selection: (91..100).into(), + src_selection: (96..100).into(), vds_extents: 12.into(), - vds_selection: Hyperslab::new(s![2..11;3]).set_block(0)?.into(), + vds_selection: Hyperslab::try_new(s![2..12;3])?.into(), }, ]; assert_eq!(pl.get_virtual_map()?, expected);