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 | |