Skip to content

Commit

Permalink
Merge pull request #446 from madsmtm/refactor-mutable
Browse files Browse the repository at this point in the history
Refactor `icrate` mutable counterpart files
  • Loading branch information
madsmtm authored May 8, 2023
2 parents de14f52 + 5d78a92 commit f37bb84
Show file tree
Hide file tree
Showing 16 changed files with 1,115 additions and 1,123 deletions.
368 changes: 276 additions & 92 deletions crates/icrate/src/Foundation/additions/array.rs

Large diffs are not rendered by default.

69 changes: 41 additions & 28 deletions crates/icrate/src/Foundation/additions/attributed_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,45 @@ unsafe impl Send for NSAttributedString {}
impl UnwindSafe for NSAttributedString {}
impl RefUnwindSafe for NSAttributedString {}

extern_methods!(
/// Creating attributed strings.
unsafe impl NSAttributedString {
/// Creates a new attributed string from the given string and attributes.
///
/// The attributes are associated with every UTF-16 code unit in the
/// string.
///
/// # Safety
///
/// The attributes must be valid.
#[doc(alias = "initWithString:")]
#[cfg(feature = "Foundation_NSDictionary")]
#[cfg(feature = "Foundation_NSString")]
pub unsafe fn new_with_attributes(
string: &Foundation::NSString,
attributes: &Foundation::NSDictionary<NSAttributedStringKey, Object>,
) -> Id<Self> {
unsafe { Self::initWithString_attributes(Self::alloc(), string, Some(attributes)) }
}

/// Creates a new attributed string without any attributes.
#[doc(alias = "initWithString:")]
#[cfg(feature = "Foundation_NSString")]
pub fn from_nsstring(string: &Foundation::NSString) -> Id<Self> {
Self::initWithString(Self::alloc(), string)
}
impl NSAttributedString {
/// Creates a new attributed string from the given string and attributes.
///
/// The attributes are associated with every UTF-16 code unit in the
/// string.
///
/// # Safety
///
/// The attributes must be valid.
#[doc(alias = "initWithString:")]
#[cfg(feature = "Foundation_NSDictionary")]
#[cfg(feature = "Foundation_NSString")]
pub unsafe fn new_with_attributes(
string: &Foundation::NSString,
attributes: &Foundation::NSDictionary<NSAttributedStringKey, Object>,
) -> Id<Self> {
unsafe { Self::initWithString_attributes(Self::alloc(), string, Some(attributes)) }
}
);

/// Creates a new attributed string without any attributes.
#[doc(alias = "initWithString:")]
#[cfg(feature = "Foundation_NSString")]
pub fn from_nsstring(string: &Foundation::NSString) -> Id<Self> {
Self::initWithString(Self::alloc(), string)
}
}

#[cfg(feature = "Foundation_NSMutableAttributedString")]
impl Foundation::NSMutableAttributedString {
// TODO: new_with_attributes

#[doc(alias = "initWithString:")]
#[cfg(feature = "Foundation_NSString")]
pub fn from_nsstring(string: &Foundation::NSString) -> Id<Self> {
Self::initWithString(Self::alloc(), string)
}

#[doc(alias = "initWithAttributedString:")]
pub fn from_attributed_nsstring(attributed_string: &NSAttributedString) -> Id<Self> {
Self::initWithAttributedString(Self::alloc(), attributed_string)
}
}
186 changes: 172 additions & 14 deletions crates/icrate/src/Foundation/additions/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
use alloc::vec::Vec;
use core::fmt;
use core::ops::Index;
#[cfg(feature = "Foundation_NSMutableData")]
use core::ops::{IndexMut, Range};
use core::panic::{RefUnwindSafe, UnwindSafe};
use core::slice::{self, SliceIndex};

use crate::common::*;
#[cfg(feature = "Foundation_NSMutableData")]
use crate::Foundation::NSMutableData;
use crate::Foundation::{self, NSData};

// SAFETY: `NSData` is immutable and `NSMutableData` can only be mutated from
Expand All @@ -17,7 +21,6 @@ unsafe impl Send for NSData {}
impl UnwindSafe for NSData {}
impl RefUnwindSafe for NSData {}

/// Creation methods.
impl NSData {
pub fn with_bytes(bytes: &[u8]) -> Id<Self> {
let bytes_ptr = bytes.as_ptr() as *mut c_void;
Expand All @@ -34,15 +37,29 @@ impl NSData {
//
// NSMutableData does not have this problem.
#[cfg(feature = "gnustep-1-7")]
let cls = objc2::class!(NSDataWithDeallocatorBlock);
let obj = unsafe { objc2::msg_send_id![objc2::class!(NSDataWithDeallocatorBlock), alloc] };
#[cfg(not(feature = "gnustep-1-7"))]
let cls = Self::class();
let obj = Self::alloc();

unsafe { Id::cast(with_vec(cls, bytes)) }
unsafe { with_vec(obj, bytes) }
}
}

#[cfg(feature = "Foundation_NSMutableData")]
impl NSMutableData {
pub fn with_bytes(bytes: &[u8]) -> Id<Self> {
let bytes_ptr = bytes.as_ptr() as *mut c_void;
// SAFETY: Same as `NSData::with_bytes`
unsafe { Self::initWithBytes_length(Self::alloc(), bytes_ptr, bytes.len()) }
}

#[cfg(feature = "block")]
pub fn from_vec(bytes: Vec<u8>) -> Id<Self> {
// SAFETY: Same as `NSData::from_vec`
unsafe { with_vec(Self::alloc(), bytes) }
}
}

/// Accessor methods.
impl NSData {
pub fn len(&self) -> usize {
self.length()
Expand All @@ -53,15 +70,54 @@ impl NSData {
}

pub fn bytes(&self) -> &[u8] {
let ptr = self.bytes_raw();
let ptr: *const u8 = ptr.cast();
// The bytes pointer may be null for length zero
if ptr.is_null() {
if let Some(ptr) = self.bytes_raw() {
let ptr: *const u8 = ptr.as_ptr().cast();
// SAFETY: The pointer is checked to not be NULL, and since we're
// working with raw bytes (`u8`), the alignment is also correct.
unsafe { slice::from_raw_parts(ptr, self.len()) }
} else {
// The bytes pointer may be null for length zero on GNUStep
&[]
}
}
}

#[cfg(feature = "Foundation_NSMutableData")]
impl NSMutableData {
#[doc(alias = "mutableBytes")]
pub fn bytes_mut(&mut self) -> &mut [u8] {
if let Some(ptr) = self.mutable_bytes_raw() {
let ptr: *mut u8 = ptr.as_ptr().cast();
// SAFETY: Same as `NSData::bytes`, with the addition that a
// mutable slice is safe, since we take `&mut NSMutableData`.
unsafe { slice::from_raw_parts_mut(ptr, self.len()) }
} else {
unsafe { slice::from_raw_parts(ptr, self.len()) }
&mut []
}
}

#[doc(alias = "appendBytes:length:")]
pub fn extend_from_slice(&mut self, bytes: &[u8]) {
let bytes_ptr: NonNull<c_void> = NonNull::new(bytes.as_ptr() as *mut u8).unwrap().cast();
unsafe { self.appendBytes_length(bytes_ptr, bytes.len()) }
}

pub fn push(&mut self, byte: u8) {
self.extend_from_slice(&[byte])
}

#[doc(alias = "replaceBytesInRange:withBytes:length:")]
pub fn replace_range(&mut self, range: Range<usize>, bytes: &[u8]) {
// No need to verify the length of the range here,
// `replaceBytesInRange:` just zero-fills if out of bounds.
let ptr = bytes.as_ptr() as *mut c_void;
unsafe { self.replaceBytesInRange_withBytes_length(range.into(), ptr, bytes.len()) }
}

pub fn set_bytes(&mut self, bytes: &[u8]) {
let len = self.len();
self.replace_range(0..len, bytes);
}
}

impl AsRef<[u8]> for NSData {
Expand All @@ -70,6 +126,20 @@ impl AsRef<[u8]> for NSData {
}
}

#[cfg(feature = "Foundation_NSMutableData")]
impl AsRef<[u8]> for NSMutableData {
fn as_ref(&self) -> &[u8] {
self.bytes()
}
}

#[cfg(feature = "Foundation_NSMutableData")]
impl AsMut<[u8]> for NSMutableData {
fn as_mut(&mut self) -> &mut [u8] {
self.bytes_mut()
}
}

// Note: We don't implement `Borrow<[u8]>` since we can't guarantee that `Eq`,
// `Ord` and `Hash` are equal for `NSData` vs. `[u8]`!

Expand All @@ -83,6 +153,24 @@ impl<I: SliceIndex<[u8]>> Index<I> for NSData {
}
}

#[cfg(feature = "Foundation_NSMutableData")]
impl<I: SliceIndex<[u8]>> Index<I> for NSMutableData {
type Output = I::Output;

#[inline]
fn index(&self, index: I) -> &Self::Output {
Index::index(self.bytes(), index)
}
}

#[cfg(feature = "Foundation_NSMutableData")]
impl<I: SliceIndex<[u8]>> IndexMut<I> for NSMutableData {
#[inline]
fn index_mut(&mut self, index: I) -> &mut Self::Output {
IndexMut::index_mut(self.bytes_mut(), index)
}
}

impl fmt::Debug for NSData {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Expand All @@ -100,12 +188,82 @@ impl<'a> IntoIterator for &'a NSData {
}
}

#[cfg(feature = "Foundation_NSMutableData")]
impl<'a> IntoIterator for &'a NSMutableData {
type Item = &'a u8;
type IntoIter = core::slice::Iter<'a, u8>;

fn into_iter(self) -> Self::IntoIter {
self.bytes().iter()
}
}

#[cfg(feature = "Foundation_NSMutableData")]
impl<'a> IntoIterator for &'a mut NSMutableData {
type Item = &'a mut u8;
type IntoIter = core::slice::IterMut<'a, u8>;

fn into_iter(self) -> Self::IntoIter {
self.bytes_mut().iter_mut()
}
}

#[cfg(feature = "Foundation_NSMutableData")]
impl Extend<u8> for NSMutableData {
/// You should use [`extend_from_slice`] whenever possible, it is more
/// performant.
///
/// [`extend_from_slice`]: Self::extend_from_slice
fn extend<T: IntoIterator<Item = u8>>(&mut self, iter: T) {
let iterator = iter.into_iter();
iterator.for_each(move |item| self.push(item));
}
}

// Vec also has this impl
#[cfg(feature = "Foundation_NSMutableData")]
impl<'a> Extend<&'a u8> for NSMutableData {
fn extend<T: IntoIterator<Item = &'a u8>>(&mut self, iter: T) {
let iterator = iter.into_iter();
iterator.for_each(move |item| self.push(*item));
}
}

#[cfg(feature = "Foundation_NSMutableData")]
impl std::io::Write for NSMutableData {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.extend_from_slice(buf);
Ok(buf.len())
}

fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
self.extend_from_slice(buf);
Ok(())
}

fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}

