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::{Eq, Ord, Ordering, PartialEq, PartialOrd}, |
6 | fmt, |
7 | hash::{Hash, Hasher}, |
8 | ops::Deref, |
9 | slice, |
10 | }; |
11 | |
12 | use crate::translate::*; |
13 | |
14 | wrapper! { |
15 | // rustdoc-stripper-ignore-next |
16 | /// A shared immutable byte slice (the equivalent of `Rc<[u8]>`). |
17 | /// |
18 | /// `From` implementations that take references (e.g. `&[u8]`) copy the |
19 | /// data. The `from_static` constructor avoids copying static data. |
20 | /// |
21 | /// ``` |
22 | /// use glib::Bytes; |
23 | /// |
24 | /// let v = vec![1, 2, 3]; |
25 | /// let b = Bytes::from(&v); |
26 | /// assert_eq!(v, b); |
27 | /// |
28 | /// let s = b"xyz"; |
29 | /// let b = Bytes::from_static(s); |
30 | /// assert_eq!(&s[..], b); |
31 | /// ``` |
32 | #[doc (alias = "GBytes" )] |
33 | pub struct Bytes(Shared<ffi::GBytes>); |
34 | |
35 | match fn { |
36 | ref => |ptr| ffi::g_bytes_ref(ptr), |
37 | unref => |ptr| ffi::g_bytes_unref(ptr), |
38 | type_ => || ffi::g_bytes_get_type(), |
39 | } |
40 | } |
41 | |
42 | impl Bytes { |
43 | // rustdoc-stripper-ignore-next |
44 | /// Copies `data` into a new shared slice. |
45 | #[doc (alias = "g_bytes_new" )] |
46 | #[inline ] |
47 | fn new<T: AsRef<[u8]>>(data: T) -> Bytes { |
48 | let data = data.as_ref(); |
49 | unsafe { from_glib_full(ffi::g_bytes_new(data.as_ptr() as *const _, data.len())) } |
50 | } |
51 | |
52 | // rustdoc-stripper-ignore-next |
53 | /// Creates a view into static `data` without copying. |
54 | #[doc (alias = "g_bytes_new_static" )] |
55 | #[inline ] |
56 | pub fn from_static(data: &'static [u8]) -> Bytes { |
57 | unsafe { |
58 | from_glib_full(ffi::g_bytes_new_static( |
59 | data.as_ptr() as *const _, |
60 | data.len(), |
61 | )) |
62 | } |
63 | } |
64 | |
65 | // rustdoc-stripper-ignore-next |
66 | /// Takes ownership of `data` and creates a new `Bytes` without copying. |
67 | pub fn from_owned<T: AsRef<[u8]> + Send + 'static>(data: T) -> Bytes { |
68 | let data: Box<T> = Box::new(data); |
69 | let (size, data_ptr) = { |
70 | let data = (*data).as_ref(); |
71 | (data.len(), data.as_ptr()) |
72 | }; |
73 | |
74 | unsafe extern "C" fn drop_box<T: AsRef<[u8]> + Send + 'static>(b: ffi::gpointer) { |
75 | let _: Box<T> = Box::from_raw(b as *mut _); |
76 | } |
77 | |
78 | unsafe { |
79 | from_glib_full(ffi::g_bytes_new_with_free_func( |
80 | data_ptr as *const _, |
81 | size, |
82 | Some(drop_box::<T>), |
83 | Box::into_raw(data) as *mut _, |
84 | )) |
85 | } |
86 | } |
87 | } |
88 | |
89 | unsafe impl Send for Bytes {} |
90 | unsafe impl Sync for Bytes {} |
91 | |
92 | impl<'a, T: ?Sized + Borrow<[u8]> + 'a> From<&'a T> for Bytes { |
93 | #[inline ] |
94 | fn from(value: &'a T) -> Bytes { |
95 | Bytes::new(data:value.borrow()) |
96 | } |
97 | } |
98 | |
99 | impl fmt::Debug for Bytes { |
100 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
101 | f&mut DebugStruct<'_, '_>.debug_struct("Bytes" ) |
102 | .field("ptr" , &ToGlibPtr::<*const _>::to_glib_none(self).0) |
103 | .field(name:"data" , &&self[..]) |
104 | .finish() |
105 | } |
106 | } |
107 | |
108 | impl AsRef<[u8]> for Bytes { |
109 | #[inline ] |
110 | fn as_ref(&self) -> &[u8] { |
111 | self |
112 | } |
113 | } |
114 | |
115 | impl Deref for Bytes { |
116 | type Target = [u8]; |
117 | |
118 | #[inline ] |
119 | fn deref(&self) -> &[u8] { |
120 | unsafe { |
121 | let mut len: usize = 0; |
122 | let ptr: *const c_void = ffi::g_bytes_get_data(self.to_glib_none().0, &mut len); |
123 | if ptr.is_null() || len == 0 { |
124 | &[] |
125 | } else { |
126 | slice::from_raw_parts(data:ptr as *const u8, len) |
127 | } |
128 | } |
129 | } |
130 | } |
131 | |
132 | impl PartialEq for Bytes { |
133 | #[doc (alias = "g_bytes_equal" )] |
134 | #[inline ] |
135 | fn eq(&self, other: &Self) -> bool { |
136 | unsafe { |
137 | from_glib(val:ffi::g_bytes_equal( |
138 | bytes1:ToGlibPtr::<*const _>::to_glib_none(self).0 as *const _, |
139 | bytes2:ToGlibPtr::<*const _>::to_glib_none(self:other).0 as *const _, |
140 | )) |
141 | } |
142 | } |
143 | } |
144 | |
145 | impl Eq for Bytes {} |
146 | |
147 | impl PartialOrd for Bytes { |
148 | #[inline ] |
149 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
150 | Some(self.cmp(other)) |
151 | } |
152 | } |
153 | |
154 | impl Ord for Bytes { |
155 | #[inline ] |
156 | fn cmp(&self, other: &Self) -> Ordering { |
157 | unsafe { |
158 | let ret: i32 = ffi::g_bytes_compare( |
159 | bytes1:ToGlibPtr::<*const _>::to_glib_none(self).0 as *const _, |
160 | bytes2:ToGlibPtr::<*const _>::to_glib_none(self:other).0 as *const _, |
161 | ); |
162 | ret.cmp(&0) |
163 | } |
164 | } |
165 | } |
166 | |
167 | macro_rules! impl_cmp { |
168 | ($lhs:ty, $rhs: ty) => { |
169 | #[allow(clippy::redundant_slicing)] |
170 | #[allow(clippy::extra_unused_lifetimes)] |
171 | impl<'a, 'b> PartialEq<$rhs> for $lhs { |
172 | #[inline] |
173 | fn eq(&self, other: &$rhs) -> bool { |
174 | self[..].eq(&other[..]) |
175 | } |
176 | } |
177 | |
178 | #[allow(clippy::redundant_slicing)] |
179 | #[allow(clippy::extra_unused_lifetimes)] |
180 | impl<'a, 'b> PartialEq<$lhs> for $rhs { |
181 | #[inline] |
182 | fn eq(&self, other: &$lhs) -> bool { |
183 | self[..].eq(&other[..]) |
184 | } |
185 | } |
186 | |
187 | #[allow(clippy::redundant_slicing)] |
188 | #[allow(clippy::extra_unused_lifetimes)] |
189 | impl<'a, 'b> PartialOrd<$rhs> for $lhs { |
190 | #[inline] |
191 | fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> { |
192 | self[..].partial_cmp(&other[..]) |
193 | } |
194 | } |
195 | |
196 | #[allow(clippy::redundant_slicing)] |
197 | #[allow(clippy::extra_unused_lifetimes)] |
198 | impl<'a, 'b> PartialOrd<$lhs> for $rhs { |
199 | #[inline] |
200 | fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> { |
201 | self[..].partial_cmp(&other[..]) |
202 | } |
203 | } |
204 | }; |
205 | } |
206 | |
207 | impl_cmp!(Bytes, [u8]); |
208 | impl_cmp!(Bytes, &'a [u8]); |
209 | impl_cmp!(&'a Bytes, [u8]); |
210 | impl_cmp!(Bytes, Vec<u8>); |
211 | impl_cmp!(&'a Bytes, Vec<u8>); |
212 | |
213 | impl Hash for Bytes { |
214 | #[inline ] |
215 | fn hash<H: Hasher>(&self, state: &mut H) { |
216 | self.len().hash(state); |
217 | Hash::hash_slice(self, state) |
218 | } |
219 | } |
220 | |
221 | #[cfg (test)] |
222 | mod tests { |
223 | use std::collections::HashSet; |
224 | |
225 | use super::*; |
226 | |
227 | #[test ] |
228 | fn eq() { |
229 | let abc: &[u8] = b"abc" ; |
230 | let def: &[u8] = b"def" ; |
231 | let a1 = Bytes::from(abc); |
232 | let a2 = Bytes::from(abc); |
233 | let d = Bytes::from(def); |
234 | assert_eq!(a1, a2); |
235 | assert_eq!(def, d); |
236 | assert_ne!(a1, d); |
237 | assert_ne!(a1, def); |
238 | } |
239 | |
240 | #[test ] |
241 | fn ord() { |
242 | let abc: &[u8] = b"abc" ; |
243 | let def: &[u8] = b"def" ; |
244 | let a = Bytes::from(abc); |
245 | let d = Bytes::from(def); |
246 | assert!(a < d); |
247 | assert!(a < def); |
248 | assert!(abc < d); |
249 | assert!(d > a); |
250 | assert!(d > abc); |
251 | assert!(def > a); |
252 | } |
253 | |
254 | #[test ] |
255 | fn hash() { |
256 | let b1 = Bytes::from(b"this is a test" ); |
257 | let b2 = Bytes::from(b"this is a test" ); |
258 | let b3 = Bytes::from(b"test" ); |
259 | let mut set = HashSet::new(); |
260 | set.insert(b1); |
261 | assert!(set.contains(&b2)); |
262 | assert!(!set.contains(&b3)); |
263 | } |
264 | |
265 | #[test ] |
266 | fn from_static() { |
267 | let b1 = Bytes::from_static(b"this is a test" ); |
268 | let b2 = Bytes::from(b"this is a test" ); |
269 | assert_eq!(b1, b2); |
270 | } |
271 | |
272 | #[test ] |
273 | fn from_owned() { |
274 | let b = Bytes::from_owned(vec![1, 2, 3]); |
275 | assert_eq!(b, [1u8, 2u8, 3u8].as_ref()); |
276 | } |
277 | } |
278 | |