From 49f1b7a2cec2b036321521f1a2b5a0fe984665f7 Mon Sep 17 00:00:00 2001 From: "Stephen M. Coakley" Date: Mon, 3 Apr 2017 02:01:07 -0500 Subject: [PATCH] Create RWops with custom impl --- sdl2-sys/src/rwops.rs | 4 +- src/sdl2/rwops.rs | 190 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 191 insertions(+), 3 deletions(-) diff --git a/sdl2-sys/src/rwops.rs b/sdl2-sys/src/rwops.rs index 1c93680864c..09eba1777aa 100644 --- a/sdl2-sys/src/rwops.rs +++ b/sdl2-sys/src/rwops.rs @@ -4,7 +4,7 @@ use sdl::SDL_bool; #[allow(dead_code)] #[repr(C)] -struct SDL_RWops_Anon { +pub struct SDL_RWops_Anon { data: [c_uchar; 24], } @@ -23,7 +23,7 @@ pub struct SDL_RWops { size: size_t, maxnum: size_t) -> size_t, pub close: extern "C" fn(context: *mut SDL_RWops) -> c_int, pub type_: uint32_t, - hidden: SDL_RWops_Anon + pub hidden: SDL_RWops_Anon, } extern "C" { diff --git a/src/sdl2/rwops.rs b/src/sdl2/rwops.rs index b69768f2aed..01980396dea 100644 --- a/src/sdl2/rwops.rs +++ b/src/sdl2/rwops.rs @@ -2,7 +2,8 @@ use std::ffi::CString; use std::io; use std::path::Path; use std::marker::PhantomData; -use libc::{c_void, c_int, size_t, c_char}; +use std::slice; +use libc::{c_void, c_int, size_t, c_char, int64_t}; use get_error; use sys::rwops as ll; @@ -74,6 +75,14 @@ impl<'a> RWops<'a> { } } + /// Create an SDL RWops object from a Rust stream. + pub fn from_stream(reader: R) -> RWops<'static> { + CustomRWopsBuilder::new(reader) + .with_seek(|reader, from| reader.seek(from)) + .with_read(|reader, buf| reader.read(buf)) + .build() + } + /// Prepares a read-write memory buffer for use with `RWops`. /// /// This method can only fail if the buffer size is zero. @@ -159,3 +168,182 @@ impl<'a> io::Seek for RWops<'a> { } } } + +/// Builder for creating custom RWops implementations. +pub struct CustomRWopsBuilder { + ptr: *mut T, + size: Option i64>>, + seek: Option io::Result>>, + read: Option io::Result>>, + write: Option io::Result>>, +} + +impl CustomRWopsBuilder { + /// Create a new custom RWops builder around a value. + pub fn new(value: T) -> Self { + let ptr = Box::into_raw(Box::new(value)); + + Self { + ptr: ptr, + size: None, + seek: None, + read: None, + write: None, + } + } + + /// Set the callback for fetching the size. + pub fn with_size i64 + 'static>(mut self, mut f: F) -> Self { + let ptr = self.ptr; + self.size = Some(Box::new(move || unsafe { + f(&mut *ptr) + })); + + self + } + + /// Set the callback for seeking. + pub fn with_seek io::Result + 'static>(mut self, mut f: F) -> Self { + let ptr = self.ptr; + self.seek = Some(Box::new(move |from| unsafe { + f(&mut *ptr, from) + })); + + self + } + + /// Set the callback for reading. + pub fn with_read io::Result + 'static>(mut self, mut f: F) -> Self { + let ptr = self.ptr; + self.read = Some(Box::new(move |buf| unsafe { + f(&mut *ptr, buf) + })); + + self + } + + /// Set the callback for writing. + pub fn with_write io::Result + 'static>(mut self, mut f: F) -> Self { + let ptr = self.ptr; + self.write = Some(Box::new(move |buf| unsafe { + f(&mut *ptr, buf) + })); + + self + } + + /// Create a RWops from the builder. + pub fn build(self) -> RWops<'static> { + struct Callbacks { + size: Option i64>>, + seek: Option io::Result>>, + read: Option io::Result>>, + write: Option io::Result>>, + drop: Box, + } + + unsafe fn get_callbacks<'a>(ptr: *mut ll::SDL_RWops) -> *mut *mut Callbacks { + &(*ptr).hidden as *const _ as *mut _ + } + + extern "C" fn stream_size(rwops: *mut ll::SDL_RWops) -> int64_t { + let mut callbacks = unsafe { + &mut **get_callbacks(rwops) + }; + + callbacks.size + .as_mut() + .map(|f| f()) + .unwrap_or(-1) + } + + extern "C" fn stream_seek(rwops: *mut ll::SDL_RWops, offset: int64_t, whence: c_int) -> int64_t { + let mut callbacks = unsafe { + &mut **get_callbacks(rwops) + }; + + let from = match whence { + SEEK_SET => io::SeekFrom::Start(offset as u64), + SEEK_CUR => io::SeekFrom::Current(offset), + SEEK_END => io::SeekFrom::End(offset), + _ => return -1, + }; + + callbacks.seek + .as_mut() + .and_then(|f| f(from).ok()) + .map(|pos| pos as i64) + .unwrap_or(-1) + } + + extern "C" fn stream_read(rwops: *mut ll::SDL_RWops, ptr: *mut c_void, size: size_t, maxnum: size_t) -> size_t { + let mut callbacks = unsafe { + &mut **get_callbacks(rwops) + }; + + let buf = unsafe { + slice::from_raw_parts_mut(ptr as *mut u8, size * maxnum) + }; + + callbacks.read + .as_mut() + .and_then(|f| f(buf).ok()) + .unwrap_or(0) + } + + extern "C" fn stream_write(rwops: *mut ll::SDL_RWops, ptr: *const c_void, size: size_t, maxnum: size_t) -> size_t { + let mut callbacks = unsafe { + &mut **get_callbacks(rwops) + }; + + let buf = unsafe { + slice::from_raw_parts(ptr as *mut u8, size * maxnum) + }; + + callbacks.write + .as_mut() + .and_then(|f| f(buf).ok()) + .unwrap_or(0) + } + + extern "C" fn stream_close(rwops: *mut ll::SDL_RWops) -> c_int { + if !rwops.is_null() { + let callbacks = unsafe { + &mut **get_callbacks(rwops) + }; + + (callbacks.drop)(); + + unsafe { + ll::SDL_FreeRW(rwops); + } + } + 0 + } + + let value_ptr = self.ptr; + let callbacks = Callbacks { + size: self.size, + seek: self.seek, + read: self.read, + write: self.write, + drop: Box::new(move || unsafe { + Box::from_raw(value_ptr); + }), + }; + + unsafe { + let rwops_ptr = ll::SDL_AllocRW(); + + *get_callbacks(rwops_ptr) = Box::into_raw(Box::new(callbacks)); + (*rwops_ptr).type_ = 0; + (*rwops_ptr).size = stream_size; + (*rwops_ptr).seek = stream_seek; + (*rwops_ptr).read = stream_read; + (*rwops_ptr).write = stream_write; + (*rwops_ptr).close = stream_close; + + RWops::from_ll(rwops_ptr) + } + } +}