noodles_cram/io/reader/header/container/
sam_header.rs1use std::io::{self, BufRead, BufReader, Read, Take};
4
5use bstr::ByteSlice;
6
7pub 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 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}