rkyv/string/
mod.rs

1//! Archived versions of string types.
2
3pub mod repr;
4
5use core::{
6    borrow::Borrow,
7    cmp,
8    error::Error,
9    fmt, hash,
10    ops::{
11        Deref, Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo,
12        RangeToInclusive,
13    },
14    str,
15};
16
17use munge::munge;
18use rancor::{fail, Fallible, Source};
19use repr::{ArchivedStringRepr, INLINE_CAPACITY};
20
21use crate::{
22    primitive::FixedUsize, seal::Seal, Place, Portable, SerializeUnsized,
23};
24
25/// An archived [`String`].
26///
27/// This has inline and out-of-line representations. Short strings will use the
28/// available space inside the structure to store the string, and long strings
29/// will store a [`RelPtr`](crate::RelPtr) to a `str` instead.
30#[repr(transparent)]
31#[cfg_attr(
32    feature = "bytecheck",
33    derive(bytecheck::CheckBytes),
34    bytecheck(verify)
35)]
36#[derive(Portable)]
37#[rkyv(crate)]
38pub struct ArchivedString {
39    repr: ArchivedStringRepr,
40}
41
42impl ArchivedString {
43    /// Extracts a string slice containing the entire `ArchivedString`.
44    #[inline]
45    pub fn as_str(&self) -> &str {
46        self.repr.as_str()
47    }
48
49    /// Extracts a sealed mutable string slice containing the entire
50    /// `ArchivedString`.
51    #[inline]
52    pub fn as_str_seal(this: Seal<'_, Self>) -> Seal<'_, str> {
53        munge!(let Self { repr } = this);
54        ArchivedStringRepr::as_str_seal(repr)
55    }
56
57    /// Resolves an archived string from a given `str`.
58    #[inline]
59    pub fn resolve_from_str(
60        value: &str,
61        resolver: StringResolver,
62        out: Place<Self>,
63    ) {
64        munge!(let ArchivedString { repr } = out);
65        if value.len() <= repr::INLINE_CAPACITY {
66            unsafe {
67                ArchivedStringRepr::emplace_inline(value, repr.ptr());
68            }
69        } else {
70            unsafe {
71                ArchivedStringRepr::emplace_out_of_line(
72                    value,
73                    resolver.pos as usize,
74                    repr,
75                );
76            }
77        }
78    }
79
80    /// Serializes an archived string from a given `str`.
81    pub fn serialize_from_str<S: Fallible + ?Sized>(
82        value: &str,
83        serializer: &mut S,
84    ) -> Result<StringResolver, S::Error>
85    where
86        S::Error: Source,
87        str: SerializeUnsized<S>,
88    {
89        if value.len() <= INLINE_CAPACITY {
90            Ok(StringResolver { pos: 0 })
91        } else if value.len() > repr::OUT_OF_LINE_CAPACITY {
92            #[derive(Debug)]
93            struct StringTooLongError;
94
95            impl fmt::Display for StringTooLongError {
96                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97                    write!(
98                        f,
99                        "String was too long for the archived representation",
100                    )
101                }
102            }
103
104            impl Error for StringTooLongError {}
105
106            fail!(StringTooLongError);
107        } else {
108            Ok(StringResolver {
109                pos: value.serialize_unsized(serializer)? as FixedUsize,
110            })
111        }
112    }
113}
114
115impl AsRef<str> for ArchivedString {
116    #[inline]
117    fn as_ref(&self) -> &str {
118        self.as_str()
119    }
120}
121
122impl Borrow<str> for ArchivedString {
123    #[inline]
124    fn borrow(&self) -> &str {
125        self.as_str()
126    }
127}
128
129impl fmt::Debug for ArchivedString {
130    #[inline]
131    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132        fmt::Debug::fmt(self.as_str(), f)
133    }
134}
135
136impl Deref for ArchivedString {
137    type Target = str;
138
139    #[inline]
140    fn deref(&self) -> &Self::Target {
141        self.as_str()
142    }
143}
144
145impl fmt::Display for ArchivedString {
146    #[inline]
147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        fmt::Display::fmt(self.as_str(), f)
149    }
150}
151
152impl Eq for ArchivedString {}
153
154impl hash::Hash for ArchivedString {
155    fn hash<H: hash::Hasher>(&self, state: &mut H) {
156        self.as_str().hash(state)
157    }
158}
159
160macro_rules! impl_index {
161    ($index:ty) => {
162        impl Index<$index> for ArchivedString {
163            type Output = str;
164
165            #[inline]
166            fn index(&self, index: $index) -> &Self::Output {
167                self.as_str().index(index)
168            }
169        }
170    };
171}
172
173impl_index!(Range<usize>);
174impl_index!(RangeFrom<usize>);
175impl_index!(RangeFull);
176impl_index!(RangeInclusive<usize>);
177impl_index!(RangeTo<usize>);
178impl_index!(RangeToInclusive<usize>);
179
180impl Ord for ArchivedString {
181    #[inline]
182    fn cmp(&self, other: &Self) -> cmp::Ordering {
183        self.as_str().cmp(other.as_str())
184    }
185}
186
187impl PartialEq for ArchivedString {
188    #[inline]
189    fn eq(&self, other: &Self) -> bool {
190        self.as_str() == other.as_str()
191    }
192}
193
194impl PartialOrd for ArchivedString {
195    #[inline]
196    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
197        Some(self.cmp(other))
198    }
199}
200
201impl PartialEq<&str> for ArchivedString {
202    #[inline]
203    fn eq(&self, other: &&str) -> bool {
204        PartialEq::eq(self.as_str(), *other)
205    }
206}
207
208impl PartialEq<str> for ArchivedString {
209    #[inline]
210    fn eq(&self, other: &str) -> bool {
211        PartialEq::eq(self.as_str(), other)
212    }
213}
214
215impl PartialEq<ArchivedString> for &str {
216    #[inline]
217    fn eq(&self, other: &ArchivedString) -> bool {
218        PartialEq::eq(other.as_str(), *self)
219    }
220}
221
222impl PartialEq<ArchivedString> for str {
223    #[inline]
224    fn eq(&self, other: &ArchivedString) -> bool {
225        PartialEq::eq(other.as_str(), self)
226    }
227}
228
229impl PartialOrd<&str> for ArchivedString {
230    #[inline]
231    fn partial_cmp(&self, other: &&str) -> Option<cmp::Ordering> {
232        self.as_str().partial_cmp(*other)
233    }
234}
235
236impl PartialOrd<str> for ArchivedString {
237    #[inline]
238    fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
239        self.as_str().partial_cmp(other)
240    }
241}
242
243impl PartialOrd<ArchivedString> for &str {
244    #[inline]
245    fn partial_cmp(&self, other: &ArchivedString) -> Option<cmp::Ordering> {
246        self.partial_cmp(&other.as_str())
247    }
248}
249
250impl PartialOrd<ArchivedString> for str {
251    #[inline]
252    fn partial_cmp(&self, other: &ArchivedString) -> Option<cmp::Ordering> {
253        self.partial_cmp(other.as_str())
254    }
255}
256
257/// The resolver for `String`.
258pub struct StringResolver {
259    pos: FixedUsize,
260}
261
262#[cfg(feature = "bytecheck")]
263mod verify {
264    use bytecheck::{
265        rancor::{Fallible, Source},
266        CheckBytes, Verify,
267    };
268
269    use crate::{
270        string::{repr::ArchivedStringRepr, ArchivedString},
271        validation::{ArchiveContext, ArchiveContextExt},
272    };
273
274    unsafe impl<C> Verify<C> for ArchivedString
275    where
276        C: Fallible + ArchiveContext + ?Sized,
277        C::Error: Source,
278    {
279        fn verify(&self, context: &mut C) -> Result<(), C::Error> {
280            if self.repr.is_inline() {
281                unsafe {
282                    str::check_bytes(self.repr.as_str_ptr(), context)?;
283                }
284            } else {
285                let base =
286                    (&self.repr as *const ArchivedStringRepr).cast::<u8>();
287                let offset = unsafe { self.repr.out_of_line_offset() };
288                let metadata = self.repr.len();
289
290                let address = base.wrapping_offset(offset).cast::<()>();
291                let ptr = ptr_meta::from_raw_parts(address, metadata);
292
293                context.in_subtree(ptr, |context| {
294                    // SAFETY: `in_subtree` has guaranteed that `ptr` is
295                    // properly aligned and points to enough bytes to represent
296                    // the pointed-to `str`.
297                    unsafe { str::check_bytes(ptr, context) }
298                })?;
299            }
300
301            Ok(())
302        }
303    }
304}