winsfs_core/io/shuffle/
reader.rs

1use std::{
2    fs::File,
3    io::{self, Seek},
4    path::Path,
5};
6
7use byteorder::{ReadBytesExt, LE};
8
9use crate::{
10    io::{ReadSite, ReadStatus, Rewind},
11    saf::Site,
12};
13
14use super::{to_u64, Header};
15
16/// A pseudo-shuffled SAF file reader.
17pub struct Reader<const D: usize, R> {
18    inner: R,
19    header: Header,
20}
21
22/// A pseudo-shuffled SAF file reader.
23impl<const D: usize, R> Reader<D, R>
24where
25    R: io::BufRead,
26{
27    /// Returns the inner reader.
28    pub fn get(&self) -> &R {
29        &self.inner
30    }
31
32    /// Returns a mutable reference to the the inner reader.
33    pub fn get_mut(&mut self) -> &mut R {
34        &mut self.inner
35    }
36
37    /// Returns the header of the reader.
38    pub fn header(&self) -> &Header {
39        &self.header
40    }
41
42    /// Returns the inner reader, consuming `self`.
43    pub fn into_inner(self) -> R {
44        self.inner
45    }
46
47    /// Creates a new reader.
48    ///
49    /// The stream is expected to be positioned after the header.
50    pub fn new(reader: R, header: Header) -> Self {
51        Self {
52            inner: reader,
53            header,
54        }
55    }
56
57    /// Reads the header from the reader.
58    ///
59    /// The stream is assumed to be positioned at the beginning.
60    pub fn read_header(&mut self) -> io::Result<Header> {
61        Header::read(&mut self.inner)
62    }
63}
64
65impl<const D: usize, R> io::Seek for Reader<D, R>
66where
67    R: io::BufRead + io::Seek,
68{
69    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
70        self.inner.seek(pos)
71    }
72}
73
74impl<const D: usize> Reader<D, io::BufReader<File>> {
75    /// Creates a new reader from a path, and read its header.
76    ///
77    /// Returns an error if the dimensionality defined in the header is not `D`.
78    ///
79    /// The stream will be positioned after the header.
80    pub fn try_from_path<P>(path: P) -> io::Result<Self>
81    where
82        P: AsRef<Path>,
83    {
84        let mut reader = File::open(path).map(io::BufReader::new)?;
85
86        let header = Header::read(&mut reader)?;
87        let header_dimension = header.shape().len();
88
89        if header_dimension == D {
90            Ok(Self::new(reader, header))
91        } else {
92            let msg = format!(
93                "shuffled SAF file header with dimension {header_dimension} \
94                 did not match provided dimension {D}"
95            );
96            Err(io::Error::new(io::ErrorKind::InvalidData, msg))
97        }
98    }
99}
100
101impl<const D: usize, R> Rewind for Reader<D, R>
102where
103    R: io::BufRead + io::Seek,
104{
105    fn is_done(&mut self) -> io::Result<bool> {
106        // TODO: This can use io::BufRead::has_data_left once stable,
107        // see github.com/rust-lang/rust/issues/86423
108        self.inner.fill_buf().map(|b| b.is_empty())
109    }
110
111    fn rewind(&mut self) -> io::Result<()> {
112        self.seek(io::SeekFrom::Start(to_u64(self.header.header_size())))
113            .map(|_| ())
114    }
115}
116
117impl<const D: usize, R> ReadSite for Reader<D, R>
118where
119    R: io::BufRead + io::Seek,
120{
121    type Site = Site<D>;
122
123    fn read_site(&mut self, buf: &mut Self::Site) -> io::Result<ReadStatus> {
124        let status = self.read_site_unnormalised(buf)?;
125
126        buf.iter_mut().for_each(|x| *x = x.exp());
127
128        Ok(status)
129    }
130
131    fn read_site_unnormalised(&mut self, buf: &mut Self::Site) -> io::Result<ReadStatus> {
132        if ReadStatus::check(&mut self.inner)?.is_done() {
133            return Ok(ReadStatus::Done);
134        }
135
136        self.inner.read_f32_into::<LE>(buf.as_mut_slice())?;
137
138        Ok(ReadStatus::NotDone)
139    }
140}