|
1 | 1 | use std::convert::TryFrom; |
2 | 2 | use std::fmt; |
| 3 | +use std::io::{Read, Seek, SeekFrom}; |
3 | 4 |
|
4 | 5 | use ndarray::{s, Array1, Array2, ArrayD, IxDyn, SliceInfo}; |
5 | 6 | use rand::prelude::{Rng, SeedableRng, SmallRng}; |
6 | 7 |
|
7 | | -use hdf5_types::TypeDescriptor; |
| 8 | +use hdf5_types::{H5Type, TypeDescriptor}; |
8 | 9 |
|
9 | 10 | mod common; |
10 | 11 |
|
@@ -171,6 +172,85 @@ where |
171 | 172 | Ok(()) |
172 | 173 | } |
173 | 174 |
|
| 175 | +fn test_byte_read_seek_impl(ds: &hdf5::Dataset, arr: &ArrayD<u8>, ndim: usize) -> hdf5::Result<()> { |
| 176 | + let mut rng = SmallRng::seed_from_u64(42); |
| 177 | + ds.write(arr)?; |
| 178 | + |
| 179 | + // Read whole |
| 180 | + let reader = ds.as_byte_reader(); |
| 181 | + let mut reader = if ndim != 1 { |
| 182 | + assert!(reader.is_err()); |
| 183 | + return Ok(()); |
| 184 | + } else { |
| 185 | + reader.unwrap() |
| 186 | + }; |
| 187 | + let mut out_bytes = vec![0u8; arr.len()]; |
| 188 | + reader.read(&mut out_bytes.as_mut_slice()).expect("io::Read failed"); |
| 189 | + assert_eq!(out_bytes.as_slice(), arr.as_slice().unwrap()); |
| 190 | + |
| 191 | + // Read in chunks |
| 192 | + let mut reader = reader.clone(); |
| 193 | + reader.seek(std::io::SeekFrom::Start(0)).expect("io::Seek failed"); |
| 194 | + let mut pos = 0; |
| 195 | + while pos < arr.len() { |
| 196 | + let chunk_len: usize = rng.gen_range(1..arr.len() + 1); |
| 197 | + let mut chunk = vec![0u8; chunk_len]; |
| 198 | + let n_read = reader.read(&mut chunk).expect("io::Read failed"); |
| 199 | + if pos + chunk_len < arr.len() { |
| 200 | + // We did not read until end. Thus, the chunk should be fully filled. |
| 201 | + assert_eq!(chunk_len, n_read); |
| 202 | + } |
| 203 | + assert_eq!(&chunk[..n_read], arr.slice(s![pos..pos + n_read]).as_slice().unwrap()); |
| 204 | + pos += chunk_len; |
| 205 | + } |
| 206 | + |
| 207 | + // Seek to the begining and read again |
| 208 | + reader.seek(SeekFrom::Start(0)).expect("io::Seek failed"); |
| 209 | + let mut out_bytes = vec![0u8; arr.len()]; |
| 210 | + reader.read(&mut out_bytes.as_mut_slice()).expect("io::Read failed"); |
| 211 | + assert_eq!(out_bytes.as_slice(), arr.as_slice().unwrap()); |
| 212 | + |
| 213 | + // Seek to a random position from start |
| 214 | + let pos = rng.gen_range(0..arr.len() + 1) as u64; |
| 215 | + let seeked_pos = reader.seek(SeekFrom::Start(pos)).expect("io::Seek failed") as usize; |
| 216 | + let mut out_bytes = vec![0u8; arr.len() - seeked_pos]; |
| 217 | + reader.read(&mut out_bytes.as_mut_slice()).expect("io::Read failed"); |
| 218 | + assert_eq!(out_bytes.as_slice(), arr.slice(s![seeked_pos..]).as_slice().unwrap()); |
| 219 | + |
| 220 | + // Seek from current position |
| 221 | + let orig_pos = reader.seek(SeekFrom::Start(pos)).expect("io::Seek failed") as i64; |
| 222 | + let rel_pos = rng.gen_range(-(arr.len() as i64)..arr.len() as i64 + 1); |
| 223 | + let pos_res = reader.seek(SeekFrom::Current(rel_pos)); |
| 224 | + if (rel_pos + orig_pos) < 0 { |
| 225 | + assert!(pos_res.is_err()) // We cannot seek before start |
| 226 | + } else { |
| 227 | + let seeked_pos = pos_res.unwrap() as usize; |
| 228 | + assert_eq!(rel_pos + orig_pos, seeked_pos as i64); |
| 229 | + let mut out_bytes = vec![0u8; arr.len() - seeked_pos]; |
| 230 | + reader.read(&mut out_bytes.as_mut_slice()).expect("io::Read failed"); |
| 231 | + assert_eq!(out_bytes.as_slice(), arr.slice(s![seeked_pos..]).as_slice().unwrap()); |
| 232 | + } |
| 233 | + |
| 234 | + // Seek to a random position from end |
| 235 | + let pos = -(rng.gen_range(0..arr.len() + 1) as i64); |
| 236 | + let seeked_pos = reader.seek(SeekFrom::End(pos)).expect("io::Seek failed") as usize; |
| 237 | + assert_eq!(pos, seeked_pos as i64 - arr.len() as i64); |
| 238 | + let mut out_bytes = vec![0u8; arr.len() - seeked_pos]; |
| 239 | + reader.read(&mut out_bytes.as_mut_slice()).expect("io::Read failed"); |
| 240 | + assert_eq!(out_bytes.as_slice(), arr.slice(s![seeked_pos..]).as_slice().unwrap()); |
| 241 | + |
| 242 | + // Seek before start |
| 243 | + assert!(reader.seek(SeekFrom::End(-(arr.len() as i64) - 1)).is_err()); |
| 244 | + |
| 245 | + // Test stream position start |
| 246 | + // Requires Rust 1.55.0: reader.rewind().expect("io::Seek::rewind failed"); |
| 247 | + assert_eq!(0, reader.seek(SeekFrom::Start(0)).unwrap()); |
| 248 | + assert_eq!(0, reader.stream_position().unwrap()); |
| 249 | + assert_eq!(0, reader.seek(SeekFrom::End(-(arr.len() as i64))).unwrap()); |
| 250 | + assert_eq!(0, reader.stream_position().unwrap()); |
| 251 | + Ok(()) |
| 252 | +} |
| 253 | + |
174 | 254 | fn test_read_write<T>() -> hdf5::Result<()> |
175 | 255 | where |
176 | 256 | T: hdf5::H5Type + fmt::Debug + PartialEq + Gen + Clone, |
@@ -278,3 +358,24 @@ fn test_read_write_rename_fields() -> hdf5::Result<()> { |
278 | 358 | test_read_write::<RenameEnum>()?; |
279 | 359 | Ok(()) |
280 | 360 | } |
| 361 | + |
| 362 | +#[test] |
| 363 | +fn test_byte_read_seek() -> hdf5::Result<()> { |
| 364 | + let mut rng = SmallRng::seed_from_u64(42); |
| 365 | + let file = new_in_memory_file()?; |
| 366 | + |
| 367 | + for ndim in 0..=2 { |
| 368 | + for _ in 0..=20 { |
| 369 | + let arr: ArrayD<u8> = gen_arr(&mut rng, ndim); |
| 370 | + |
| 371 | + let ds: hdf5::Dataset = file.new_dataset::<u8>().shape(arr.shape()).create("x")?; |
| 372 | + let ds = scopeguard::guard(ds, |ds| { |
| 373 | + drop(ds); |
| 374 | + drop(file.unlink("x")); |
| 375 | + }); |
| 376 | + |
| 377 | + test_byte_read_seek_impl(&ds, &arr, ndim)?; |
| 378 | + } |
| 379 | + } |
| 380 | + Ok(()) |
| 381 | +} |
0 commit comments