sdl2/
rwops.rs

1use crate::get_error;
2use libc::c_void;
3use libc::{c_char, c_int, size_t};
4use std::ffi::CString;
5use std::io;
6use std::marker::PhantomData;
7use std::path::Path;
8
9use crate::sys;
10
11/// A structure that provides an abstract interface to stream I/O.
12pub struct RWops<'a> {
13    raw: *mut sys::SDL_RWops,
14    _marker: PhantomData<&'a ()>,
15}
16
17impl<'a> RWops<'a> {
18    // this can prevent introducing UB until
19    // https://github.com/rust-lang/rust-clippy/issues/5953 is fixed
20    #[allow(clippy::trivially_copy_pass_by_ref)]
21    pub unsafe fn raw(&self) -> *mut sys::SDL_RWops {
22        self.raw
23    }
24
25    pub unsafe fn from_ll<'b>(raw: *mut sys::SDL_RWops) -> RWops<'b> {
26        RWops {
27            raw,
28            _marker: PhantomData,
29        }
30    }
31
32    /// Creates an SDL file stream.
33    #[doc(alias = "SDL_RWFromFile")]
34    pub fn from_file<P: AsRef<Path>>(path: P, mode: &str) -> Result<RWops<'static>, String> {
35        let raw = unsafe {
36            let path_c = CString::new(path.as_ref().to_str().unwrap()).unwrap();
37            let mode_c = CString::new(mode).unwrap();
38            sys::SDL_RWFromFile(
39                path_c.as_ptr() as *const c_char,
40                mode_c.as_ptr() as *const c_char,
41            )
42        };
43
44        if raw.is_null() {
45            Err(get_error())
46        } else {
47            Ok(RWops {
48                raw,
49                _marker: PhantomData,
50            })
51        }
52    }
53
54    /// Prepares a read-only memory buffer for use with `RWops`.
55    ///
56    /// This method can only fail if the buffer size is zero.
57    #[doc(alias = "SDL_RWFromConstMem")]
58    pub fn from_bytes(buf: &'a [u8]) -> Result<RWops<'a>, String> {
59        let raw =
60            unsafe { sys::SDL_RWFromConstMem(buf.as_ptr() as *const c_void, buf.len() as c_int) };
61
62        if raw.is_null() {
63            Err(get_error())
64        } else {
65            Ok(RWops {
66                raw,
67                _marker: PhantomData,
68            })
69        }
70    }
71
72    /// Reads a `Read` object into a buffer and then passes it to `RWops.from_bytes`.
73    ///
74    /// The buffer must be provided to this function and must live as long as the
75    /// `RWops`, but the `RWops` does not take ownership of it.
76    pub fn from_read<T>(r: &mut T, buffer: &'a mut Vec<u8>) -> Result<RWops<'a>, String>
77    where
78        T: io::Read + Sized,
79    {
80        match r.read_to_end(buffer) {
81            Ok(_size) => RWops::from_bytes(buffer),
82            Err(ioerror) => {
83                let msg = format!("IO error: {}", ioerror);
84                Err(msg)
85            }
86        }
87    }
88
89    /// Prepares a read-write memory buffer for use with `RWops`.
90    ///
91    /// This method can only fail if the buffer size is zero.
92    #[doc(alias = "SDL_RWFromMem")]
93    pub fn from_bytes_mut(buf: &'a mut [u8]) -> Result<RWops<'a>, String> {
94        let raw = unsafe { sys::SDL_RWFromMem(buf.as_ptr() as *mut c_void, buf.len() as c_int) };
95
96        if raw.is_null() {
97            Err(get_error())
98        } else {
99            Ok(RWops {
100                raw,
101                _marker: PhantomData,
102            })
103        }
104    }
105
106    /// Gets the stream's total size in bytes.
107    ///
108    /// Returns `None` if the stream size can't be determined
109    /// (either because it doesn't make sense for the stream type, or there was an error).
110    pub fn len(&self) -> Option<usize> {
111        let result = unsafe { ((*self.raw).size.unwrap())(self.raw) };
112
113        match result {
114            -1 => None,
115            v => Some(v as usize),
116        }
117    }
118
119    // Tells if the stream is empty
120    pub fn is_empty(&self) -> bool {
121        match self.len() {
122            Some(s) => s == 0,
123            None => true,
124        }
125    }
126}
127
128impl<'a> Drop for RWops<'a> {
129    fn drop(&mut self) {
130        let ret = unsafe { ((*self.raw).close.unwrap())(self.raw) };
131        if ret != 0 {
132            panic!("{}", get_error());
133        }
134    }
135}
136
137impl<'a> io::Read for RWops<'a> {
138    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
139        let out_len = buf.len() as size_t;
140        // FIXME: it's better to use as_mut_ptr().
141        // number of objects read, or 0 at error or end of file.
142        let ret = unsafe {
143            ((*self.raw).read.unwrap())(
144                self.raw,
145                buf.as_ptr() as *mut c_void,
146                1,
147                out_len as libc::size_t,
148            )
149        };
150        Ok(ret)
151    }
152}
153
154impl<'a> io::Write for RWops<'a> {
155    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
156        let in_len = buf.len() as size_t;
157        let ret = unsafe {
158            ((*self.raw).write.unwrap())(
159                self.raw,
160                buf.as_ptr() as *const c_void,
161                1,
162                in_len as libc::size_t,
163            )
164        };
165        Ok(ret)
166    }
167
168    fn flush(&mut self) -> io::Result<()> {
169        Ok(())
170    }
171}
172
173impl<'a> io::Seek for RWops<'a> {
174    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
175        // whence code is different from SeekStyle
176        let (whence, offset) = match pos {
177            io::SeekFrom::Start(pos) => (sys::RW_SEEK_SET, pos as i64),
178            io::SeekFrom::End(pos) => (sys::RW_SEEK_END, pos),
179            io::SeekFrom::Current(pos) => (sys::RW_SEEK_CUR, pos),
180        };
181        let ret = unsafe { ((*self.raw).seek.unwrap())(self.raw, offset, whence as i32) };
182        if ret == -1 {
183            Err(io::Error::last_os_error())
184        } else {
185            Ok(ret as u64)
186        }
187    }
188}