Skip to content

Commit 4c52fbd

Browse files
authored
Auto merge of #446 - jrmuizel:cfdata-arc, r=jdm
Add CFData::from_arc This allows creating CFData around data without creating a copy.
2 parents f419d4b + ce17c9a commit 4c52fbd

File tree

2 files changed

+87
-0
lines changed

2 files changed

+87
-0
lines changed

core-foundation-sys/src/data.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ extern {
2727
pub fn CFDataGetBytePtr(theData: CFDataRef) -> *const u8;
2828
pub fn CFDataGetBytes(theData: CFDataRef, range: CFRange, buffer: *mut u8);
2929
pub fn CFDataGetLength(theData: CFDataRef) -> CFIndex;
30+
pub fn CFDataCreateWithBytesNoCopy(
31+
allocator: CFAllocatorRef,
32+
bytes: *const u8,
33+
length: CFIndex,
34+
allocator: CFAllocatorRef,
35+
) -> CFDataRef;
3036

3137
pub fn CFDataGetTypeID() -> CFTypeID;
3238
}

core-foundation/src/data.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ use core_foundation_sys::base::CFIndex;
1414
use core_foundation_sys::base::{kCFAllocatorDefault};
1515
use std::ops::Deref;
1616
use std::slice;
17+
use std::sync::Arc;
18+
1719

1820
use base::{CFIndexConvertible, TCFType};
1921

@@ -26,6 +28,7 @@ impl_TCFType!(CFData, CFDataRef, CFDataGetTypeID);
2628
impl_CFTypeDescription!(CFData);
2729

2830
impl CFData {
31+
/// Creates a CFData around a copy `buffer`
2932
pub fn from_buffer(buffer: &[u8]) -> CFData {
3033
unsafe {
3134
let data_ref = CFDataCreate(kCFAllocatorDefault,
@@ -35,6 +38,41 @@ impl CFData {
3538
}
3639
}
3740

41+
/// Creates a CFData referencing `buffer` without creating a copy
42+
pub fn from_arc<T: AsRef<[u8]> + Sync + Send>(buffer: Arc<T>) -> Self {
43+
use std::os::raw::c_void;
44+
use crate::base::{CFAllocator, CFAllocatorContext};
45+
46+
unsafe {
47+
let ptr = (*buffer).as_ref().as_ptr() as *const _;
48+
let len = (*buffer).as_ref().len().to_CFIndex();
49+
let info = Arc::into_raw(buffer) as *mut c_void;
50+
51+
extern "C" fn deallocate<T>(_: *mut c_void, info: *mut c_void) {
52+
unsafe {
53+
drop(Arc::from_raw(info as *mut T));
54+
}
55+
}
56+
57+
// Use a separate allocator for each allocation because
58+
// we need `info` to do the deallocation vs. `ptr`
59+
let allocator = CFAllocator::new(CFAllocatorContext {
60+
info,
61+
version: 0,
62+
retain: None,
63+
reallocate: None,
64+
release: None,
65+
copyDescription: None,
66+
allocate: None,
67+
deallocate: Some(deallocate::<T>),
68+
preferredSize: None,
69+
});
70+
let data_ref =
71+
CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, ptr, len, allocator.as_CFTypeRef());
72+
TCFType::wrap_under_create_rule(data_ref)
73+
}
74+
}
75+
3876
/// Returns a pointer to the underlying bytes in this data. Note that this byte buffer is
3977
/// read-only.
4078
#[inline]
@@ -61,3 +99,46 @@ impl Deref for CFData {
6199
self.bytes()
62100
}
63101
}
102+
103+
#[cfg(test)]
104+
mod test {
105+
use super::CFData;
106+
use std::sync::Arc;
107+
108+
#[test]
109+
fn test_data_provider() {
110+
let l = vec![5];
111+
CFData::from_arc(Arc::new(l));
112+
113+
let l = vec![5];
114+
CFData::from_arc(Arc::new(l.into_boxed_slice()));
115+
116+
// Make sure the buffer is actually dropped
117+
use std::sync::atomic::{AtomicBool, Ordering::SeqCst};
118+
struct VecWrapper {
119+
inner: Vec<u8>,
120+
dropped: Arc<AtomicBool>,
121+
}
122+
123+
impl Drop for VecWrapper {
124+
fn drop(&mut self) {
125+
self.dropped.store(true, SeqCst)
126+
}
127+
}
128+
129+
impl std::convert::AsRef<[u8]> for VecWrapper {
130+
fn as_ref(&self) -> &[u8] {
131+
&self.inner
132+
}
133+
}
134+
135+
let dropped = Arc::new(AtomicBool::default());
136+
let l = Arc::new(VecWrapper {inner: vec![5], dropped: dropped.clone() });
137+
let m = l.clone();
138+
let dp = CFData::from_arc(l);
139+
drop(m);
140+
assert!(!dropped.load(SeqCst));
141+
drop(dp);
142+
assert!(dropped.load(SeqCst))
143+
}
144+
}

0 commit comments

Comments
 (0)