diff --git a/objc2_foundation/src/data.rs b/objc2_foundation/src/data.rs index 263c61665..418163a7c 100644 --- a/objc2_foundation/src/data.rs +++ b/objc2_foundation/src/data.rs @@ -1,6 +1,6 @@ #[cfg(feature = "block")] use alloc::vec::Vec; -use core::ops::Range; +use core::ops::{Deref, DerefMut, Range}; use core::slice; use core::{ffi::c_void, ptr::NonNull}; @@ -43,9 +43,12 @@ pub unsafe trait INSData: INSObject { #[cfg(feature = "block")] fn from_vec(bytes: Vec) -> Id { + use core::mem::ManuallyDrop; + use objc2_block::{Block, ConcreteBlock}; let capacity = bytes.capacity(); + let dealloc = ConcreteBlock::new(move |bytes: *mut c_void, len: usize| unsafe { // Recreate the Vec and let it drop let _ = Vec::from_raw_parts(bytes as *mut u8, len, capacity); @@ -53,9 +56,6 @@ pub unsafe trait INSData: INSObject { let dealloc = dealloc.copy(); let dealloc: &Block<(*mut c_void, usize), ()> = &dealloc; - let mut bytes = bytes; - let bytes_ptr = bytes.as_mut_ptr() as *mut c_void; - // GNUStep's NSData `initWithBytesNoCopy:length:deallocator:` has a // bug; it forgets to assign the input buffer and length to the // instance before it swizzles to NSDataWithDeallocatorBlock. @@ -72,15 +72,17 @@ pub unsafe trait INSData: INSObject { }; #[cfg(not(gnustep))] let cls = Self::class(); + + let mut bytes = ManuallyDrop::new(bytes); + unsafe { let obj: *mut Self = msg_send![cls, alloc]; let obj: *mut Self = msg_send![ obj, - initWithBytesNoCopy: bytes_ptr, + initWithBytesNoCopy: bytes.as_mut_ptr() as *mut c_void, length: bytes.len(), deallocator: dealloc, ]; - core::mem::forget(bytes); Id::new(NonNull::new_unchecked(obj)) } } @@ -98,45 +100,45 @@ unsafe impl INSMutableCopying for NSData { type Output = NSMutableData; } +impl Deref for NSData { + type Target = [u8]; + + fn deref(&self) -> &[u8] { + self.bytes() + } +} + pub unsafe trait INSMutableData: INSData { fn bytes_mut(&mut self) -> &mut [u8] { let ptr: *mut c_void = unsafe { msg_send![self, mutableBytes] }; // The bytes pointer may be null for length zero - let (ptr, len) = if ptr.is_null() { - (0x1 as *mut u8, 0) + if ptr.is_null() { + &mut [] } else { - (ptr as *mut u8, self.len()) - }; - unsafe { slice::from_raw_parts_mut(ptr, len) } + unsafe { slice::from_raw_parts_mut(ptr as *mut u8, self.len()) } + } } + /// Expands with zeroes, or truncates the buffer. fn set_len(&mut self, len: usize) { - unsafe { - let _: () = msg_send![self, setLength: len]; - } + unsafe { msg_send![self, setLength: len] } } fn append(&mut self, bytes: &[u8]) { let bytes_ptr = bytes.as_ptr() as *const c_void; - unsafe { - let _: () = msg_send![ - self, - appendBytes: bytes_ptr, - length:bytes.len(), - ]; - } + unsafe { msg_send![self, appendBytes: bytes_ptr, length: bytes.len()] } } fn replace_range(&mut self, range: Range, bytes: &[u8]) { let range = NSRange::from(range); - let bytes_ptr = bytes.as_ptr() as *const c_void; + let ptr = bytes.as_ptr() as *const c_void; unsafe { - let _: () = msg_send![ + msg_send![ self, - replaceBytesInRange:range, - withBytes:bytes_ptr, - length:bytes.len(), - ]; + replaceBytesInRange: range, + withBytes: ptr, + length: bytes.len(), + ] } } @@ -160,6 +162,20 @@ unsafe impl INSMutableCopying for NSMutableData { type Output = NSMutableData; } +impl Deref for NSMutableData { + type Target = [u8]; + + fn deref(&self) -> &[u8] { + self.bytes() + } +} + +impl DerefMut for NSMutableData { + fn deref_mut(&mut self) -> &mut [u8] { + self.bytes_mut() + } +} + #[cfg(test)] mod tests { use super::{INSData, INSMutableData, NSData, NSMutableData}; @@ -171,8 +187,8 @@ mod tests { fn test_bytes() { let bytes = [3, 7, 16, 52, 112, 19]; let data = NSData::with_bytes(&bytes); - assert!(data.len() == bytes.len()); - assert!(data.bytes() == bytes); + assert_eq!(data.len(), bytes.len()); + assert_eq!(data.bytes(), bytes); } #[test] @@ -185,43 +201,43 @@ mod tests { fn test_bytes_mut() { let mut data = NSMutableData::with_bytes(&[7, 16]); data.bytes_mut()[0] = 3; - assert!(data.bytes() == [3, 16]); + assert_eq!(data.bytes(), [3, 16]); } #[test] fn test_set_len() { let mut data = NSMutableData::with_bytes(&[7, 16]); data.set_len(4); - assert!(data.len() == 4); - assert!(data.bytes() == [7, 16, 0, 0]); + assert_eq!(data.len(), 4); + assert_eq!(data.bytes(), [7, 16, 0, 0]); data.set_len(1); - assert!(data.len() == 1); - assert!(data.bytes() == [7]); + assert_eq!(data.len(), 1); + assert_eq!(data.bytes(), [7]); } #[test] fn test_append() { let mut data = NSMutableData::with_bytes(&[7, 16]); data.append(&[3, 52]); - assert!(data.len() == 4); - assert!(data.bytes() == [7, 16, 3, 52]); + assert_eq!(data.len(), 4); + assert_eq!(data.bytes(), [7, 16, 3, 52]); } #[test] fn test_replace() { let mut data = NSMutableData::with_bytes(&[7, 16]); data.replace_range(0..0, &[3]); - assert!(data.bytes() == [3, 7, 16]); + assert_eq!(data.bytes(), [3, 7, 16]); data.replace_range(1..2, &[52, 13]); - assert!(data.bytes() == [3, 52, 13, 16]); + assert_eq!(data.bytes(), [3, 52, 13, 16]); data.replace_range(2..4, &[6]); - assert!(data.bytes() == [3, 52, 6]); + assert_eq!(data.bytes(), [3, 52, 6]); data.set_bytes(&[8, 17]); - assert!(data.bytes() == [8, 17]); + assert_eq!(data.bytes(), [8, 17]); } #[cfg(feature = "block")]