skia_safe/interop/
stream.rs

1//! `SkStream` and relatives.
2//! This implementation covers the minimal subset to interface with Rust streams.
3//!
4//! Bindings that wrap functions that use Skia stream types _must_ use Rust streams instead.
5
6use crate::{prelude::*, Data};
7use skia_bindings::{
8    self as sb, SkDynamicMemoryWStream, SkMemoryStream, SkStream, SkStreamAsset, SkWStream,
9};
10use std::{ffi, fmt, io, marker::PhantomData, mem, pin::Pin, ptr};
11
12/// Trait representing an Skia allocated Stream type with a base class of SkStream.
13#[repr(transparent)]
14pub struct Stream<N: NativeStreamBase>(ptr::NonNull<N>);
15unsafe impl<N: NativeStreamBase> Send for Stream<N> {}
16
17pub trait NativeStreamBase {
18    fn as_stream_mut(&mut self) -> &mut SkStream;
19}
20
21impl<T: NativeStreamBase> Drop for Stream<T> {
22    fn drop(&mut self) {
23        unsafe {
24            sb::C_SkStream_delete(self.0.as_ptr() as *mut _);
25        }
26    }
27}
28
29impl<N: NativeStreamBase> Stream<N> {
30    pub fn from_ptr(ptr: *mut N) -> Option<Stream<N>> {
31        ptr::NonNull::new(ptr).map(Stream)
32    }
33}
34
35pub type StreamAsset = Stream<SkStreamAsset>;
36impl NativeBase<SkStream> for SkStreamAsset {}
37
38impl NativeStreamBase for SkStreamAsset {
39    fn as_stream_mut(&mut self) -> &mut SkStream {
40        self.base_mut()
41    }
42}
43
44impl NativeAccess for StreamAsset {
45    type Native = SkStreamAsset;
46
47    fn native(&self) -> &SkStreamAsset {
48        unsafe { self.0.as_ref() }
49    }
50    fn native_mut(&mut self) -> &mut SkStreamAsset {
51        unsafe { self.0.as_mut() }
52    }
53}
54
55impl fmt::Debug for StreamAsset {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        f.debug_struct("StreamAsset").finish()
58    }
59}
60
61#[repr(C)]
62pub struct MemoryStream<'a> {
63    native: ptr::NonNull<SkMemoryStream>,
64    pd: PhantomData<&'a ()>,
65}
66unsafe impl Send for MemoryStream<'_> {}
67impl NativeBase<SkStream> for SkMemoryStream {}
68
69impl NativeStreamBase for SkMemoryStream {
70    fn as_stream_mut(&mut self) -> &mut SkStream {
71        self.base_mut()
72    }
73}
74
75impl Drop for MemoryStream<'_> {
76    fn drop(&mut self) {
77        unsafe {
78            sb::C_SkStream_delete(self.native_mut().as_stream_mut());
79        }
80    }
81}
82
83impl NativeAccess for MemoryStream<'_> {
84    type Native = SkMemoryStream;
85
86    fn native(&self) -> &SkMemoryStream {
87        unsafe { self.native.as_ref() }
88    }
89    fn native_mut(&mut self) -> &mut SkMemoryStream {
90        unsafe { self.native.as_mut() }
91    }
92}
93
94impl fmt::Debug for MemoryStream<'_> {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        f.debug_struct("MemoryStream")
97            .field("offset", &self.native().fOffset)
98            .finish()
99    }
100}
101
102impl MemoryStream<'_> {
103    // Create a stream asset that refers the bytes provided.
104    #[allow(unused)]
105    pub fn from_bytes(bytes: &[u8]) -> MemoryStream {
106        let ptr = unsafe { sb::C_SkMemoryStream_MakeDirect(bytes.as_ptr() as _, bytes.len()) };
107
108        MemoryStream {
109            native: ptr::NonNull::new(ptr).unwrap(),
110            pd: PhantomData,
111        }
112    }
113}
114
115pub type DynamicMemoryWStream = Handle<SkDynamicMemoryWStream>;
116
117impl NativeBase<SkWStream> for SkDynamicMemoryWStream {}
118
119impl NativeDrop for SkDynamicMemoryWStream {
120    fn drop(&mut self) {
121        unsafe {
122            sb::C_SkWStream_destruct(self.base_mut());
123        }
124    }
125}
126
127impl fmt::Debug for DynamicMemoryWStream {
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129        f.debug_struct("DynamicMemoryWStream")
130            .field(
131                "bytes_written_before_tail",
132                &self.native().fBytesWrittenBeforeTail,
133            )
134            .finish()
135    }
136}
137
138impl DynamicMemoryWStream {
139    pub fn new() -> Self {
140        Self::construct(|w_stream| unsafe { sb::C_SkDynamicMemoryWStream_Construct(w_stream) })
141    }
142
143    pub fn from_bytes(bytes: &[u8]) -> Self {
144        let mut stream = Self::new();
145        stream.write(bytes);
146        stream
147    }
148
149    pub fn write(&mut self, bytes: &[u8]) -> bool {
150        unsafe {
151            sb::C_SkWStream_write(
152                self.native_mut().base_mut(),
153                bytes.as_ptr() as _,
154                bytes.len(),
155            )
156        }
157    }
158
159    pub fn detach_as_data(&mut self) -> Data {
160        Data::from_ptr(unsafe { sb::C_SkDynamicMemoryWStream_detachAsData(self.native_mut()) })
161            .unwrap()
162    }
163
164    pub fn detach_as_stream(&mut self) -> StreamAsset {
165        StreamAsset::from_ptr(unsafe {
166            sb::C_SkDynamicMemoryWStream_detachAsStream(self.native_mut())
167        })
168        .unwrap()
169    }
170}
171
172#[allow(unused)]
173pub struct RustStream<'a> {
174    // This can't be a handle, because we need to be able to create a "deletable" C++ SkStream*.
175    inner: RefHandle<sb::RustStream>,
176    _phantom: PhantomData<&'a mut ()>,
177}
178
179#[allow(unused)]
180impl RustStream<'_> {
181    pub fn stream_mut(&mut self) -> &mut SkStream {
182        self.inner.native_mut().base_mut()
183    }
184
185    pub fn into_native(mut self) -> *mut SkStream {
186        let stream = self.inner.native_mut().base_mut() as *mut _;
187        mem::forget(self.inner);
188        stream
189    }
190}
191
192impl NativeBase<SkStream> for sb::RustStream {}
193
194impl NativeDrop for sb::RustStream {
195    fn drop(&mut self) {
196        unsafe { sb::C_RustStream_delete(self) }
197    }
198}
199
200#[allow(unused)]
201impl<'a> RustStream<'a> {
202    pub fn new_seekable<T: io::Read + io::Seek>(val: &'a mut T) -> Self {
203        Self {
204            inner: RefHandle::from_ptr(unsafe {
205                sb::C_RustStream_new(
206                    val as *mut T as *mut ffi::c_void,
207                    usize::MAX,
208                    Some(read_trampoline::<T>),
209                    Some(seek_start_trampoline::<T>),
210                    Some(seek_current_trampoline::<T>),
211                )
212            })
213            .unwrap(),
214            _phantom: PhantomData,
215        }
216    }
217
218    pub fn new<T: io::Read>(val: &'a mut T) -> Self {
219        Self {
220            inner: RefHandle::from_ptr(unsafe {
221                sb::C_RustStream_new(
222                    val as *mut T as *mut ffi::c_void,
223                    usize::MAX,
224                    Some(read_trampoline::<T>),
225                    None,
226                    None,
227                )
228            })
229            .unwrap(),
230            _phantom: PhantomData,
231        }
232    }
233}
234
235unsafe extern "C" fn read_trampoline<T>(
236    val: *mut ffi::c_void,
237    buf: *mut ffi::c_void,
238    count: usize,
239) -> usize
240where
241    T: io::Read,
242{
243    let val: &mut T = &mut *(val as *mut _);
244
245    if buf.is_null() {
246        const BUF_SIZE: usize = 128;
247
248        let mut buf = [0; BUF_SIZE];
249
250        let mut out_bytes = 0;
251        let mut count = count;
252
253        // This is OK because we just abort if it panics anyway.
254        let mut val = std::panic::AssertUnwindSafe(val);
255
256        let reader = move || {
257            while count > 0 {
258                let bytes = match val.read(&mut buf[..count.min(BUF_SIZE)]) {
259                    Ok(0) => break,
260                    Ok(bytes) => bytes,
261                    Err(_) => 0,
262                };
263
264                count -= bytes;
265                out_bytes += bytes;
266            }
267
268            out_bytes
269        };
270
271        match std::panic::catch_unwind(reader) {
272            Ok(res) => res,
273            Err(_) => {
274                println!("Panic in FFI callback for `SkStream::read`");
275                std::process::abort();
276            }
277        }
278    } else {
279        let buf: &mut [u8] = std::slice::from_raw_parts_mut(buf as _, count as _);
280
281        val.read(buf).unwrap_or(0)
282    }
283}
284
285unsafe extern "C" fn seek_start_trampoline<T: io::Seek>(val: *mut ffi::c_void, pos: usize) -> bool {
286    let val: &mut T = &mut *(val as *mut _);
287
288    // This is OK because we just abort if it panics anyway, we don't try
289    // to continue at all.
290    let mut val = std::panic::AssertUnwindSafe(val);
291
292    match std::panic::catch_unwind(move || val.seek(io::SeekFrom::Start(pos as _))) {
293        Ok(res) => res.is_ok(),
294        Err(_) => {
295            println!("Panic in FFI callback for `SkStream::start`");
296            std::process::abort();
297        }
298    }
299}
300
301unsafe extern "C" fn seek_current_trampoline<T: io::Seek>(
302    val: *mut ffi::c_void,
303    offset: ffi::c_long,
304) -> bool {
305    let val: &mut T = &mut *(val as *mut _);
306
307    // This is OK because we just abort if it panics anyway, we don't try
308    // to continue at all.
309    let mut val = std::panic::AssertUnwindSafe(val);
310
311    match std::panic::catch_unwind(move || val.seek(io::SeekFrom::Current(offset as _))) {
312        Ok(res) => res.is_ok(),
313        Err(_) => {
314            println!("Panic in FFI callback for `SkStream::move`");
315            std::process::abort();
316        }
317    }
318}
319
320#[allow(unused)]
321pub struct RustWStream<'a> {
322    /// We need to be able to refer to the inner RustWStream to be referred to by pointer, so box
323    /// it.
324    inner: Pin<Box<Handle<sb::RustWStream>>>,
325    _phantom: PhantomData<&'a mut ()>,
326}
327
328#[allow(unused)]
329impl RustWStream<'_> {
330    pub fn stream_mut(&mut self) -> &mut SkWStream {
331        self.inner.native_mut().base_mut()
332    }
333}
334
335impl NativeBase<SkWStream> for sb::RustWStream {}
336
337impl NativeDrop for sb::RustWStream {
338    fn drop(&mut self) {
339        unsafe { sb::C_RustWStream_destruct(self) }
340    }
341}
342
343impl<'a> RustWStream<'a> {
344    pub fn new<T: io::Write>(writer: &'a mut T) -> Self {
345        return RustWStream {
346            inner: Box::pin(Handle::construct(|ptr| unsafe {
347                sb::C_RustWStream_construct(
348                    ptr,
349                    writer as *mut T as *mut ffi::c_void,
350                    Some(write_trampoline::<T>),
351                    Some(flush_trampoline::<T>),
352                );
353            })),
354            _phantom: PhantomData,
355        };
356
357        unsafe extern "C" fn write_trampoline<T: io::Write>(
358            val: *mut ffi::c_void,
359            buf: *const ffi::c_void,
360            count: usize,
361        ) -> bool {
362            if count == 0 {
363                return true;
364            }
365            let buf: &[u8] = std::slice::from_raw_parts(buf as _, count as _);
366            let val: &mut T = &mut *(val as *mut _);
367
368            // This is OK because we just abort if it panics anyway.
369            let mut val = std::panic::AssertUnwindSafe(val);
370
371            let writer = move || {
372                let mut written = 0;
373                while written != count {
374                    match val.write(&buf[written..]) {
375                        Ok(res) if res != 0 => {
376                            written += res;
377                        }
378                        _ => return false,
379                    }
380                }
381                true
382            };
383
384            match std::panic::catch_unwind(writer) {
385                Ok(res) => res,
386                Err(_) => {
387                    println!("Panic in FFI callback for `SkWStream::write`");
388                    std::process::abort();
389                }
390            }
391        }
392
393        unsafe extern "C" fn flush_trampoline<T: io::Write>(val: *mut ffi::c_void) {
394            let val: &mut T = &mut *(val as *mut _);
395            // This is OK because we just abort if it panics anyway.
396            let mut val = std::panic::AssertUnwindSafe(val);
397
398            let flusher = move || {
399                // Not sure what could be done to handle a flush() error.
400                // Idea: use a with_stream method on the RustWStream that takes a closure, stores
401                // the flush() result and then return a result from with_stream.
402                let _flush_result_ignored = val.flush();
403            };
404
405            match std::panic::catch_unwind(flusher) {
406                Ok(_) => {}
407                Err(_) => {
408                    println!("Panic in FFI callback for `SkWStream::flush`");
409                    std::process::abort();
410                }
411            }
412        }
413    }
414}
415
416#[cfg(test)]
417mod tests {
418    use super::{MemoryStream, RustStream};
419    use crate::interop::DynamicMemoryWStream;
420
421    #[test]
422    fn detaching_empty_dynamic_memory_w_stream_leads_to_non_null_data() {
423        let mut stream = DynamicMemoryWStream::new();
424        let data = stream.detach_as_data();
425        assert_eq!(0, data.size())
426    }
427
428    #[test]
429    fn memory_stream_from_bytes() {
430        let stream = MemoryStream::from_bytes(&[1, 2, 3]);
431        drop(stream);
432    }
433
434    #[test]
435    fn read_from_rust_stream() {
436        let mut data: &[u8] = &[12u8, 13u8, 14u8];
437        let mut stream = RustStream::new(&mut data);
438
439        let mut first_byte = 0i8;
440        unsafe {
441            stream.stream_mut().readS8(&mut first_byte);
442        }
443        assert_eq!(first_byte, 12i8)
444    }
445}