1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use crate::{Head, StreamId};
use anyhow::Result;
use std::convert::TryInto;
use std::fs::File;
use std::io::{self, Read, Seek, SeekFrom};
use std::path::Path;

pub struct StreamReader {
    file: File,
    id: StreamId,
    start: u64,
    len: u64,
    pos: u64,
}

impl StreamReader {
    pub(crate) fn new(path: &Path, head: &Head, start: u64, len: u64) -> Result<Self> {
        if start + len > head.len {
            return Err(anyhow::anyhow!(
                "trying to read after the end of the stream"
            ));
        }
        let mut file = File::open(path)?;
        file.seek(SeekFrom::Start(start))?;
        Ok(Self {
            file,
            id: head.id,
            start,
            len,
            pos: 0,
        })
    }

    pub fn id(&self) -> &StreamId {
        &self.id
    }
}

impl Read for StreamReader {
    fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
        // read from file but within start/len bounds
        if self.pos >= self.len {
            return Ok(0);
        }
        if buf.len() as u64 >= self.len - self.pos {
            let (vbuf, _) = buf.split_at_mut((self.len - self.pos).try_into().unwrap());
            buf = vbuf;
        }
        let n = self.file.read(buf)?;
        self.pos += n as u64;
        Ok(n)
    }
}

impl Seek for StreamReader {
    fn seek(&mut self, seek: SeekFrom) -> io::Result<u64> {
        fn add_offset(position: i128, offset: i128) -> io::Result<u64> {
            let sum = position + offset;
            if sum < 0 {
                Err(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    "seek before beginning",
                ))
            } else if sum > u64::max_value() as i128 {
                Err(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    "seek target overflowed u64",
                ))
            } else {
                Ok(sum as u64)
            }
        }
        // seek from file but within start/len bounds
        let pos = match seek {
            SeekFrom::Start(start) => start,
            SeekFrom::Current(current) => add_offset(self.pos as _, current as _)?,
            SeekFrom::End(end) => add_offset(self.len as _, end as _)?,
        };
        let start = add_offset(self.start as _, pos as _)?;
        self.file.seek(SeekFrom::Start(start))?;
        self.pos = pos;
        Ok(self.pos)
    }

    fn stream_position(&mut self) -> io::Result<u64> {
        Ok(self.pos)
    }
}