// Take a look at the license at the top of the repository in the LICENSE file. use std::{ borrow::Borrow, cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd}, fmt, hash::{Hash, Hasher}, ops::Deref, slice, }; use crate::translate::*; wrapper! { // rustdoc-stripper-ignore-next /// A shared immutable byte slice (the equivalent of `Rc<[u8]>`). /// /// `From` implementations that take references (e.g. `&[u8]`) copy the /// data. The `from_static` constructor avoids copying static data. /// /// ``` /// use glib::Bytes; /// /// let v = vec![1, 2, 3]; /// let b = Bytes::from(&v); /// assert_eq!(v, b); /// /// let s = b"xyz"; /// let b = Bytes::from_static(s); /// assert_eq!(&s[..], b); /// ``` #[doc(alias = "GBytes")] pub struct Bytes(Shared); match fn { ref => |ptr| ffi::g_bytes_ref(ptr), unref => |ptr| ffi::g_bytes_unref(ptr), type_ => || ffi::g_bytes_get_type(), } } impl Bytes { // rustdoc-stripper-ignore-next /// Copies `data` into a new shared slice. #[doc(alias = "g_bytes_new")] #[inline] fn new>(data: T) -> Bytes { let data = data.as_ref(); unsafe { from_glib_full(ffi::g_bytes_new(data.as_ptr() as *const _, data.len())) } } // rustdoc-stripper-ignore-next /// Creates a view into static `data` without copying. #[doc(alias = "g_bytes_new_static")] #[inline] pub fn from_static(data: &'static [u8]) -> Bytes { unsafe { from_glib_full(ffi::g_bytes_new_static( data.as_ptr() as *const _, data.len(), )) } } // rustdoc-stripper-ignore-next /// Takes ownership of `data` and creates a new `Bytes` without copying. pub fn from_owned + Send + 'static>(data: T) -> Bytes { let data: Box = Box::new(data); let (size, data_ptr) = { let data = (*data).as_ref(); (data.len(), data.as_ptr()) }; unsafe extern "C" fn drop_box + Send + 'static>(b: ffi::gpointer) { let _: Box = Box::from_raw(b as *mut _); } unsafe { from_glib_full(ffi::g_bytes_new_with_free_func( data_ptr as *const _, size, Some(drop_box::), Box::into_raw(data) as *mut _, )) } } } unsafe impl Send for Bytes {} unsafe impl Sync for Bytes {} impl<'a, T: ?Sized + Borrow<[u8]> + 'a> From<&'a T> for Bytes { #[inline] fn from(value: &'a T) -> Bytes { Bytes::new(value.borrow()) } } impl fmt::Debug for Bytes { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Bytes") .field("ptr", &ToGlibPtr::<*const _>::to_glib_none(self).0) .field("data", &&self[..]) .finish() } } impl AsRef<[u8]> for Bytes { #[inline] fn as_ref(&self) -> &[u8] { self } } impl Deref for Bytes { type Target = [u8]; #[inline] fn deref(&self) -> &[u8] { unsafe { let mut len = 0; let ptr = ffi::g_bytes_get_data(self.to_glib_none().0, &mut len); if ptr.is_null() || len == 0 { &[] } else { slice::from_raw_parts(ptr as *const u8, len) } } } } impl PartialEq for Bytes { #[doc(alias = "g_bytes_equal")] #[inline] fn eq(&self, other: &Self) -> bool { unsafe { from_glib(ffi::g_bytes_equal( ToGlibPtr::<*const _>::to_glib_none(self).0 as *const _, ToGlibPtr::<*const _>::to_glib_none(other).0 as *const _, )) } } } impl Eq for Bytes {} impl PartialOrd for Bytes { #[inline] fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for Bytes { #[inline] fn cmp(&self, other: &Self) -> Ordering { unsafe { let ret = ffi::g_bytes_compare( ToGlibPtr::<*const _>::to_glib_none(self).0 as *const _, ToGlibPtr::<*const _>::to_glib_none(other).0 as *const _, ); ret.cmp(&0) } } } macro_rules! impl_cmp { ($lhs:ty, $rhs: ty) => { #[allow(clippy::redundant_slicing)] #[allow(clippy::extra_unused_lifetimes)] impl<'a, 'b> PartialEq<$rhs> for $lhs { #[inline] fn eq(&self, other: &$rhs) -> bool { self[..].eq(&other[..]) } } #[allow(clippy::redundant_slicing)] #[allow(clippy::extra_unused_lifetimes)] impl<'a, 'b> PartialEq<$lhs> for $rhs { #[inline] fn eq(&self, other: &$lhs) -> bool { self[..].eq(&other[..]) } } #[allow(clippy::redundant_slicing)] #[allow(clippy::extra_unused_lifetimes)] impl<'a, 'b> PartialOrd<$rhs> for $lhs { #[inline] fn partial_cmp(&self, other: &$rhs) -> Option { self[..].partial_cmp(&other[..]) } } #[allow(clippy::redundant_slicing)] #[allow(clippy::extra_unused_lifetimes)] impl<'a, 'b> PartialOrd<$lhs> for $rhs { #[inline] fn partial_cmp(&self, other: &$lhs) -> Option { self[..].partial_cmp(&other[..]) } } }; } impl_cmp!(Bytes, [u8]); impl_cmp!(Bytes, &'a [u8]); impl_cmp!(&'a Bytes, [u8]); impl_cmp!(Bytes, Vec); impl_cmp!(&'a Bytes, Vec); impl Hash for Bytes { #[inline] fn hash(&self, state: &mut H) { self.len().hash(state); Hash::hash_slice(self, state) } } #[cfg(test)] mod tests { use std::collections::HashSet; use super::*; #[test] fn eq() { let abc: &[u8] = b"abc"; let def: &[u8] = b"def"; let a1 = Bytes::from(abc); let a2 = Bytes::from(abc); let d = Bytes::from(def); assert_eq!(a1, a2); assert_eq!(def, d); assert_ne!(a1, d); assert_ne!(a1, def); } #[test] fn ord() { let abc: &[u8] = b"abc"; let def: &[u8] = b"def"; let a = Bytes::from(abc); let d = Bytes::from(def); assert!(a < d); assert!(a < def); assert!(abc < d); assert!(d > a); assert!(d > abc); assert!(def > a); } #[test] fn hash() { let b1 = Bytes::from(b"this is a test"); let b2 = Bytes::from(b"this is a test"); let b3 = Bytes::from(b"test"); let mut set = HashSet::new(); set.insert(b1); assert!(set.contains(&b2)); assert!(!set.contains(&b3)); } #[test] fn from_static() { let b1 = Bytes::from_static(b"this is a test"); let b2 = Bytes::from(b"this is a test"); assert_eq!(b1, b2); } #[test] fn from_owned() { let b = Bytes::from_owned(vec![1, 2, 3]); assert_eq!(b, [1u8, 2u8, 3u8].as_ref()); } }