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
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use crate::error::IoError;
use core::usize;

type Result<T> = core::result::Result<T, IoError>;

#[derive(Clone, Copy)]
pub enum SeekFrom {
    Start(usize),
    End(isize),
    Current(isize),
}

/// Implementation of loadable asset for buffer-like type
pub struct BufferCursor<T: AsRef<[u8]>> {
    data: T,
    pos: usize,
}

impl<T: AsRef<[u8]>> BufferCursor<T> {
    pub fn new(data: T) -> Self {
        Self { data, pos: 0 }
    }
}

impl<T: AsRef<[u8]>> SeekableAsset for BufferCursor<T> {
    fn seek(&mut self, pos: SeekFrom) -> Result<usize> {
        let new_pos = match pos {
            SeekFrom::Start(pos) => pos as isize,
            SeekFrom::End(pos) => self.data.as_ref().len() as isize + pos,
            SeekFrom::Current(pos) => self.pos as isize + pos,
        };
        if new_pos < 0 {
            return Err(IoError::SeekBeforeStart);
        }
        self.pos = new_pos as usize;

        Ok(self.pos)
    }
}

impl<T: AsRef<[u8]>> LoadableAsset for BufferCursor<T> {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        let data = self.data.as_ref();

        if self.pos >= data.len() {
            return Err(IoError::UnexpectedEof);
        }
        let bytes_to_read = buf.len().min(data.len() - self.pos);
        buf[0..bytes_to_read].copy_from_slice(&data[self.pos..self.pos + bytes_to_read]);
        self.pos += bytes_to_read;
        Ok(bytes_to_read)
    }
}

pub trait SeekableAsset {
    /// Seek position in the asset. Returns current position in the asset
    fn seek(&mut self, pos: SeekFrom) -> Result<usize>;
}

// While no_std environment does not provide Read
// trait, we are forced to use substitution for it
pub trait LoadableAsset {
    /// Read data from asset to `buf`
    /// Returns count of read bytes. Should return 0 read bytes when EOF was reached
    fn read(&mut self, buf: &mut [u8]) -> Result<usize>;

    fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<()> {
        while !buf.is_empty() {
            match self.read(buf)? {
                0 => {
                    break;
                }
                n => {
                    let tmp = buf;
                    buf = &mut tmp[n..];
                }
            }
        }
        if !buf.is_empty() {
            return Err(IoError::UnexpectedEof);
        }

        Ok(())
    }
}

pub trait DataRecorder {
    /// Writes given buffer to recorder.
    /// Returns count of written bytes or 0 if end of the
    /// destination asset was reached (e.g. buffer filled)
    fn write(&mut self, buf: &[u8]) -> Result<usize>;

    /// Writes all bytes to the destiantion or returns
    /// [IoError::WriteZero] if destination refused to accept
    /// more bytes
    fn write_all(&mut self, mut buf: &[u8]) -> Result<()> {
        while !buf.is_empty() {
            match self.write(buf)? {
                0 => return Err(IoError::WriteZero),
                n => buf = &buf[n..],
            }
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn buffer_cursor_seek_works() {
        const BUFFER: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
        let mut cursor = BufferCursor::new(BUFFER);

        let mut tmp = [0u8; 1];
        cursor.read_exact(&mut tmp).unwrap();
        assert_eq!(tmp[0], 1);

        cursor.seek(SeekFrom::Current(1)).unwrap();
        cursor.read_exact(&mut tmp).unwrap();
        assert_eq!(tmp[0], 3);

        cursor.seek(SeekFrom::Start(2)).unwrap();
        cursor.read_exact(&mut tmp).unwrap();
        assert_eq!(tmp[0], 3);

        let mut tmp = [0u8; 3];
        cursor.seek(SeekFrom::End(-2)).unwrap();
        let read_bytes = cursor.read(&mut tmp).unwrap();
        assert_eq!(read_bytes, 2);
        assert_eq!(tmp[0], 9);
        assert_eq!(tmp[1], 10);
    }
}