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(name:"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&mut DebugStruct<'_, '_>.debug_struct("MemoryStream")
97 .field(name:"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: *mut SkMemoryStream = unsafe { sb::C_SkMemoryStream_MakeDirect(data:bytes.as_ptr() as _, length: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&mut DebugStruct<'_, '_>.debug_struct("DynamicMemoryWStream")
130 .field(
131 name:"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: *mut SkStream = 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: AssertUnwindSafe<&mut T> = std::panic::AssertUnwindSafe(val);
291
292 match std::panic::catch_unwind(move || val.seek(pos:io::SeekFrom::Start(pos as _))) {
293 Ok(res: Result) => 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: AssertUnwindSafe<&mut T> = std::panic::AssertUnwindSafe(val);
310
311 match std::panic::catch_unwind(move || val.seek(pos:io::SeekFrom::Current(offset as _))) {
312 Ok(res: Result) => 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}
446