rkyv/
ffi.rs

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