| 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 | |
| 6 | use crate::{prelude::*, Data}; |
| 7 | use skia_bindings::{ |
| 8 | self as sb, SkDynamicMemoryWStream, SkMemoryStream, SkStream, SkStreamAsset, SkWStream, |
| 9 | }; |
| 10 | use 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)] |
| 14 | pub struct Stream<N: NativeStreamBase>(ptr::NonNull<N>); |
| 15 | unsafe impl<N: NativeStreamBase> Send for Stream<N> {} |
| 16 | |
| 17 | pub trait NativeStreamBase { |
| 18 | fn as_stream_mut(&mut self) -> &mut SkStream; |
| 19 | } |
| 20 | |
| 21 | impl<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 | |
| 29 | impl<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 | |
| 35 | pub type StreamAsset = Stream<SkStreamAsset>; |
| 36 | impl NativeBase<SkStream> for SkStreamAsset {} |
| 37 | |
| 38 | impl NativeStreamBase for SkStreamAsset { |
| 39 | fn as_stream_mut(&mut self) -> &mut SkStream { |
| 40 | self.base_mut() |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | impl 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 | |
| 55 | impl 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)] |
| 62 | pub struct MemoryStream<'a> { |
| 63 | native: ptr::NonNull<SkMemoryStream>, |
| 64 | pd: PhantomData<&'a ()>, |
| 65 | } |
| 66 | unsafe impl Send for MemoryStream<'_> {} |
| 67 | impl NativeBase<SkStream> for SkMemoryStream {} |
| 68 | |
| 69 | impl NativeStreamBase for SkMemoryStream { |
| 70 | fn as_stream_mut(&mut self) -> &mut SkStream { |
| 71 | self.base_mut() |
| 72 | } |
| 73 | } |
| 74 | |
| 75 | impl 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 | |
| 83 | impl 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 | |
| 94 | impl 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 | |
| 102 | impl 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 | |
| 115 | pub type DynamicMemoryWStream = Handle<SkDynamicMemoryWStream>; |
| 116 | |
| 117 | impl NativeBase<SkWStream> for SkDynamicMemoryWStream {} |
| 118 | |
| 119 | impl NativeDrop for SkDynamicMemoryWStream { |
| 120 | fn drop(&mut self) { |
| 121 | unsafe { |
| 122 | sb::C_SkWStream_destruct(self.base_mut()); |
| 123 | } |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | impl 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 | |
| 138 | impl 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)] |
| 173 | pub 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)] |
| 180 | impl 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 | |
| 192 | impl NativeBase<SkStream> for sb::RustStream {} |
| 193 | |
| 194 | impl NativeDrop for sb::RustStream { |
| 195 | fn drop(&mut self) { |
| 196 | unsafe { sb::C_RustStream_delete(self) } |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | #[allow (unused)] |
| 201 | impl<'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 | |
| 235 | unsafe extern "C" fn read_trampoline<T>( |
| 236 | val: *mut ffi::c_void, |
| 237 | buf: *mut ffi::c_void, |
| 238 | count: usize, |
| 239 | ) -> usize |
| 240 | where |
| 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 | |
| 285 | unsafe 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 | |
| 301 | unsafe 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)] |
| 321 | pub 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)] |
| 329 | impl RustWStream<'_> { |
| 330 | pub fn stream_mut(&mut self) -> &mut SkWStream { |
| 331 | self.inner.native_mut().base_mut() |
| 332 | } |
| 333 | } |
| 334 | |
| 335 | impl NativeBase<SkWStream> for sb::RustWStream {} |
| 336 | |
| 337 | impl NativeDrop for sb::RustWStream { |
| 338 | fn drop(&mut self) { |
| 339 | unsafe { sb::C_RustWStream_destruct(self) } |
| 340 | } |
| 341 | } |
| 342 | |
| 343 | impl<'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)] |
| 417 | mod 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 | |