| 1 | use std::{ |
| 2 | ffi::{CStr, CString}, |
| 3 | fmt, io, |
| 4 | ops::Deref, |
| 5 | path::Path, |
| 6 | }; |
| 7 | |
| 8 | use skia_bindings::{self as sb, SkData}; |
| 9 | |
| 10 | use crate::{interop::RustStream, prelude::*}; |
| 11 | |
| 12 | pub type Data = RCHandle<SkData>; |
| 13 | unsafe_send_sync!(Data); |
| 14 | require_base_type!(SkData, sb::SkNVRefCnt); |
| 15 | |
| 16 | impl NativeRefCounted for SkData { |
| 17 | fn _ref(&self) { |
| 18 | unsafe { sb::C_SkData_ref(self) } |
| 19 | } |
| 20 | |
| 21 | fn _unref(&self) { |
| 22 | unsafe { sb::C_SkData_unref(self) } |
| 23 | } |
| 24 | |
| 25 | fn unique(&self) -> bool { |
| 26 | unsafe { sb::C_SkData_unique(self) } |
| 27 | } |
| 28 | } |
| 29 | |
| 30 | impl Deref for Data { |
| 31 | type Target = [u8]; |
| 32 | fn deref(&self) -> &Self::Target { |
| 33 | self.as_bytes() |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | impl PartialEq for Data { |
| 38 | // Although there is an implementation in SkData for equality testing, we |
| 39 | // prefer to stay on the Rust side for that. |
| 40 | fn eq(&self, other: &Self) -> bool { |
| 41 | self.deref() == other.deref() |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | impl fmt::Debug for Data { |
| 46 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 47 | f.debug_struct("Data" ).field(name:"size" , &self.size()).finish() |
| 48 | } |
| 49 | } |
| 50 | |
| 51 | impl Data { |
| 52 | pub fn size(&self) -> usize { |
| 53 | self.native().fSize |
| 54 | } |
| 55 | |
| 56 | pub fn is_empty(&self) -> bool { |
| 57 | self.size() == 0 |
| 58 | } |
| 59 | |
| 60 | pub fn as_bytes(&self) -> &[u8] { |
| 61 | unsafe { safer::from_raw_parts(self.native().fPtr as _, self.size()) } |
| 62 | } |
| 63 | |
| 64 | // TODO: |
| 65 | // pub unsafe fn writable_data(&mut self) -> &mut [u8] |
| 66 | |
| 67 | pub fn copy_range(&self, offset: usize, buffer: &mut [u8]) -> &Self { |
| 68 | buffer.copy_from_slice(&self.as_bytes()[offset..offset + buffer.len()]); |
| 69 | self |
| 70 | } |
| 71 | |
| 72 | // TODO: rename to copy_from() ? or from_bytes()? |
| 73 | pub fn new_copy(data: &[u8]) -> Self { |
| 74 | Data::from_ptr(unsafe { sb::C_SkData_MakeWithCopy(data.as_ptr() as _, data.len()) }) |
| 75 | .unwrap() |
| 76 | } |
| 77 | |
| 78 | /// Constructs Data from a given byte slice without copying it. |
| 79 | /// |
| 80 | /// Users must make sure that the underlying slice will outlive the lifetime of the Data. |
| 81 | #[allow (clippy::missing_safety_doc)] |
| 82 | pub unsafe fn new_bytes(data: &[u8]) -> Self { |
| 83 | Data::from_ptr(sb::C_SkData_MakeWithoutCopy(data.as_ptr() as _, data.len())).unwrap() |
| 84 | } |
| 85 | |
| 86 | #[allow (clippy::missing_safety_doc)] |
| 87 | pub unsafe fn new_uninitialized(length: usize) -> Data { |
| 88 | Data::from_ptr(sb::C_SkData_MakeUninitialized(length)).unwrap() |
| 89 | } |
| 90 | |
| 91 | pub fn new_zero_initialized(length: usize) -> Data { |
| 92 | Data::from_ptr(unsafe { sb::C_SkData_MakeZeroInitialized(length) }).unwrap() |
| 93 | } |
| 94 | |
| 95 | // TODO: use Range as stand in for offset / length? |
| 96 | pub fn new_subset(data: &Data, offset: usize, length: usize) -> Data { |
| 97 | Data::from_ptr(unsafe { sb::C_SkData_MakeSubset(data.native(), offset, length) }).unwrap() |
| 98 | } |
| 99 | |
| 100 | /// Constructs Data from a copy of a &str. |
| 101 | /// |
| 102 | /// Functions that use `Data` as a string container usually expect it to contain a c-string |
| 103 | /// including the terminating 0 byte, so this function converts the Rust `str` to a `CString` |
| 104 | /// and calls [`Self::new_cstr()`]. |
| 105 | pub fn new_str(str: impl AsRef<str>) -> Data { |
| 106 | Self::new_cstr(&CString::new(str.as_ref()).unwrap()) |
| 107 | } |
| 108 | |
| 109 | /// Constructs Data from a &CStr by copying its contents. |
| 110 | pub fn new_cstr(cstr: &CStr) -> Data { |
| 111 | Data::from_ptr(unsafe { sb::C_SkData_MakeWithCString(cstr.as_ptr()) }).unwrap() |
| 112 | } |
| 113 | |
| 114 | /// Create a new `Data` referencing the file with the specified path. If the file cannot be |
| 115 | /// opened, the path contains 0 bytes, or the path is not valid UTF-8, this returns `None`. |
| 116 | /// |
| 117 | /// This function opens the file as a memory mapped file for the lifetime of `Data` returned. |
| 118 | pub fn from_filename(path: impl AsRef<Path>) -> Option<Self> { |
| 119 | let path = CString::new(path.as_ref().to_str()?).ok()?; |
| 120 | Data::from_ptr(unsafe { sb::C_SkData_MakeFromFileName(path.as_ptr()) }) |
| 121 | } |
| 122 | |
| 123 | // TODO: MakeFromFile (is there a way to wrap this safely?) |
| 124 | |
| 125 | /// Attempt to read size bytes into a [`Data`]. If the read succeeds, return the data, |
| 126 | /// else return `None`. Either way the stream's cursor may have been changed as a result |
| 127 | /// of calling read(). |
| 128 | pub fn from_stream(mut stream: impl io::Read, size: usize) -> Option<Self> { |
| 129 | let mut stream = RustStream::new(&mut stream); |
| 130 | Data::from_ptr(unsafe { sb::C_SkData_MakeFromStream(stream.stream_mut(), size) }) |
| 131 | } |
| 132 | |
| 133 | pub fn new_empty() -> Self { |
| 134 | Data::from_ptr(unsafe { sb::C_SkData_MakeEmpty() }).unwrap() |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | #[cfg (test)] |
| 139 | mod tests { |
| 140 | use super::*; |
| 141 | |
| 142 | impl RefCount for SkData { |
| 143 | fn ref_cnt(&self) -> usize { |
| 144 | self._base.ref_cnt() |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | #[test ] |
| 149 | fn data_supports_equals() { |
| 150 | let x: &[u8] = &[1u8, 2u8, 3u8]; |
| 151 | let d1 = Data::new_copy(x); |
| 152 | let d2 = Data::new_copy(x); |
| 153 | assert!(d1 == d2) |
| 154 | } |
| 155 | |
| 156 | #[test ] |
| 157 | fn from_stream_empty() { |
| 158 | let data = []; |
| 159 | let cursor = io::Cursor::new(data); |
| 160 | let data = Data::from_stream(cursor, 0).unwrap(); |
| 161 | assert_eq!(data.len(), 0); |
| 162 | } |
| 163 | |
| 164 | #[test ] |
| 165 | fn from_stream() { |
| 166 | let data = [1u8]; |
| 167 | let cursor = io::Cursor::new(data); |
| 168 | let data = Data::from_stream(cursor, 1).unwrap(); |
| 169 | assert_eq!(data.len(), 1); |
| 170 | assert_eq!(data[0], 1u8); |
| 171 | } |
| 172 | } |
| 173 | |