rkyv/
ffi.rs

1//! Archived versions of FFI types.
2
3use crate::{ser::Serializer, ArchiveUnsized, MetadataResolver, RelPtr, SerializeUnsized};
4use core::{
5    borrow::Borrow,
6    cmp, fmt, hash,
7    ops::{Deref, Index, RangeFull},
8    pin::Pin,
9};
10use std::ffi::CStr;
11
12/// An archived [`CString`](std::ffi::CString).
13///
14/// Uses a [`RelPtr`] to a `CStr` under the hood.
15#[repr(transparent)]
16pub struct ArchivedCString(RelPtr<CStr>);
17
18impl ArchivedCString {
19    /// Returns the contents of this CString as a slice of bytes.
20    ///
21    /// The returned slice does **not** contain the trailing nul terminator, and it is guaranteed to
22    /// not have any interior nul bytes. If you need the nul terminator, use
23    /// [`as_bytes_with_nul`][ArchivedCString::as_bytes_with_nul()] instead.
24    #[inline]
25    pub fn as_bytes(&self) -> &[u8] {
26        self.as_c_str().to_bytes()
27    }
28
29    /// Equivalent to [`as_bytes`][ArchivedCString::as_bytes()] except that the returned slice
30    /// includes the trailing nul terminator.
31    #[inline]
32    pub fn as_bytes_with_nul(&self) -> &[u8] {
33        self.as_c_str().to_bytes_with_nul()
34    }
35
36    /// Extracts a `CStr` slice containing the entire string.
37    #[inline]
38    pub fn as_c_str(&self) -> &CStr {
39        unsafe { &*self.0.as_ptr() }
40    }
41
42    /// Extracts a pinned mutable `CStr` slice containing the entire string.
43    #[inline]
44    pub fn pin_mut_c_str(self: Pin<&mut Self>) -> Pin<&mut CStr> {
45        unsafe { self.map_unchecked_mut(|s| &mut *s.0.as_mut_ptr()) }
46    }
47
48    /// Resolves an archived C string from the given C string and parameters.
49    ///
50    /// # Safety
51    ///
52    /// - `pos` must be the position of `out` within the archive
53    /// - `resolver` must be the result of serializing a C string
54    #[inline]
55    pub unsafe fn resolve_from_c_str(
56        c_str: &CStr,
57        pos: usize,
58        resolver: CStringResolver,
59        out: *mut Self,
60    ) {
61        let (fp, fo) = out_field!(out.0);
62        // metadata_resolver is guaranteed to be (), but it's better to be explicit about it
63        #[allow(clippy::unit_arg)]
64        c_str.resolve_unsized(pos + fp, resolver.pos, resolver.metadata_resolver, fo);
65    }
66
67    /// Serializes a C string.
68    #[inline]
69    pub fn serialize_from_c_str<S: Serializer + ?Sized>(
70        c_str: &CStr,
71        serializer: &mut S,
72    ) -> Result<CStringResolver, S::Error> {
73        Ok(CStringResolver {
74            pos: c_str.serialize_unsized(serializer)?,
75            metadata_resolver: c_str.serialize_metadata(serializer)?,
76        })
77    }
78}
79
80impl AsRef<CStr> for ArchivedCString {
81    fn as_ref(&self) -> &CStr {
82        self.as_c_str()
83    }
84}
85
86impl Borrow<CStr> for ArchivedCString {
87    #[inline]
88    fn borrow(&self) -> &CStr {
89        self.as_c_str()
90    }
91}
92
93impl fmt::Debug for ArchivedCString {
94    #[inline]
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        self.as_c_str().fmt(f)
97    }
98}
99
100impl Deref for ArchivedCString {
101    type Target = CStr;
102
103    #[inline]
104    fn deref(&self) -> &Self::Target {
105        self.as_c_str()
106    }
107}
108
109impl Eq for ArchivedCString {}
110
111impl hash::Hash for ArchivedCString {
112    #[inline]
113    fn hash<H: hash::Hasher>(&self, state: &mut H) {
114        self.as_bytes_with_nul().hash(state);
115    }
116}
117
118impl Index<RangeFull> for ArchivedCString {
119    type Output = CStr;
120
121    #[inline]
122    fn index(&self, _: RangeFull) -> &Self::Output {
123        self.as_c_str()
124    }
125}
126
127impl Ord for ArchivedCString {
128    #[inline]
129    fn cmp(&self, other: &Self) -> cmp::Ordering {
130        self.as_bytes().cmp(other.as_bytes())
131    }
132}
133
134impl PartialEq for ArchivedCString {
135    #[inline]
136    fn eq(&self, other: &Self) -> bool {
137        self.as_bytes() == other.as_bytes()
138    }
139}
140
141impl PartialEq<&CStr> for ArchivedCString {
142    #[inline]
143    fn eq(&self, other: &&CStr) -> bool {
144        PartialEq::eq(self.as_c_str(), other)
145    }
146}
147
148impl PartialEq<ArchivedCString> for &CStr {
149    #[inline]
150    fn eq(&self, other: &ArchivedCString) -> bool {
151        PartialEq::eq(other.as_c_str(), self)
152    }
153}
154
155impl PartialOrd for ArchivedCString {
156    #[inline]
157    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
158        Some(self.cmp(other))
159    }
160}
161
162/// The resolver for `CString`.
163pub struct CStringResolver {
164    pos: usize,
165    metadata_resolver: MetadataResolver<CStr>,
166}
167
168#[cfg(feature = "validation")]
169const _: () = {
170    use crate::validation::{
171        owned::{CheckOwnedPointerError, OwnedPointerError},
172        ArchiveContext,
173    };
174    use bytecheck::{CheckBytes, Error};
175
176    impl<C: ArchiveContext + ?Sized> CheckBytes<C> for ArchivedCString
177    where
178        C::Error: Error,
179    {
180        type Error = CheckOwnedPointerError<CStr, C>;
181
182        #[inline]
183        unsafe fn check_bytes<'a>(
184            value: *const Self,
185            context: &mut C,
186        ) -> Result<&'a Self, Self::Error> {
187            let rel_ptr = RelPtr::<CStr>::manual_check_bytes(value.cast(), context)
188                .map_err(OwnedPointerError::PointerCheckBytesError)?;
189            let ptr = context
190                .check_subtree_rel_ptr(rel_ptr)
191                .map_err(OwnedPointerError::ContextError)?;
192
193            let range = context
194                .push_prefix_subtree(ptr)
195                .map_err(OwnedPointerError::ContextError)?;
196            CStr::check_bytes(ptr, context).map_err(OwnedPointerError::ValueCheckBytesError)?;
197            context
198                .pop_prefix_range(range)
199                .map_err(OwnedPointerError::ContextError)?;
200
201            Ok(&*value)
202        }
203    }
204};