Skip to content

Commit 1c7d7eb

Browse files
committed
address some issues with the slice API
1 parent a64c141 commit 1c7d7eb

File tree

2 files changed

+90
-14
lines changed

2 files changed

+90
-14
lines changed

hdf5-rs/src/hl/container.rs

Lines changed: 70 additions & 12 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);
@@ -251,11 +263,6 @@ impl<'a> Writer<'a> {
251263
ensure!(!self.obj.is_attr(), "slicing cannot be used on attribute datasets");
252264

253265
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-
259266
let slice_s: &[SliceOrIndex] = slice.as_ref();
260267
let slice_dim = slice_s.len();
261268
if shape.ndim() != slice_dim {
@@ -490,18 +497,54 @@ impl Container {
490497
self.as_reader().read_1d()
491498
}
492499

500+
/// Reads the given `slice` of the dataset into a 1-dimensional array.
501+
///
502+
/// The dataset must be 1-dimensional.
503+
pub fn read_slice_1d<T, S>(&self, slice: &SliceInfo<S, Ix1>) -> Result<Array1<T>>
504+
where
505+
T: H5Type,
506+
S: AsRef<[SliceOrIndex]>,
507+
{
508+
self.as_reader().read_slice_1d(slice)
509+
}
510+
493511
/// Reads a dataset/attribute into a 2-dimensional array.
494512
///
495513
/// The dataset/attribute must be 2-dimensional.
496514
pub fn read_2d<T: H5Type>(&self) -> Result<Array2<T>> {
497515
self.as_reader().read_2d()
498516
}
499517

518+
/// Reads the given `slice` of the dataset into a 2-dimensional array.
519+
///
520+
/// The dataset must be 2-dimensional.
521+
pub fn read_slice_2d<T, S>(&self, slice: &SliceInfo<S, Ix2>) -> Result<Array2<T>>
522+
where
523+
T: H5Type,
524+
S: AsRef<[SliceOrIndex]>,
525+
{
526+
self.as_reader().read_slice_2d(slice)
527+
}
528+
500529
/// Reads a dataset/attribute into an array with dynamic number of dimensions.
501530
pub fn read_dyn<T: H5Type>(&self) -> Result<ArrayD<T>> {
502531
self.as_reader().read_dyn()
503532
}
504533

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

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

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)