rkyv/string/
mod.rs

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