| // Copyright 2013 The Servo Project Developers. See the COPYRIGHT |
| // file at the top-level directory of this distribution. |
| // |
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| // option. This file may not be copied, modified, or distributed |
| // except according to those terms. |
| |
| //! Core Foundation byte buffers. |
| |
| pub use core_foundation_sys::data::*; |
| 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}; |
| |
| |
| declare_TCFType!{ |
| /// A byte buffer. |
| CFData, CFDataRef |
| } |
| 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, |
| buffer.as_ptr(), |
| buffer.len().to_CFIndex()); |
| TCFType::wrap_under_create_rule(data_ref) |
| } |
| } |
| |
| /// 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] |
| pub fn bytes<'a>(&'a self) -> &'a [u8] { |
| unsafe { |
| slice::from_raw_parts(CFDataGetBytePtr(self.0), self.len() as usize) |
| } |
| } |
| |
| /// Returns the length of this byte buffer. |
| #[inline] |
| pub fn len(&self) -> CFIndex { |
| unsafe { |
| CFDataGetLength(self.0) |
| } |
| } |
| } |
| |
| impl Deref for CFData { |
| type Target = [u8]; |
| |
| #[inline] |
| fn deref(&self) -> &[u8] { |
| 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)) |
| } |
| } |