1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use std::{ |
4 | borrow::Borrow, |
5 | cmp::Ordering, |
6 | fmt, |
7 | hash::{Hash, Hasher}, |
8 | mem, |
9 | ops::{Bound, Deref, RangeBounds}, |
10 | slice, |
11 | }; |
12 | |
13 | use crate::{ffi, translate::*}; |
14 | |
15 | wrapper! { |
16 | // rustdoc-stripper-ignore-next |
17 | /// A shared immutable byte slice (the equivalent of `Rc<[u8]>`). |
18 | /// |
19 | /// `From` implementations that take references (e.g. `&[u8]`) copy the |
20 | /// data. The `from_static` constructor avoids copying static data. |
21 | /// |
22 | /// ``` |
23 | /// use glib::Bytes; |
24 | /// |
25 | /// let v = vec![1, 2, 3]; |
26 | /// let b = Bytes::from(&v); |
27 | /// assert_eq!(v, b); |
28 | /// |
29 | /// let s = b"xyz"; |
30 | /// let b = Bytes::from_static(s); |
31 | /// assert_eq!(&s[..], b); |
32 | /// ``` |
33 | #[doc (alias = "GBytes" )] |
34 | pub struct Bytes(Shared<ffi::GBytes>); |
35 | |
36 | match fn { |
37 | ref => |ptr| ffi::g_bytes_ref(ptr), |
38 | unref => |ptr| ffi::g_bytes_unref(ptr), |
39 | type_ => || ffi::g_bytes_get_type(), |
40 | } |
41 | } |
42 | |
43 | impl Bytes { |
44 | // rustdoc-stripper-ignore-next |
45 | /// Copies `data` into a new shared slice. |
46 | #[doc (alias = "g_bytes_new" )] |
47 | #[inline ] |
48 | fn new<T: AsRef<[u8]>>(data: T) -> Bytes { |
49 | let data = data.as_ref(); |
50 | unsafe { from_glib_full(ffi::g_bytes_new(data.as_ptr() as *const _, data.len())) } |
51 | } |
52 | |
53 | // rustdoc-stripper-ignore-next |
54 | /// Creates a view into static `data` without copying. |
55 | #[doc (alias = "g_bytes_new_static" )] |
56 | #[inline ] |
57 | pub fn from_static(data: &'static [u8]) -> Bytes { |
58 | unsafe { |
59 | from_glib_full(ffi::g_bytes_new_static( |
60 | data.as_ptr() as *const _, |
61 | data.len(), |
62 | )) |
63 | } |
64 | } |
65 | |
66 | // rustdoc-stripper-ignore-next |
67 | /// Takes ownership of `data` and creates a new `Bytes` without copying. |
68 | #[doc (alias = "g_bytes_new" )] |
69 | pub fn from_owned<T: AsRef<[u8]> + Send + 'static>(data: T) -> Bytes { |
70 | let data: Box<T> = Box::new(data); |
71 | let (size, data_ptr) = { |
72 | let data = (*data).as_ref(); |
73 | (data.len(), data.as_ptr()) |
74 | }; |
75 | |
76 | unsafe extern "C" fn drop_box<T: AsRef<[u8]> + Send + 'static>(b: ffi::gpointer) { |
77 | let _: Box<T> = Box::from_raw(b as *mut _); |
78 | } |
79 | |
80 | unsafe { |
81 | from_glib_full(ffi::g_bytes_new_with_free_func( |
82 | data_ptr as *const _, |
83 | size, |
84 | Some(drop_box::<T>), |
85 | Box::into_raw(data) as *mut _, |
86 | )) |
87 | } |
88 | } |
89 | |
90 | // rustdoc-stripper-ignore-next |
91 | /// Returns the underlying data of the `Bytes`. |
92 | /// |
93 | /// If there is no other reference to `self` then this does not copy the data, otherwise |
94 | /// it is copied into newly allocated heap memory. |
95 | #[doc (alias = "g_bytes_unref_to_data" )] |
96 | pub fn into_data(self) -> crate::collections::Slice<u8> { |
97 | unsafe { |
98 | let mut size = mem::MaybeUninit::uninit(); |
99 | let ret = ffi::g_bytes_unref_to_data(self.into_glib_ptr(), size.as_mut_ptr()); |
100 | crate::collections::Slice::from_glib_full_num(ret as *mut u8, size.assume_init()) |
101 | } |
102 | } |
103 | |
104 | fn calculate_offset_size(&self, range: impl RangeBounds<usize>) -> (usize, usize) { |
105 | let len = self.len(); |
106 | |
107 | let start_offset = match range.start_bound() { |
108 | Bound::Included(v) => *v, |
109 | Bound::Excluded(v) => v.checked_add(1).expect("Invalid start offset" ), |
110 | Bound::Unbounded => 0, |
111 | }; |
112 | assert!(start_offset < len, "Start offset after valid range" ); |
113 | |
114 | let end_offset = match range.end_bound() { |
115 | Bound::Included(v) => v.checked_add(1).expect("Invalid end offset" ), |
116 | Bound::Excluded(v) => *v, |
117 | Bound::Unbounded => len, |
118 | }; |
119 | assert!(end_offset <= len, "End offset after valid range" ); |
120 | |
121 | let size = end_offset.saturating_sub(start_offset); |
122 | |
123 | (start_offset, size) |
124 | } |
125 | |
126 | // rustdoc-stripper-ignore-next |
127 | /// Creates a new `Bytes` that references the given `range` of `bytes`. |
128 | #[doc (alias = "g_bytes_new_from_bytes" )] |
129 | pub fn from_bytes(bytes: &Self, range: impl RangeBounds<usize>) -> Self { |
130 | let (offset, size) = bytes.calculate_offset_size(range); |
131 | unsafe { |
132 | from_glib_full(ffi::g_bytes_new_from_bytes( |
133 | bytes.to_glib_none().0, |
134 | offset, |
135 | size, |
136 | )) |
137 | } |
138 | } |
139 | } |
140 | |
141 | unsafe impl Send for Bytes {} |
142 | unsafe impl Sync for Bytes {} |
143 | |
144 | impl<'a, T: ?Sized + Borrow<[u8]> + 'a> From<&'a T> for Bytes { |
145 | #[inline ] |
146 | fn from(value: &'a T) -> Bytes { |
147 | Bytes::new(data:value.borrow()) |
148 | } |
149 | } |
150 | |
151 | impl fmt::Debug for Bytes { |
152 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
153 | f&mut DebugStruct<'_, '_>.debug_struct("Bytes" ) |
154 | .field("ptr" , &ToGlibPtr::<*const _>::to_glib_none(self).0) |
155 | .field(name:"data" , &&self[..]) |
156 | .finish() |
157 | } |
158 | } |
159 | |
160 | impl AsRef<[u8]> for Bytes { |
161 | #[inline ] |
162 | fn as_ref(&self) -> &[u8] { |
163 | self |
164 | } |
165 | } |
166 | |
167 | impl Deref for Bytes { |
168 | type Target = [u8]; |
169 | |
170 | #[inline ] |
171 | fn deref(&self) -> &[u8] { |
172 | unsafe { |
173 | let mut len: usize = 0; |
174 | let ptr: *const c_void = ffi::g_bytes_get_data(self.to_glib_none().0, &mut len); |
175 | if ptr.is_null() || len == 0 { |
176 | &[] |
177 | } else { |
178 | slice::from_raw_parts(data:ptr as *const u8, len) |
179 | } |
180 | } |
181 | } |
182 | } |
183 | |
184 | impl PartialEq for Bytes { |
185 | #[doc (alias = "g_bytes_equal" )] |
186 | #[inline ] |
187 | fn eq(&self, other: &Self) -> bool { |
188 | unsafe { |
189 | from_glib(val:ffi::g_bytes_equal( |
190 | bytes1:ToGlibPtr::<*const _>::to_glib_none(self).0 as *const _, |
191 | bytes2:ToGlibPtr::<*const _>::to_glib_none(self:other).0 as *const _, |
192 | )) |
193 | } |
194 | } |
195 | } |
196 | |
197 | impl Eq for Bytes {} |
198 | |
199 | impl PartialOrd for Bytes { |
200 | #[inline ] |
201 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
202 | Some(self.cmp(other)) |
203 | } |
204 | } |
205 | |
206 | impl Ord for Bytes { |
207 | #[inline ] |
208 | fn cmp(&self, other: &Self) -> Ordering { |
209 | unsafe { |
210 | let ret = ffi::g_bytes_compare( |
211 | bytes1:ToGlibPtr::<*const _>::to_glib_none(self).0 as *const _, |
212 | bytes2:ToGlibPtr::<*const _>::to_glib_none(self:other).0 as *const _, |
213 | ); |
214 | ret.cmp(&0) |
215 | } |
216 | } |
217 | } |
218 | |
219 | macro_rules! impl_cmp { |
220 | ($lhs:ty, $rhs: ty) => { |
221 | #[allow(clippy::redundant_slicing)] |
222 | #[allow(clippy::extra_unused_lifetimes)] |
223 | impl<'a, 'b> PartialEq<$rhs> for $lhs { |
224 | #[inline] |
225 | fn eq(&self, other: &$rhs) -> bool { |
226 | self[..].eq(&other[..]) |
227 | } |
228 | } |
229 | |
230 | #[allow(clippy::redundant_slicing)] |
231 | #[allow(clippy::extra_unused_lifetimes)] |
232 | impl<'a, 'b> PartialEq<$lhs> for $rhs { |
233 | #[inline] |
234 | fn eq(&self, other: &$lhs) -> bool { |
235 | self[..].eq(&other[..]) |
236 | } |
237 | } |
238 | |
239 | #[allow(clippy::redundant_slicing)] |
240 | #[allow(clippy::extra_unused_lifetimes)] |
241 | impl<'a, 'b> PartialOrd<$rhs> for $lhs { |
242 | #[inline] |
243 | fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> { |
244 | self[..].partial_cmp(&other[..]) |
245 | } |
246 | } |
247 | |
248 | #[allow(clippy::redundant_slicing)] |
249 | #[allow(clippy::extra_unused_lifetimes)] |
250 | impl<'a, 'b> PartialOrd<$lhs> for $rhs { |
251 | #[inline] |
252 | fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> { |
253 | self[..].partial_cmp(&other[..]) |
254 | } |
255 | } |
256 | }; |
257 | } |
258 | |
259 | impl_cmp!(Bytes, [u8]); |
260 | impl_cmp!(Bytes, &'a [u8]); |
261 | impl_cmp!(&'a Bytes, [u8]); |
262 | impl_cmp!(Bytes, Vec<u8>); |
263 | impl_cmp!(&'a Bytes, Vec<u8>); |
264 | |
265 | impl Hash for Bytes { |
266 | #[inline ] |
267 | fn hash<H: Hasher>(&self, state: &mut H) { |
268 | self.len().hash(state); |
269 | Hash::hash_slice(self, state) |
270 | } |
271 | } |
272 | |
273 | #[cfg (test)] |
274 | mod tests { |
275 | use std::collections::HashSet; |
276 | |
277 | use super::*; |
278 | |
279 | #[test ] |
280 | fn eq() { |
281 | let abc: &[u8] = b"abc" ; |
282 | let def: &[u8] = b"def" ; |
283 | let a1 = Bytes::from(abc); |
284 | let a2 = Bytes::from(abc); |
285 | let d = Bytes::from(def); |
286 | assert_eq!(a1, a2); |
287 | assert_eq!(def, d); |
288 | assert_ne!(a1, d); |
289 | assert_ne!(a1, def); |
290 | } |
291 | |
292 | #[test ] |
293 | fn ord() { |
294 | let abc: &[u8] = b"abc" ; |
295 | let def: &[u8] = b"def" ; |
296 | let a = Bytes::from(abc); |
297 | let d = Bytes::from(def); |
298 | assert!(a < d); |
299 | assert!(a < def); |
300 | assert!(abc < d); |
301 | assert!(d > a); |
302 | assert!(d > abc); |
303 | assert!(def > a); |
304 | } |
305 | |
306 | #[test ] |
307 | fn hash() { |
308 | let b1 = Bytes::from(b"this is a test" ); |
309 | let b2 = Bytes::from(b"this is a test" ); |
310 | let b3 = Bytes::from(b"test" ); |
311 | let mut set = HashSet::new(); |
312 | set.insert(b1); |
313 | assert!(set.contains(&b2)); |
314 | assert!(!set.contains(&b3)); |
315 | } |
316 | |
317 | #[test ] |
318 | fn from_static() { |
319 | let b1 = Bytes::from_static(b"this is a test" ); |
320 | let b2 = Bytes::from(b"this is a test" ); |
321 | assert_eq!(b1, b2); |
322 | } |
323 | |
324 | #[test ] |
325 | fn from_owned() { |
326 | let b = Bytes::from_owned(vec![1, 2, 3]); |
327 | assert_eq!(b, [1u8, 2u8, 3u8].as_ref()); |
328 | } |
329 | |
330 | #[test ] |
331 | fn from_bytes() { |
332 | let b1 = Bytes::from_owned(vec![1, 2, 3]); |
333 | let b2 = Bytes::from_bytes(&b1, 1..=1); |
334 | assert_eq!(b2, [2u8].as_ref()); |
335 | let b2 = Bytes::from_bytes(&b1, 1..); |
336 | assert_eq!(b2, [2u8, 3u8].as_ref()); |
337 | let b2 = Bytes::from_bytes(&b1, ..2); |
338 | assert_eq!(b2, [1u8, 2u8].as_ref()); |
339 | let b2 = Bytes::from_bytes(&b1, ..); |
340 | assert_eq!(b2, [1u8, 2u8, 3u8].as_ref()); |
341 | } |
342 | |
343 | #[test ] |
344 | pub fn into_data() { |
345 | let b = Bytes::from(b"this is a test" ); |
346 | let d = b.into_data(); |
347 | assert_eq!(d.as_slice(), b"this is a test" ); |
348 | } |
349 | } |
350 | |