Skip to content

Commit

Permalink
Improve NSMutableData
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Oct 31, 2021
1 parent 063d612 commit 08f7e6d
Showing 1 changed file with 56 additions and 40 deletions.
96 changes: 56 additions & 40 deletions objc2_foundation/src/data.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -43,19 +43,19 @@ pub unsafe trait INSData: INSObject {

#[cfg(feature = "block")]
fn from_vec(bytes: Vec<u8>) -> Id<Self, Self::Ownership> {
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);
});
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.
Expand All @@ -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))
}
}
Expand All @@ -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<usize>, 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(),
]
}
}

Expand All @@ -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};
Expand All @@ -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]
Expand All @@ -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")]
Expand Down

0 comments on commit 08f7e6d

Please sign in to comment.