1pub 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#[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 #[inline]
45 pub fn as_str(&self) -> &str {
46 self.repr.as_str()
47 }
48
49 #[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 #[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 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
257pub 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 unsafe { str::check_bytes(ptr, context) }
298 })?;
299 }
300
301 Ok(())
302 }
303 }
304}