glib/
bytes.rs

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