#![cfg(all(feature = "threadsafe", feature = "deflate"))]
use rust_hdf5::H5File;
const TEST_PYTHON: &str = "/Users/stevek/mamba/envs/bs2026.1/bin/python";
fn python() -> Option<&'static str> {
static PY: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
PY.get_or_init(|| {
let candidate =
std::env::var("RUST_HDF5_TEST_PYTHON").unwrap_or_else(|_| TEST_PYTHON.to_string());
if std::path::Path::new(&candidate).exists() {
Some(candidate)
} else {
eprintln!("skipping h5py cross-check: {candidate} not present");
None
}
})
.as_deref()
}
fn tmp(name: &str) -> std::path::PathBuf {
use std::sync::atomic::{AtomicU64, Ordering};
static COUNTER: AtomicU64 = AtomicU64::new(0);
let n = COUNTER.fetch_add(1, Ordering::Relaxed);
std::env::temp_dir().join(format!(
"rust_hdf5_par_{}_{}_{}.h5",
name,
std::process::id(),
n
))
}
fn value(k: usize, i: usize) -> i32 {
(k * 1_000_000 + i) as i32
}
#[test]
fn parallel_multi_dataset_compressed_write_roundtrips() {
const N: usize = 8; const LEN: usize = 4096; const CHUNK: usize = 256; const ITERS: usize = 20;
for iter in 0..ITERS {
let path = tmp(&format!("iter{iter}"));
{
let file = H5File::create(&path).unwrap();
let mut handles = Vec::with_capacity(N);
for k in 0..N {
let ds = file
.new_dataset::<i32>()
.shape([LEN])
.chunk(&[CHUNK])
.deflate(4)
.create(&format!("ds{k}"))
.unwrap();
handles.push((k, ds));
}
std::thread::scope(|s| {
for (k, ds) in handles {
s.spawn(move || {
let data: Vec<i32> = (0..LEN).map(|i| value(k, i)).collect();
ds.write_raw(&data)
.unwrap_or_else(|e| panic!("write_raw ds{k}: {e}"));
});
}
});
file.close().unwrap();
}
{
let file = H5File::open(&path).unwrap();
for k in 0..N {
let ds = file.dataset(&format!("ds{k}")).unwrap();
let got = ds.read_raw::<i32>().unwrap();
let want: Vec<i32> = (0..LEN).map(|i| value(k, i)).collect();
assert_eq!(
got.len(),
LEN,
"dataset ds{k} length mismatch on iter {iter}"
);
assert_eq!(got, want, "dataset ds{k} content mismatch on iter {iter}");
}
}
if iter == 0 {
if let Some(py) = python() {
let script = format!(
"import h5py, numpy as np\n\
f = h5py.File(r'{}', 'r')\n\
for k in range({N}):\n\
\x20 d = f['ds%d' % k][...]\n\
\x20 exp = np.arange({LEN}, dtype='i8') + k * 1000000\n\
\x20 assert d.shape == ({LEN},), (k, d.shape)\n\
\x20 assert (d.astype('i8') == exp).all(), (k, d[:8], exp[:8])\n\
f.close()\n",
path.display()
);
let status = std::process::Command::new(py)
.arg("-c")
.arg(&script)
.status()
.expect("failed to spawn python");
assert!(status.success(), "h5py cross-check failed for {path:?}");
}
}
std::fs::remove_file(&path).ok();
}
}