blake_streams_core/
read.rs

1use crate::{Head, StreamId};
2use anyhow::Result;
3use std::convert::TryInto;
4use std::fs::File;
5use std::io::{self, Read, Seek, SeekFrom};
6use std::path::Path;
7
8pub struct StreamReader {
9    file: File,
10    id: StreamId,
11    start: u64,
12    len: u64,
13    pos: u64,
14}
15
16impl StreamReader {
17    pub(crate) fn new(path: &Path, head: &Head, start: u64, len: u64) -> Result<Self> {
18        if start + len > head.len {
19            return Err(anyhow::anyhow!(
20                "trying to read after the end of the stream"
21            ));
22        }
23        let mut file = File::open(path)?;
24        file.seek(SeekFrom::Start(start))?;
25        Ok(Self {
26            file,
27            id: head.id,
28            start,
29            len,
30            pos: 0,
31        })
32    }
33
34    pub fn id(&self) -> &StreamId {
35        &self.id
36    }
37}
38
39impl Read for StreamReader {
40    fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
41        // read from file but within start/len bounds
42        if self.pos >= self.len {
43            return Ok(0);
44        }
45        if buf.len() as u64 >= self.len - self.pos {
46            let (vbuf, _) = buf.split_at_mut((self.len - self.pos).try_into().unwrap());
47            buf = vbuf;
48        }
49        let n = self.file.read(buf)?;
50        self.pos += n as u64;
51        Ok(n)
52    }
53}
54
55impl Seek for StreamReader {
56    fn seek(&mut self, seek: SeekFrom) -> io::Result<u64> {
57        fn add_offset(position: i128, offset: i128) -> io::Result<u64> {
58            let sum = position + offset;
59            if sum < 0 {
60                Err(io::Error::new(
61                    io::ErrorKind::InvalidInput,
62                    "seek before beginning",
63                ))
64            } else if sum > u64::max_value() as i128 {
65                Err(io::Error::new(
66                    io::ErrorKind::InvalidInput,
67                    "seek target overflowed u64",
68                ))
69            } else {
70                Ok(sum as u64)
71            }
72        }
73        // seek from file but within start/len bounds
74        let pos = match seek {
75            SeekFrom::Start(start) => start,
76            SeekFrom::Current(current) => add_offset(self.pos as _, current as _)?,
77            SeekFrom::End(end) => add_offset(self.len as _, end as _)?,
78        };
79        let start = add_offset(self.start as _, pos as _)?;
80        self.file.seek(SeekFrom::Start(start))?;
81        self.pos = pos;
82        Ok(self.pos)
83    }
84
85    fn stream_position(&mut self) -> io::Result<u64> {
86        Ok(self.pos)
87    }
88}