Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions core-foundation-sys/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 81 additions & 0 deletions core-foundation/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand All @@ -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,
Expand All @@ -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]
Expand All @@ -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))
}
}