slow5lib 0.1.0

Rust re-implementation of slow5lib: read and write SLOW5/BLOW5 nanopore sequencing files
Documentation
use std::collections::HashMap;

use crate::Result;
use crate::aux::AuxValue;
use crate::header::SignalCompression;

/// A record whose signal is still in compressed form.
///
/// Produced by [`Slow5Reader::records_raw()`](crate::Slow5Reader::records_raw). The I/O cost is paid to construct
/// this; call [`decompress()`](RawRecord::decompress) (CPU-bound) to obtain a [`Record`].
/// Keeping these two steps separate allows callers to parallelise decompression
/// while disk reads remain sequential.
///
/// # Examples
///
/// ```no_run
/// use slow5lib::Slow5Reader;
///
/// # fn example() -> slow5lib::Result<()> {
/// let mut reader = Slow5Reader::open("reads.blow5")?;
/// for raw in reader.records_raw() {
///     let rec = raw?.decompress()?;
///     println!("{}: {} samples", rec.read_id, rec.raw_signal.len());
/// }
/// # Ok(()) }
/// ```
#[derive(Debug)]
pub struct RawRecord {
    pub read_id: String,
    pub read_group: u32,
    pub digitisation: f64,
    pub offset: f64,
    pub range: f64,
    pub sampling_rate: f64,
    /// Raw signal bytes as read from disk.
    /// For `SignalCompression::None`: little-endian i16 samples packed tightly.
    /// For `SignalCompression::SvbZd`: a 4-byte element-count prefix followed by
    /// U32Classic-encoded zigzag-delta data.
    /// For `SignalCompression::ExZd`: svb's self-describing ex-zd frame (no
    /// separate length prefix; the sample count is embedded in the frame).
    pub(crate) signal_bytes: Vec<u8>,
    pub(crate) signal_compression: SignalCompression,
    /// Auxiliary field values in header schema order.
    pub aux: HashMap<String, AuxValue>,
}

impl RawRecord {
    /// Decompress the raw signal, returning a fully decoded [`Record`].
    ///
    /// For SvbZd: reverse zigzag-delta decode on u32 values, then U32Classic
    /// decode, then truncate to i16. For ExZd: reverse the patched/exception
    /// decode, zigzag-delta decode, then undo the qts shift. For uncompressed:
    /// reinterpret bytes as i16.
    ///
    /// This is the CPU-bound half of reading a BLOW5 record.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use slow5lib::Slow5Reader;
    ///
    /// # fn example() -> slow5lib::Result<()> {
    /// let mut reader = Slow5Reader::open("reads.blow5")?;
    /// // records_raw() pays only the I/O cost; decompress() pays the CPU cost.
    /// let rec = reader.records_raw().next().unwrap()?.decompress()?;
    /// println!("{}: {} samples", rec.read_id, rec.raw_signal.len());
    /// # Ok(()) }
    /// ```
    pub fn decompress(self) -> Result<Record> {
        let raw_signal =
            crate::compression::decompress_signal(&self.signal_bytes, self.signal_compression)?;
        Ok(Record {
            read_id: self.read_id,
            read_group: self.read_group,
            digitisation: self.digitisation,
            offset: self.offset,
            range: self.range,
            sampling_rate: self.sampling_rate,
            raw_signal,
            aux: self.aux,
        })
    }
}

/// A fully decoded SLOW5/BLOW5 record.
///
/// Produced by [`RawRecord::decompress()`], [`Slow5Reader::records()`](crate::Slow5Reader::records),
/// or [`Slow5IndexedReader::get()`](crate::Slow5IndexedReader::get).
///
/// # Examples
///
/// ```
/// use slow5lib::record::Record;
/// use std::collections::HashMap;
///
/// let record = Record {
///     read_id: "read-001".to_string(),
///     read_group: 0,
///     digitisation: 2048.0,
///     offset: -224.0,
///     range: 1463.0,
///     sampling_rate: 4000.0,
///     raw_signal: vec![512i16, 515, 510],
///     aux: HashMap::new(),
/// };
/// // Convert ADC samples to picoamperes
/// let pa: Vec<f32> = record.raw_signal.iter()
///     .map(|&s| (s as f64 + record.offset) * record.range / record.digitisation)
///     .map(|v| v as f32)
///     .collect();
/// assert_eq!(pa.len(), 3);
/// ```
#[derive(Debug)]
pub struct Record {
    pub read_id: String,
    pub read_group: u32,
    pub digitisation: f64,
    pub offset: f64,
    pub range: f64,
    pub sampling_rate: f64,
    /// Decoded signal samples in ADC units.
    pub raw_signal: Vec<i16>,
    /// Auxiliary field values keyed by field name.
    pub aux: HashMap<String, AuxValue>,
}