objc2_core_foundation/
data.rs

1use core::slice;
2
3#[cfg(feature = "alloc")]
4use alloc::vec::Vec;
5
6use crate::{CFData, CFIndex};
7
8fn get_len(bytes: &[u8]) -> CFIndex {
9    // An allocation in Rust cannot be larger than isize::MAX, so this will
10    // never fail.
11    let len = bytes.len();
12    debug_assert!(len < CFIndex::MAX as usize);
13    len as CFIndex
14}
15
16impl CFData {
17    /// Creates a new `CFData` from a byte slice.
18    #[inline]
19    #[doc(alias = "CFDataCreate")]
20    pub fn from_bytes(bytes: &[u8]) -> crate::CFRetained<Self> {
21        let len = get_len(bytes);
22        unsafe { crate::CFData::new(None, bytes.as_ptr(), len) }.expect("failed creating CFData")
23    }
24
25    /// Alias for easier transition from the `core-foundation` crate.
26    #[inline]
27    #[deprecated = "renamed to CFData::from_bytes"]
28    pub fn from_buffer(bytes: &[u8]) -> crate::CFRetained<Self> {
29        Self::from_bytes(bytes)
30    }
31
32    /// Creates a new `CFData` from a `'static` byte slice.
33    ///
34    /// This may be slightly more efficient than [`CFData::from_bytes`], as it
35    /// may be able to re-use the existing buffer (since we know it won't be
36    /// deallocated).
37    #[inline]
38    #[doc(alias = "CFDataCreateWithBytesNoCopy")]
39    pub fn from_static_bytes(bytes: &'static [u8]) -> crate::CFRetained<Self> {
40        let len = get_len(bytes);
41        // SAFETY: Same as `CFString::from_static_str`.
42        unsafe { CFData::with_bytes_no_copy(None, bytes.as_ptr(), len, crate::kCFAllocatorNull) }
43            .expect("failed creating CFData")
44    }
45
46    /// The number of bytes contained by the `CFData`.
47    #[inline]
48    #[doc(alias = "CFDataGetLength")]
49    pub fn len(&self) -> usize {
50        self.length() as usize
51    }
52
53    /// Whether the `CFData` is empty.
54    #[inline]
55    pub fn is_empty(&self) -> bool {
56        self.len() == 0
57    }
58
59    /// The underlying bytes in the `CFData`.
60    ///
61    ///
62    /// # Safety
63    ///
64    /// The `CFData` must not be mutated for the lifetime of the returned
65    /// string. Consider using [`to_vec`] instead if this requirement is a bit
66    /// difficult to uphold.
67    ///
68    /// [`to_vec`]: Self::to_vec
69    #[inline]
70    #[doc(alias = "CFDataGetBytePtr")]
71    pub unsafe fn as_bytes_unchecked(&self) -> &[u8] {
72        let ptr = self.byte_ptr();
73        if !ptr.is_null() {
74            // SAFETY: The pointer is valid, and caller ensures that the
75            // `CFData` is not mutated for the lifetime of it.
76            //
77            // Same as
78            unsafe { slice::from_raw_parts(ptr, self.len()) }
79        } else {
80            // The bytes pointer may be null for length zero
81            &[]
82        }
83    }
84
85    /// Copy the contents of the `CFData` into a new [`Vec`].
86    #[cfg(feature = "alloc")]
87    #[doc(alias = "CFDataGetBytePtr")]
88    pub fn to_vec(&self) -> Vec<u8> {
89        // NOTE: We don't do `Vec::from`, as that will call the allocator
90        // while the buffer is active, and we don't know if that allocator
91        // uses a CFMutableData under the hood (though very theoretical).
92
93        let mut vec = Vec::with_capacity(self.len());
94        // SAFETY: We've pre-allocated the Vec, so it won't call the allocator
95        // while the byte slice is alive (and hence it won't ).
96        vec.extend_from_slice(unsafe { self.as_bytes_unchecked() });
97        vec
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn roundtrip() {
107        let data = CFData::from_bytes(&[1, 2, 3]);
108        assert_eq!(data.to_vec(), [1, 2, 3]);
109    }
110
111    #[test]
112    fn empty() {
113        let data = CFData::from_bytes(&[]);
114        assert!(data.is_empty());
115        assert_eq!(data.to_vec(), []);
116    }
117}