# slow5lib
[](https://crates.io/crates/slow5lib)
[](https://docs.rs/slow5lib)
[](https://github.com/Psy-Fer/slow5lib-rs/actions/workflows/ci.yml)
[](https://opensource.org/licenses/MIT)
[](https://blog.rust-lang.org/2025/05/15/Rust-1.87.0/)
> **This is an independent Rust re-implementation of [hasindu2008/slow5lib](https://github.com/hasindu2008/slow5lib). It is not a port, not a wrapper, and shares no code with the original C library. The C implementation is the authoritative specification for wire format and behaviour; this crate is designed to be wire-compatible with it.**
Read and write **SLOW5** (text) and **BLOW5** (binary) files containing Oxford Nanopore Technologies sequencing signal data. The library exposes sequential and indexed access patterns with optional parallel decompression via rayon.
**[API reference](https://docs.rs/slow5lib)**
## What are SLOW5 and BLOW5?
SLOW5 and BLOW5 are file formats for storing raw Oxford Nanopore sequencing signal data, developed by [Hasindu Gamaarachchi](https://github.com/hasindu2008) as a simpler, more performant alternative to FAST5/POD5.
- **SLOW5** is a tab-separated text format, human-readable and easy to process with standard tools.
- **BLOW5** is the binary equivalent: same information, significantly smaller files and faster I/O. Records can be block-compressed with zstd or zlib, and the raw signal is compressed with SVB-ZD (StreamVByte + delta + zigzag) or ex-zd (quantize-trailing-shift + zigzag-delta + patched exceptions).
Both formats store one record per sequencing read, with primary fields (`read_id`, `raw_signal`, calibration parameters) and optional auxiliary fields for per-read metadata.
## Installation
```toml
[dependencies]
slow5lib = "0.1"
# optional: parallel decompression via rayon
slow5lib = { version = "0.1", features = ["rayon"] }
```
## Quick start
### Reading a SLOW5 file
```rust,no_run
use slow5lib::{Slow5Reader, Result};
fn main() -> Result<()> {
let mut reader = Slow5Reader::open("reads.slow5")?;
for record in reader.records() {
let rec = record?;
println!("{}: {} samples", rec.read_id, rec.raw_signal.len());
}
Ok(())
}
```
### Reading a BLOW5 file
```rust,no_run
use slow5lib::{Slow5Reader, Result};
fn main() -> Result<()> {
let mut reader = Slow5Reader::open("reads.blow5")?;
for record in reader.records() {
let rec = record?;
// calibrated picoamperes: (raw + offset) * range / digitisation
let pa: Vec<f32> = rec.raw_signal.iter()
.map(|&s| (s as f64 + rec.offset) * rec.range / rec.digitisation)
.map(|v| v as f32)
.collect();
println!("{}: {} samples, first pA = {:.2}", rec.read_id, pa.len(), pa[0]);
}
Ok(())
}
```
### Parallel decompression
For high-throughput pipelines, disk I/O stays on one thread while decompression fans out across the rayon pool:
```rust,no_run
use slow5lib::{Slow5Reader, Result};
use rayon::prelude::*;
fn main() -> Result<()> {
let mut reader = Slow5Reader::open("reads.blow5")?;
reader.records_raw()
.par_bridge()
.map(|r| r?.decompress())
.for_each(|rec| {
let rec = rec.unwrap();
println!("{}: {} samples", rec.read_id, rec.raw_signal.len());
});
Ok(())
}
```
Or use the convenience wrapper with the `rayon` feature:
```rust,no_run
# #[cfg(feature = "rayon")]
use rayon::prelude::*;
# #[cfg(feature = "rayon")]
```rust,no_run
use slow5lib::{Slow5Writer, Record, Result};
use slow5lib::header::{Header, RecordCompression, SignalCompression, ReadGroup};
use slow5lib::aux::AuxMeta;
fn main() -> Result<()> {
let header = Header {
version: (0, 2, 0),
num_read_groups: 1,
record_compression: RecordCompression::Zstd,
signal_compression: SignalCompression::SvbZd,
read_groups: vec![ReadGroup::default()],
aux_meta: AuxMeta::default(),
};
let mut writer = Slow5Writer::create("out.blow5", header)?;
let record = Record {
read_id: "my-read-001".into(),
read_group: 0,
digitisation: 2048.0,
offset: -224.0,
range: 1463.0,
sampling_rate: 4000.0,
raw_signal: vec![512i16, 515, 510, 518, 511],
aux: Default::default(),
};
writer.write(&record)?;
writer.finish()?;
Ok(())
}
```
## Compression
| None | None | Uncompressed; byte-identical to C library |
| None | SVB-ZD | StreamVByte + delta + zigzag; byte-identical to C library |
| None | ex-zd | Quantize-shift + zigzag-delta + patched exceptions; byte-identical to C library |
| Zstd | SVB-ZD | Recommended default; smallest files, fastest random access |
| Zstd | ex-zd | Alternative signal codec; compression relative to SVB-ZD depends on the data |
| Zstd | None | |
| Zlib | SVB-ZD | Compatible with tools that do not support Zstd |
| Zlib | ex-zd | |
| Zlib | None | |
SVB-ZD and ex-zd signal compression use the [`svb`](https://crates.io/crates/svb) crate with SIMD-accelerated encode/decode (AVX2, SSSE3, NEON).
## Design notes
**No mmap.** The library uses `BufReader<File>` for sequential reads and `FileExt::read_at` (pread) for indexed random access. Memory-mapped I/O is deliberately avoided -- it is the main source of poor memory behaviour in C and Python implementations of similar formats.
**Two reader types reflect two access patterns.** `Slow5Reader` is a sequential cursor (not `Sync`). `Slow5IndexedReader` holds a `File` for `read_at` and is `Send + Sync` -- multiple threads can call `get()` concurrently with no locking.
**Decompression is separated from I/O.** `records_raw()` yields `RawRecord` values (compressed bytes, no CPU work). `RawRecord::decompress()` pays the CPU cost. Splitting the two lets rayon's `par_bridge()` decompress in parallel while disk reads stay sequential.
## Known limitations
- **Windows is not in CI.** The indexed reader uses Unix `read_at` (`FileExt::read_at`); a Windows equivalent (`seek_read`) has not been wired in. WSL and Git Bash both expose Unix `read_at` and are unaffected.
- **Parity testing covers one dataset.** Byte-identical and value-level parity is validated against a single author-supplied BLOW5 file and against `slow5tools` committed fixtures; it has not yet been validated across multiple organisms, sequencing platforms, or library preps.
## Validation
Wire-compatibility is validated two ways:
- `tests/parity.rs` compares Rust writer output against reference files committed to the repo, generated with `slow5tools` v1.3.0 (v1.4.0 for the ex-zd fixtures, since ex-zd support was added upstream after v1.3.0).
- `tests/c_parity.rs` runs live against whatever `slow5tools` build is on `PATH` at test time (skips automatically if absent).
If you re-validate against a different upstream version and find a wire-format or behavioural difference, please open an issue -- see [CONTRIBUTING.md](CONTRIBUTING.md) for the policy on re-validating after upstream releases.
## Acknowledgements
SLOW5 and BLOW5 were designed by [Hasindu Gamaarachchi](https://github.com/hasindu2008) and contributors. The original C library ([hasindu2008/slow5lib](https://github.com/hasindu2008/slow5lib)) is the reference implementation and specification for the format. This crate is an independent re-implementation; the C library's behaviour and wire format are authoritative wherever they differ from any written specification.
The original slow5lib is released under the MIT licence. A copy is included in [`LICENSE-slow5lib`](LICENSE-slow5lib).
If you use SLOW5/BLOW5 in your research, please cite:
Gamaarachchi, H., Samarakoon, H., Jenner, S.P. et al. Fast nanopore sequencing data analysis with SLOW5. *Nat Biotechnol* **40**, 1026-1029 (2022). https://doi.org/10.1038/s41587-021-01147-4
Samarakoon, H., Ferguson, J.M., Jenner, S.P. et al. Flexible and efficient handling of nanopore sequencing signal data with slow5tools. *Genome Biol* **24**, 69 (2023). https://doi.org/10.1186/s13059-023-02910-3
## AI assistance
This library was developed with AI assistance (Claude). Architecture decisions, wire-compatibility validation, and algorithm choices are the author's own; AI tooling served as an accelerator over existing skill. See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
## MSRV
1.87 (edition 2024).
## License
MIT. See [LICENSE](LICENSE). Copyright 2026 James Ferguson.
The SLOW5/BLOW5 format and original C implementation are copyright Hasindu Gamaarachchi and contributors, MIT licence. See [LICENSE-slow5lib](LICENSE-slow5lib).