Add CFData::from_arc
This allows creating a zero-copy CFData wrapping existing data.
This commit is contained in:
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user