noodles_cram/io/reader/header/container/
sam_header.rs

1//! CRAM header container SAM header reader.
2
3use std::io::{self, BufRead, BufReader, Read, Take};
4
5use bstr::ByteSlice;
6
7/// A CRAM header container SAM header reader.
8pub struct Reader<R> {
9    inner: BufReader<Take<R>>,
10    is_eol: bool,
11}
12
13impl<R> Reader<R>
14where
15    R: Read,
16{
17    pub(super) fn new(inner: R, len: u64) -> Self {
18        Self {
19            inner: BufReader::new(inner.take(len)),
20            is_eol: true,
21        }
22    }
23
24    /// Discards all input until EOF.
25    pub fn discard_to_end(&mut self) -> io::Result<usize> {
26        let mut n = 0;
27
28        loop {
29            let src = self.inner.fill_buf()?;
30
31            if src.is_empty() {
32                return Ok(n);
33            }
34
35            let len = src.len();
36
37            self.inner.consume(len);
38
39            n += len;
40        }
41    }
42}
43
44impl<R> Read for Reader<R>
45where
46    R: Read,
47{
48    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
49        let mut src = self.fill_buf()?;
50        let amt = src.read(buf)?;
51
52        if !src.is_empty() {
53            self.is_eol = false;
54        }
55
56        self.consume(amt);
57
58        Ok(amt)
59    }
60}
61
62impl<R> BufRead for Reader<R>
63where
64    R: Read,
65{
66    fn fill_buf(&mut self) -> io::Result<&[u8]> {
67        const NUL: u8 = 0x00;
68        const LINE_FEED: u8 = b'\n';
69
70        let src = self.inner.fill_buf()?;
71
72        let buf = if self.is_eol && src.first().map(|&b| b == NUL).unwrap_or(true) {
73            &[]
74        } else if let Some(i) = src.as_bstr().find_byte(LINE_FEED) {
75            self.is_eol = true;
76            &src[..=i]
77        } else {
78            self.is_eol = false;
79            src
80        };
81
82        Ok(buf)
83    }
84
85    fn consume(&mut self, amt: usize) {
86        self.inner.consume(amt);
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_read_with_trailing_nul_padding() -> io::Result<()> {
96        const DATA: &[u8] = b"@HD\tVN:1.6\n";
97
98        let mut buf = DATA.to_vec();
99        buf.resize(1 << 10, 0);
100
101        let mut src = &buf[..];
102        let mut reader = Reader::new(&mut src, 1 << 10);
103
104        let mut buf = Vec::new();
105        reader.read_to_end(&mut buf)?;
106
107        assert_eq!(buf, DATA);
108
109        Ok(())
110    }
111}