Add CFData::from_arc

This allows creating a zero-copy CFData wrapping existing data.
This commit is contained in:
Jeff Muizelaar
2021-02-13 22:31:52 -05:00
parent 987123d00c
commit ce17c9aa59
2 changed files with 87 additions and 0 deletions
+6
View File
@@ -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;
}
+81
View File
@@ -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<T: AsRef<[u8]> + Sync + Send>(buffer: Arc<T>) -> 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<T>(_: *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::<T>),
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<u8>,
dropped: Arc<AtomicBool>,
}
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))
}
}