Skip to content

Commit fdd751a

Browse files
authored
Merge pull request #27 from pmarks/pmarks/fix-slices
Some fixes for slice API
2 parents a64c141 + afad564 commit fdd751a

File tree

3 files changed

+95
-24
lines changed

3 files changed

+95
-24
lines changed

hdf5-rs/src/hl/container.rs

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,9 @@ impl<'a> Reader<'a> {
5656
}
5757

5858
/// Reads a slice of an n-dimensional array.
59-
/// If the array has a fixed number of dimensions, it must match the dimensionality of
60-
/// dataset. Use the multi-dimensional slice macro `s![]` from `ndarray` to conveniently create
59+
/// If the dimensionality `D` has a fixed number of dimensions, it must match the dimensionality of
60+
/// the slice, after singleton dimensions are dropped.
61+
/// Use the multi-dimensional slice macro `s![]` from `ndarray` to conveniently create
6162
/// a multidimensional slice.
6263
pub fn read_slice<T, S, D>(&self, slice: &SliceInfo<S, D>) -> Result<Array<T, D>>
6364
where
@@ -68,10 +69,6 @@ impl<'a> Reader<'a> {
6869
ensure!(!self.obj.is_attr(), "slicing cannot be used on attribute datasets");
6970

7071
let shape = self.obj.get_shape()?;
71-
if let Some(ndim) = D::NDIM {
72-
let obj_ndim = shape.ndim();
73-
ensure!(obj_ndim == ndim, "ndim mismatch: expected {}, got {}", ndim, obj_ndim);
74-
}
7572

7673
let slice_s: &[SliceOrIndex] = slice.as_ref();
7774
let slice_dim = slice_s.len();
@@ -86,7 +83,14 @@ impl<'a> Reader<'a> {
8683
}
8784

8885
if shape.ndim() == 0 {
89-
// Fall back to a simple read for the scalar case, slicing has no effect
86+
// Check that return dimensionality is 0.
87+
if let Some(ndim) = D::NDIM {
88+
let obj_ndim = 0;
89+
ensure!(obj_ndim == ndim, "ndim mismatch: slice outputs dims {}, output type dims {}", obj_ndim, ndim);
90+
}
91+
92+
// Fall back to a simple read for the scalar case
93+
// Slicing has no effect
9094
self.read()
9195
} else {
9296
let fspace = self.obj.space()?;
@@ -102,6 +106,14 @@ impl<'a> Reader<'a> {
102106
})
103107
.collect();
104108

109+
// *Output* dimensionality must match the reduced shape,
110+
// (i.e. dimensionality after singleton 'SliceOrIndex::Index'
111+
// axes are dropped.
112+
if let Some(ndim) = D::NDIM {
113+
let obj_ndim = reduced_shape.len();
114+
ensure!(obj_ndim == ndim, "ndim mismatch: slice outputs dims {}, output type dims {}", obj_ndim, ndim);
115+
}
116+
105117
let mspace = Dataspace::try_new(&out_shape, false)?;
106118
let size = out_shape.iter().product();
107119
let mut vec = Vec::with_capacity(size);
@@ -149,9 +161,8 @@ impl<'a> Reader<'a> {
149161
}
150162

151163
/// Reads the given `slice` of the dataset into a 1-dimensional array.
152-
///
153-
/// The dataset must be 1-dimensional.
154-
pub fn read_slice_1d<T, S>(&self, slice: &SliceInfo<S, Ix1>) -> Result<Array1<T>>
164+
/// The slice must yield a 1-dimensional result.
165+
pub fn read_slice_1d<T, S>(&self, slice: &SliceInfo<S, ndarray::Ix1>) -> Result<Array1<T>>
155166
where
156167
T: H5Type,
157168
S: AsRef<[SliceOrIndex]>,
@@ -166,10 +177,10 @@ impl<'a> Reader<'a> {
166177
self.read()
167178
}
168179

180+
169181
/// Reads the given `slice` of the dataset into a 2-dimensional array.
170-
///
171-
/// The dataset must be 2-dimensional.
172-
pub fn read_slice_2d<T, S>(&self, slice: &SliceInfo<S, Ix2>) -> Result<Array2<T>>
182+
/// The slice must yield a 2-dimensional result.
183+
pub fn read_slice_2d<T, S>(&self, slice: &SliceInfo<S, ndarray::Ix2>) -> Result<Array2<T>>
173184
where
174185
T: H5Type,
175186
S: AsRef<[SliceOrIndex]>,
@@ -251,11 +262,6 @@ impl<'a> Writer<'a> {
251262
ensure!(!self.obj.is_attr(), "slicing cannot be used on attribute datasets");
252263

253264
let shape = self.obj.get_shape()?;
254-
if let Some(ndim) = D::NDIM {
255-
let obj_ndim = shape.ndim();
256-
ensure!(obj_ndim == ndim, "ndim mismatch: expected {}, got {}", ndim, obj_ndim);
257-
}
258-
259265
let slice_s: &[SliceOrIndex] = slice.as_ref();
260266
let slice_dim = slice_s.len();
261267
if shape.ndim() != slice_dim {
@@ -490,18 +496,52 @@ impl Container {
490496
self.as_reader().read_1d()
491497
}
492498

499+
/// Reads the given `slice` of the dataset into a 1-dimensional array.
500+
/// The slice must yield a 1-dimensional result.
501+
pub fn read_slice_1d<T, S>(&self, slice: &SliceInfo<S, ndarray::Ix1>) -> Result<Array1<T>>
502+
where
503+
T: H5Type,
504+
S: AsRef<[SliceOrIndex]>,
505+
{
506+
self.as_reader().read_slice_1d(slice)
507+
}
508+
493509
/// Reads a dataset/attribute into a 2-dimensional array.
494510
///
495511
/// The dataset/attribute must be 2-dimensional.
496512
pub fn read_2d<T: H5Type>(&self) -> Result<Array2<T>> {
497513
self.as_reader().read_2d()
498514
}
499515

516+
/// Reads the given `slice` of the dataset into a 1-dimensional array.
517+
/// The slice must yield a 2-dimensional result.
518+
pub fn read_slice_2d<T, S>(&self, slice: &SliceInfo<S, ndarray::Ix2>) -> Result<Array2<T>>
519+
where
520+
T: H5Type,
521+
S: AsRef<[SliceOrIndex]>,
522+
{
523+
self.as_reader().read_slice_2d(slice)
524+
}
525+
500526
/// Reads a dataset/attribute into an array with dynamic number of dimensions.
501527
pub fn read_dyn<T: H5Type>(&self) -> Result<ArrayD<T>> {
502528
self.as_reader().read_dyn()
503529
}
504530

531+
/// Reads a slice of an n-dimensional array.
532+
/// If the dimensionality `D` has a fixed number of dimensions, it must match the dimensionality of
533+
/// the slice, after singleton dimensions are dropped.
534+
/// Use the multi-dimensional slice macro `s![]` from `ndarray` to conveniently create
535+
/// a multidimensional slice.
536+
pub fn read_slice<T, S, D>(&self, slice: &SliceInfo<S, D>) -> Result<Array<T, D>>
537+
where
538+
T: H5Type,
539+
S: AsRef<[SliceOrIndex]>,
540+
D: ndarray::Dimension,
541+
{
542+
self.as_reader().read_slice(slice)
543+
}
544+
505545
/// Reads a scalar dataset/attribute.
506546
pub fn read_scalar<T: H5Type>(&self) -> Result<T> {
507547
self.as_reader().read_scalar()
@@ -533,6 +573,21 @@ impl Container {
533573
self.as_writer().write_raw(arr)
534574
}
535575

576+
/// Writes all data from the array `arr` into the given `slice` of the target dataset.
577+
/// The shape of `arr` must match the shape the set of elements included in the slice.
578+
/// If the array has a fixed number of dimensions, it must match the dimensionality of
579+
/// dataset. Use the multi-dimensional slice macro `s![]` from `ndarray` to conveniently create
580+
/// a multidimensional slice.
581+
pub fn write_slice<'b, A, T, S, D>(&self, arr: A, slice: &SliceInfo<S, D>) -> Result<()>
582+
where
583+
A: Into<ArrayView<'b, T, D>>,
584+
T: H5Type,
585+
S: AsRef<[SliceOrIndex]>,
586+
D: ndarray::Dimension,
587+
{
588+
self.as_writer().write_slice(arr, slice)
589+
}
590+
536591
/// Writes a scalar dataset/attribute.
537592
pub fn write_scalar<T: H5Type>(&self, val: &T) -> Result<()> {
538593
self.as_writer().write_scalar(val)

hdf5-rs/src/hl/space.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,8 @@ impl Dataspace {
5656
/// Select a slice (known as a 'hyperslab' in HDF5 terminology) of the Dataspace.
5757
/// Returns the shape of array that is capable of holding the resulting slice.
5858
/// Useful when you want to read a subset of a dataset.
59-
pub fn select_slice<T, D>(&self, slice: &SliceInfo<T, D>) -> Result<Vec<Ix>>
60-
where
61-
T: AsRef<[SliceOrIndex]>,
62-
D: ndarray::Dimension,
59+
pub fn select_slice<S>(&self, slice: S) -> Result<Vec<Ix>>
60+
where S: AsRef<[SliceOrIndex]>
6361
{
6462
let shape = self.dims();
6563
let ss: &[SliceOrIndex] = slice.as_ref();

hdf5-rs/tests/test_dataset.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::fmt;
22

3-
use ndarray::{ArrayD, IxDyn, SliceInfo};
3+
use ndarray::{s, Array1, Array2, ArrayD, IxDyn, SliceInfo};
44
use rand::prelude::{Rng, SeedableRng, SmallRng};
55

66
use hdf5_types::TypeDescriptor;
@@ -40,7 +40,7 @@ where
4040
}
4141

4242
fn test_read_slice<T, R>(
43-
rng: &mut R, ds: &h5::Dataset, arr: &ArrayD<T>, _ndim: usize,
43+
rng: &mut R, ds: &h5::Dataset, arr: &ArrayD<T>, ndim: usize,
4444
) -> h5::Result<()>
4545
where
4646
T: h5::H5Type + fmt::Debug + PartialEq + Gen,
@@ -81,6 +81,24 @@ where
8181
let bad_sliced_read: h5::Result<ArrayD<T>> = dsr.read_slice(&bad_slice);
8282
assert!(bad_sliced_read.is_err());
8383

84+
// Tests for dimension-dropping slices with static dimensionality.
85+
if ndim == 2 && shape[0] > 0 && shape[1] > 0 {
86+
let v: Array1<T> = dsr.read_slice_1d(s![0, ..])?;
87+
assert_eq!(shape[1], v.shape()[0]);
88+
89+
let v: Array1<T> = dsr.read_slice_1d(s![.., 0])?;
90+
assert_eq!(shape[0], v.shape()[0]);
91+
}
92+
93+
if ndim == 3 && shape[0] > 0 && shape[1] > 0 && shape[2] > 0 {
94+
let v: Array2<T> = dsr.read_slice_2d(s![0, .., ..])?;
95+
assert_eq!(shape[1], v.shape()[0]);
96+
assert_eq!(shape[2], v.shape()[1]);
97+
98+
let v: Array1<T> = dsr.read_slice_1d(s![0, 0, ..])?;
99+
assert_eq!(shape[2], v.shape()[0]);
100+
}
101+
84102
Ok(())
85103
}
86104

0 commit comments

Comments
 (0)