// #[cfg(feature = "Foundation_NSMutableData")]
// impl FromIterator<u8> for Id<NSMutableData> {
// fn from_iter<T: IntoIterator<Item = u8>>(iter: T) -> Self {
// let iter = iter.into_iter();
// let (lower, _) = iter.size_hint();
// let data = Self::with_capacity(lower);
// for item in iter {
// data.push(item);
// }
// data
// }
// }

#[cfg(feature = "block")]
pub(crate) unsafe fn with_vec(cls: &Class, bytes: Vec<u8>) -> Id<Object> {
unsafe fn with_vec<T: Message>(obj: Option<Allocated<T>>, bytes: Vec<u8>) -> Id<T> {
use core::mem::ManuallyDrop;

use block2::{Block, ConcreteBlock};
use objc2::msg_send_id;

let capacity = bytes.capacity();

Expand All @@ -120,8 +278,8 @@ pub(crate) unsafe fn with_vec(cls: &Class, bytes: Vec<u8>) -> Id<Object> {
let bytes_ptr: *mut c_void = bytes.as_mut_ptr().cast();

unsafe {
msg_send_id![
msg_send_id![cls, alloc],
objc2::msg_send_id![
obj,
initWithBytesNoCopy: bytes_ptr,
length: bytes.len(),
deallocator: dealloc,
Expand Down
Loading

0 comments on commit f37bb84

Please sign in to comment.