rustzx_core/host/
io.rs

1use crate::error::IoError;
2use core::usize;
3
4type Result<T> = core::result::Result<T, IoError>;
5
6#[derive(Clone, Copy)]
7pub enum SeekFrom {
8    Start(usize),
9    End(isize),
10    Current(isize),
11}
12
13/// Implementation of loadable asset for buffer-like type
14pub struct BufferCursor<T: AsRef<[u8]>> {
15    data: T,
16    pos: usize,
17}
18
19impl<T: AsRef<[u8]>> BufferCursor<T> {
20    pub fn into_inner(self) -> T {
21        self.data
22    }
23}
24
25impl<T: AsRef<[u8]>> BufferCursor<T> {
26    pub fn new(data: T) -> Self {
27        Self { data, pos: 0 }
28    }
29}
30
31impl<T: AsRef<[u8]>> SeekableAsset for BufferCursor<T> {
32    fn seek(&mut self, pos: SeekFrom) -> Result<usize> {
33        let new_pos = match pos {
34            SeekFrom::Start(pos) => pos as isize,
35            SeekFrom::End(pos) => self.data.as_ref().len() as isize + pos,
36            SeekFrom::Current(pos) => self.pos as isize + pos,
37        };
38        if new_pos < 0 {
39            return Err(IoError::SeekBeforeStart);
40        }
41        self.pos = new_pos as usize;
42
43        Ok(self.pos)
44    }
45}
46
47impl<T: AsRef<[u8]>> LoadableAsset for BufferCursor<T> {
48    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
49        let data = self.data.as_ref();
50
51        if self.pos >= data.len() {
52            return Err(IoError::UnexpectedEof);
53        }
54        let bytes_to_read = buf.len().min(data.len() - self.pos);
55        buf[0..bytes_to_read].copy_from_slice(&data[self.pos..self.pos + bytes_to_read]);
56        self.pos += bytes_to_read;
57        Ok(bytes_to_read)
58    }
59}
60
61pub trait SeekableAsset {
62    /// Seek position in the asset. Returns current position in the asset
63    fn seek(&mut self, pos: SeekFrom) -> Result<usize>;
64}
65
66// While no_std environment does not provide Read
67// trait, we are forced to use substitution for it
68pub trait LoadableAsset {
69    /// Read data from asset to `buf`
70    /// Returns count of read bytes. Should return 0 read bytes when EOF was reached
71    fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
72
73    fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<()> {
74        while !buf.is_empty() {
75            match self.read(buf)? {
76                0 => {
77                    break;
78                }
79                n => {
80                    let tmp = buf;
81                    buf = &mut tmp[n..];
82                }
83            }
84        }
85        if !buf.is_empty() {
86            return Err(IoError::UnexpectedEof);
87        }
88
89        Ok(())
90    }
91}
92
93pub trait DataRecorder {
94    /// Writes given buffer to recorder.
95    /// Returns count of written bytes or 0 if end of the
96    /// destination asset was reached (e.g. buffer filled)
97    fn write(&mut self, buf: &[u8]) -> Result<usize>;
98
99    /// Writes all bytes to the destination or returns
100    /// [IoError::WriteZero] if destination refused to accept
101    /// more bytes
102    fn write_all(&mut self, mut buf: &[u8]) -> Result<()> {
103        while !buf.is_empty() {
104            match self.write(buf)? {
105                0 => return Err(IoError::WriteZero),
106                n => buf = &buf[n..],
107            }
108        }
109        Ok(())
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn buffer_cursor_seek_works() {
119        const BUFFER: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
120        let mut cursor = BufferCursor::new(BUFFER);
121
122        let mut tmp = [0u8; 1];
123        cursor.read_exact(&mut tmp).unwrap();
124        assert_eq!(tmp[0], 1);
125
126        cursor.seek(SeekFrom::Current(1)).unwrap();
127        cursor.read_exact(&mut tmp).unwrap();
128        assert_eq!(tmp[0], 3);
129
130        cursor.seek(SeekFrom::Start(2)).unwrap();
131        cursor.read_exact(&mut tmp).unwrap();
132        assert_eq!(tmp[0], 3);
133
134        let mut tmp = [0u8; 3];
135        cursor.seek(SeekFrom::End(-2)).unwrap();
136        let read_bytes = cursor.read(&mut tmp).unwrap();
137        assert_eq!(read_bytes, 2);
138        assert_eq!(tmp[0], 9);
139        assert_eq!(tmp[1], 10);
140    }
141}