A compression code path that silently discarded every value handed to it, behind a comment conceding “actual dataset.write() calls would go here.” A dataspace parser that misread a four-byte header as three, and got away with it because its own test fixture was built to match the bug. SciRS2 0.6.2 found both, fixed both, and removed the C library that made the first one hard to see.
Today we released SciRS2 0.6.2 — a Pure Rust hardening release that retires the C-linked hdf5 crate in favor of a real pure-Rust backend, closes a critical silent-data-loss bug that shipped inside the code path it replaces, and drops libnuma, tracy-client, and opencl3 from the dependency graph entirely. A breaking TLS-provider change closes out the cycle.
No C. No Fortran. No CUDA Toolkit, no system BLAS, and — as of this release — no system libhdf5 either. SciRS2 0.6.2 compiles to a single static binary (or WASM) and runs everywhere, with four fewer native libraries standing between cargo build and a working binary than it had a week ago.
Why SciRS2 0.6.2 is a game changer
C dependencies in a Rust scientific stack don’t just cost you a system package install — they cost you visibility. The failure modes this release fixes were all hiding behind exactly that opacity:
- A “native” compression code path that creates the dataset, then never calls
write()on it — because the real work was in a different, uninvoked branch. - A binary format parser that misreads a header field by one byte, silently, because nothing forced it to fail loudly.
libnuma’s FFI surface (eightextern "C"functions) standing in for eleven lines of/sysparsing that could have been pure Rust from the start.- A profiler that requires linking a C++ client just to record a span and export a trace file.
- An OpenCL binding that links
-lOpenCLat build time even though OpenCL itself is a runtime-loadable ICD by design.
SciRS2 0.6.2 ends all of that, concretely:
- Pure-Rust HDF5, replacing two separate C/legacy code paths at once. The C-linked
hdf5crate and the in-treehdf5_litereader (2,697 lines) are both gone, superseded byoxih5everywherescirs2-iotouches HDF5 — including MAT v7.3 support, which no longer needs thehdf5Cargo feature or a systemlibhdf5to work at all. - A critical silent-data-loss fix.
EnhancedHDF5File::create_dataset_with_compressiondiscarded every value it was handed whenever thehdf5feature was enabled — the per-type match arms were no-ops under a comment admitting the real write call didn’t exist yet. Fixed, with a round-trip test now asserting the values actually come back. - A self-concealing parser bug, closed. The old
hdf5_litereader parsed the version-2 Dataspace message header as three bytes instead of the spec-mandated four, mistaking theflagsbyte for thetypebyte that follows it. Every v2-dataspace dimension it produced was read one byte early — and the module’s own synthetic test fixture had been hand-built to match the buggy layout, so the suite passed anyway. - Three more C/C++ dependencies removed.
libnuma(Linux NUMA topology, now real/sysparsing),tracy-client(C++ profiler, now a pure-Rust Chrome-Trace-Event exporter), andopencl3(now a runtimedlopenloader instead of a build-time link) are all gone from the default dependency graph. - Honest NUMA topology, in two places at once.
scirs2-spatial’sNumaTopology::detect()had the identical fabricated-data problem as the oldlibnumaestimate — hardcoded “one node per 8 CPUs, 1GB per node” regardless of the real machine. Both are now real measurements:/sys/devices/system/node/node count, per-node CPU lists and memory, and the firmware ACPI SLIT distance matrix.
Technical Deep Dive: what “pure Rust” actually removes
The compression bug, precisely. create_dataset_with_compression had two branches: a “native” one that created the HDF5 dataset object and then matched on the value’s type with arms like let _data_size = array.len(); — computing a size, discarding the array, writing nothing — and a fallback create_fallback_dataset that was the only branch that actually stored anything. Because the native branch returned early, every caller that reached it (write_datasets_parallel, write_hdf5_enhanced) silently lost data while reporting fabricated compression statistics for the values it had just dropped. The fix deletes the native branch outright — the storing path is now the only path.
Why the dataspace bug survived. HDF5’s version-2 Dataspace message format specifies a type byte immediately after the flags byte in its header. The old parser read flags and then treated the next field as if type didn’t exist, shifting every subsequent dimension read one byte early. Bugs like this are usually caught by round-tripping against real files — but the module’s own test fixture was constructed by hand to match the parser’s expectations, not the HDF5 specification, so the bug and its test formed a closed, self-consistent loop that never touched reality. oxih5 parses the header correctly; the fixture — preserved as scirs2-io/tests/hdf5_conformance.rs — was corrected to emit spec-conformant bytes, with a regression test now pinning that the old truncated form is rejected.
The TLS change, and why it’s automatic. reqwest and ureq now build with rustls-no-provider instead of pulling in the default aws-lc-rs (C) backend — but that would normally push a manual CryptoProvider::install_default(...) call onto every downstream application. SciRS2 avoids that by installing a pure-Rust oxitls-rustcrypto-provider as the process-default on first use of its own networking code, and only if the process doesn’t already have one installed. It’s also preferred over the abandoned upstream rustls-rustcrypto 0.0.2-alpha because it fixes RUSTSEC-2026-0104, an unpatched CRL-parsing panic in rustls-webpki.
One exception, deliberately kept. scirs2-core’s mpsgraph feature (Apple MPSGraph GPU acceleration, macOS-only, off by default) still compiles a small Objective-C wrapper via the cc crate. There is no pure-Rust binding path to that framework yet, and the feature stays opt-in — everything else in this release moved off C entirely.
Getting Started
Two of 0.6.2’s headline changes, both usable without any extra Cargo feature now:
cargo add scirs2-io scirs2-core --features scirs2-core/tracy
use scirs2_core::profiling::tracy::TracyClient;
fn main() {
// Pure-Rust Tracy-compatible profiling -- no C++ client linked,
// even with the `tracy` feature enabled.
let client = TracyClient::new();
if client.is_active() {
client.message("profiling enabled");
}
{
let _span = client.span("my_operation");
// work here
} // span ends on drop
// Export to Chrome Trace Event Format -- open in ui.perfetto.dev
// or chrome://tracing. Valid even with the `tracy` feature off,
// where it writes an empty-but-valid trace document.
let _ = client.export_chrome_trace(std::env::temp_dir().join("trace.json"));
}
use scirs2_io::matlab::enhanced::{EnhancedMatFile, MatFileConfig};
use scirs2_io::matlab::MatType;
use scirs2_core::ndarray::{ArrayD, IxDyn};
use std::collections::HashMap;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// use_v73 no longer needs the `hdf5` feature or a system libhdf5 --
// oxih5 backs it in default builds as of 0.6.2.
let mat = EnhancedMatFile::new(MatFileConfig {
use_v73: true,
..Default::default()
});
let data = ArrayD::from_shape_vec(IxDyn(&[2, 2]), vec![1.0, 2.0, 3.0, 4.0])?;
let mut vars = HashMap::new();
vars.insert("data".to_string(), MatType::Double(data));
let path = std::env::temp_dir().join("example.mat");
mat.write(&path, &vars)?;
let loaded = mat.read(&path)?;
assert!(loaded.contains_key("data"));
Ok(())
}
What’s New in 0.6.2
Added
- MAT v7.3 (HDF5-based) support in
scirs2-iois now available in default builds —EnhancedMatFile,V73MatFile, andPartialIoSupportno longer require thehdf5Cargo feature or a systemlibhdf5. All 45 feature gates acrossmatlab::enhancedandmatlab::v73_enhancedare gone. scirs2-core’sTracyClientgainsexport_chrome_trace(path)— a pure-Rust Chrome Trace Event Format exporter, viewable atui.perfetto.devorchrome://tracing.
Removed
- The dead
libhdf5-backed paths inhdf5::enhanced(~250 lines), including the actively-destructive compression path described above. - Breaking: the
hdf5_litemodule (2,697 lines) — superseded entirely byoxih5.Hdf5Reader,Hdf5Attribute,Hdf5DataType,Hdf5Dataset,Hdf5Group,Hdf5Node,Hdf5NodeType, andHdf5Valueare gone; direct users move tooxih5::File. libnuma,tracy-client, andopencl3— all three fully removed from the dependency graph, with OpenCL now runtime-dlopen-loaded vialibloadinginstead of link-time linked.
Changed
- Breaking (TLS):
reqwest/ureqnow build withrustls-no-provider; SciRS2 auto-installs the pure-Rustoxitls-rustcrypto-provideron first networking use unless the application already installed its own. universal_reader’s HDF5 path widens format coverage underoxih5: superblock v2/v3, version-2 B-trees, fractal heaps, new-style link-message groups, extensible/fixed array chunk indices, virtual datasets, and szip compression all now read wherehdf5_litereported a format error. Variable-length strings decode to realDataColumn::Stringinstead of raw bytes.- Breaking:
EnhancedHDF5File::get_compression_statsnow returnsResult<CompressionStats>with genuinely measured figures, instead of a hard-codedcompression_ratio: 1.0it never actually computed. PartialIoSupport::write_array_sliceperforms a true in-place overwrite viaoxih5::write_dataset_in_place_f64, soMATLAB_classattributes, object references, and cell arrays all survive a partial write — rebuilding the whole file, the previous approach, would have destroyed all of them.scirs2-ndimage’s CUDA kernel JIT now compiles through the pure-Rustoxicuda-nvrtccrate (runtimedlopen, zero build-time CUDA SDK dependency).- Dependency bumps:
oxih5/oxih5-core0.1.3→0.2.2,oxisql-*0.3.2→0.4.0,oxiz0.2.3→0.2.4,oxicuda-*0.5.0→0.5.1 (+ newoxicuda-nvrtc), Cranelift 0.133→0.134 (fixes amemmap2unsound advisory).
Fixed
- The critical silent-data-loss bug in
create_dataset_with_compression, described above. - Writing
MatType::SparseLogicalto MAT v7.3 stored all zeros —boolvalues were converted viaformat!("{:?}", x).parse::<f64>(), and"true"doesn’t parse as a float. EnhancedMatFile::read_sparse_from_hdf5returned every non-zero value asT::default()under a comment calling it a placeholder.- Malformed sparse CSC→COO conversions in
matlab::enhancednow reportIoError::FormatErrorinstead of panicking on a truncated group. - The self-concealing version-2 Dataspace header bug, described above.
scirs2-spatial’sNumaTopology::detect()fabricated topology instead of measuring it — now reads real Linuxsysfsdata with an honest single-node fallback elsewhere.
See CHANGELOG.md [0.6.2] for the complete list.
Tips
- Drop the
hdf5feature flag from yourCargo.toml. MAT v7.3 and general HDF5 support work in default builds now — if you were enablinghdf5just to unlockEnhancedMatFile/V73MatFile, that feature gate no longer exists to enable. - If you use
scirs2_io::hdf5_litedirectly, migrate now. The module is deleted, not deprecated. Move tooxih5::File, which is a strict superset of whathdf5_litecould read — including files that used to fail with a format error. - Re-check any
get_compression_stats()call sites. The return type changed toResult<CompressionStats>, and the numbers are now real measurements — a ratio below1.0is an expected, honest answer foroxih5’s uncompressed writer output, not a regression. - If your process installs a custom
rustls::CryptoProvider, install it before first use of SciRS2 networking. SciRS2 only installs its own pure-Rust provider when nothing has claimed the process-default slot yet — an existing default is never overridden. - Re-run anything that depends on
NumaTopology::detect()output. Both thescirs2-core/libnumapath andscirs2-spatial’s independent implementation now report real topology instead of an estimate — code that special-cased the old “8 CPUs per node” heuristic should be re-verified against actual hardware. mpsgraphis still the one opt-in exception. If you’re on macOS and want Apple MPSGraph acceleration, that feature still compiles a small Objective-C wrapper — everything else inscirs2-core’s default build is C-free.
This is the foundation
SciRS2 0.6.2 is the sovereign scientific-computing layer of the COOLJAPAN ecosystem — and a release that removes silent data loss and four C dependencies in the same cycle matters precisely because so much builds on top of it:
- NumRS2 — NumPy-compatible N-dimensional arrays in pure Rust.
- PandRS — Pandas-compatible DataFrames.
- OptiRS — advanced ML optimizers extending SciRS2.
- ToRSh — a PyTorch-compatible deep-learning framework.
- SkleaRS — a scikit-learn-compatible ML library.
- TrustformeRS — Hugging Face Transformers in pure Rust.
Every one of these inherits SciRS2’s I/O layer along with everything else — a compressed HDF5 write that used to silently drop data is one less place a downstream training pipeline could lose a checkpoint without ever knowing it. Storage runs on OxiH5; the numeric core still rests on OxiBLAS and OxiFFT; verification on OxiZ; the NVIDIA performance path on OxiCUDA; TLS on OxiTLS. No C. No Fortran. No exceptions in the default build.
Repository: https://github.com/cool-japan/scirs
Star the repo if you’d rather a compression bug fail loudly at compile time than silently at runtime.
The era of “it probably wrote the file” is over. Pure Rust scientific computing is here — fast, safe, and honest about what actually made it to disk.
— KitaSan at COOLJAPAN OÜ July 22, 2026