diff --git a/core-foundation-sys/src/data.rs b/core-foundation-sys/src/data.rs index 7a62c9b82..e5ed0dc9f 100644 --- a/core-foundation-sys/src/data.rs +++ b/core-foundation-sys/src/data.rs @@ -27,6 +27,12 @@ extern { pub fn CFDataGetBytePtr(theData: CFDataRef) -> *const u8; pub fn CFDataGetBytes(theData: CFDataRef, range: CFRange, buffer: *mut u8); pub fn CFDataGetLength(theData: CFDataRef) -> CFIndex; + pub fn CFDataCreateWithBytesNoCopy( + allocator: CFAllocatorRef, + bytes: *const u8, + length: CFIndex, + allocator: CFAllocatorRef, + ) -> CFDataRef; pub fn CFDataGetTypeID() -> CFTypeID; } diff --git a/core-foundation/src/data.rs b/core-foundation/src/data.rs index 2b5010a52..c510c7434 100644 --- a/core-foundation/src/data.rs +++ b/core-foundation/src/data.rs @@ -14,6 +14,8 @@ use core_foundation_sys::base::CFIndex; use core_foundation_sys::base::{kCFAllocatorDefault}; use std::ops::Deref; use std::slice; +use std::sync::Arc; + use base::{CFIndexConvertible, TCFType}; @@ -26,6 +28,7 @@ impl_TCFType!(CFData, CFDataRef, CFDataGetTypeID); impl_CFTypeDescription!(CFData); impl CFData { + /// Creates a CFData around a copy `buffer` pub fn from_buffer(buffer: &[u8]) -> CFData { unsafe { let data_ref = CFDataCreate(kCFAllocatorDefault, @@ -35,6 +38,41 @@ impl CFData { } } + /// Creates a CFData referencing `buffer` without creating a copy + pub fn from_arc + Sync + Send>(buffer: Arc) -> Self { + use std::os::raw::c_void; + use crate::base::{CFAllocator, CFAllocatorContext}; + + unsafe { + let ptr = (*buffer).as_ref().as_ptr() as *const _; + let len = (*buffer).as_ref().len().to_CFIndex(); + let info = Arc::into_raw(buffer) as *mut c_void; + + extern "C" fn deallocate(_: *mut c_void, info: *mut c_void) { + unsafe { + drop(Arc::from_raw(info as *mut T)); + } + } + + // Use a separate allocator for each allocation because + // we need `info` to do the deallocation vs. `ptr` + let allocator = CFAllocator::new(CFAllocatorContext { + info, + version: 0, + retain: None, + reallocate: None, + release: None, + copyDescription: None, + allocate: None, + deallocate: Some(deallocate::), + preferredSize: None, + }); + let data_ref = + CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, ptr, len, allocator.as_CFTypeRef()); + TCFType::wrap_under_create_rule(data_ref) + } + } + /// Returns a pointer to the underlying bytes in this data. Note that this byte buffer is /// read-only. #[inline] @@ -61,3 +99,46 @@ impl Deref for CFData { self.bytes() } } + +#[cfg(test)] +mod test { + use super::CFData; + use std::sync::Arc; + + #[test] + fn test_data_provider() { + let l = vec![5]; + CFData::from_arc(Arc::new(l)); + + let l = vec![5]; + CFData::from_arc(Arc::new(l.into_boxed_slice())); + + // Make sure the buffer is actually dropped + use std::sync::atomic::{AtomicBool, Ordering::SeqCst}; + struct VecWrapper { + inner: Vec, + dropped: Arc, + } + + impl Drop for VecWrapper { + fn drop(&mut self) { + self.dropped.store(true, SeqCst) + } + } + + impl std::convert::AsRef<[u8]> for VecWrapper { + fn as_ref(&self) -> &[u8] { + &self.inner + } + } + + let dropped = Arc::new(AtomicBool::default()); + let l = Arc::new(VecWrapper {inner: vec![5], dropped: dropped.clone() }); + let m = l.clone(); + let dp = CFData::from_arc(l); + drop(m); + assert!(!dropped.load(SeqCst)); + drop(dp); + assert!(dropped.load(SeqCst)) + } +}