1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{
4 borrow::Borrow,
5 cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd},
6 fmt,
7 hash::{Hash, Hasher},
8 ops::Deref,
9 slice,
10};
11
12use crate::translate::*;
13
14wrapper! {
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
42impl 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
89unsafe impl Send for Bytes {}
90unsafe impl Sync for Bytes {}
91
92impl<'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
99impl 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
108impl AsRef<[u8]> for Bytes {
109 #[inline]
110 fn as_ref(&self) -> &[u8] {
111 self
112 }
113}
114
115impl 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
132impl 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
145impl Eq for Bytes {}
146
147impl PartialOrd for Bytes {
148 #[inline]
149 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
150 Some(self.cmp(other))
151 }
152}
153
154impl 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
167macro_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
207impl_cmp!(Bytes, [u8]);
208impl_cmp!(Bytes, &'a [u8]);
209impl_cmp!(&'a Bytes, [u8]);
210impl_cmp!(Bytes, Vec<u8>);
211impl_cmp!(&'a Bytes, Vec<u8>);
212
213impl 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)]
222mod 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