1use core::{
4 marker::PhantomPinned,
5 mem,
6 ptr::{self, copy_nonoverlapping, write_bytes},
7 slice, str,
8};
9
10use munge::munge;
11use rancor::{Panic, ResultExt as _, Source};
12
13use crate::{
14 primitive::{ArchivedIsize, ArchivedUsize, FixedIsize, FixedUsize},
15 seal::Seal,
16 Place, Portable,
17};
18
19#[derive(Clone, Copy, Portable)]
20#[rkyv(crate)]
21#[repr(C)]
22struct OutOfLineRepr {
23 len: ArchivedUsize,
24 offset: ArchivedIsize,
25 _phantom: PhantomPinned,
26}
27
28pub const INLINE_CAPACITY: usize = mem::size_of::<OutOfLineRepr>();
30pub const OUT_OF_LINE_CAPACITY: usize = (1 << (FixedUsize::BITS - 2)) - 1;
32
33#[derive(Clone, Copy, Portable)]
34#[rkyv(crate)]
35#[repr(C)]
36struct InlineRepr {
37 bytes: [u8; INLINE_CAPACITY],
38}
39
40#[derive(Portable)]
42#[rkyv(crate)]
43#[repr(C)]
44pub union ArchivedStringRepr {
45 out_of_line: OutOfLineRepr,
46 inline: InlineRepr,
47}
48
49impl ArchivedStringRepr {
50 #[inline]
52 pub fn is_inline(&self) -> bool {
53 unsafe { self.inline.bytes[0] & 0xc0 != 0x80 }
54 }
55
56 #[inline]
62 pub unsafe fn out_of_line_offset(&self) -> isize {
63 unsafe { self.out_of_line.offset.to_native() as isize }
66 }
67
68 #[inline]
70 pub fn as_ptr(&self) -> *const u8 {
71 if self.is_inline() {
72 unsafe { self.inline.bytes.as_ptr() }
73 } else {
74 unsafe {
75 (self as *const Self)
76 .cast::<u8>()
77 .offset(self.out_of_line_offset())
78 }
79 }
80 }
81
82 #[inline]
84 pub fn as_mut_ptr(this: Seal<'_, Self>) -> *mut u8 {
85 let this = unsafe { this.unseal_unchecked() };
86 if this.is_inline() {
87 unsafe { this.inline.bytes.as_mut_ptr() }
88 } else {
89 unsafe {
90 (this as *mut Self)
91 .cast::<u8>()
92 .offset(this.out_of_line_offset())
93 }
94 }
95 }
96
97 #[inline]
99 pub fn len(&self) -> usize {
100 if self.is_inline() {
101 unsafe {
102 self.inline
103 .bytes
104 .iter()
105 .position(|b| *b == 0xff)
106 .unwrap_or(INLINE_CAPACITY)
107 }
108 } else {
109 let len = unsafe { self.out_of_line.len.to_native() };
110 #[cfg(not(feature = "big_endian"))]
112 let len = (len & 0b0011_1111) | ((len & !0xff) >> 2);
113 #[cfg(feature = "big_endian")]
115 let len = len & (FixedUsize::MAX >> 2);
116 len as usize
117 }
118 }
119
120 #[inline]
122 pub fn is_empty(&self) -> bool {
123 self.len() == 0
124 }
125
126 #[inline]
128 pub fn as_str_ptr(&self) -> *const str {
129 ptr_meta::from_raw_parts(self.as_ptr().cast(), self.len())
130 }
131
132 #[inline]
134 pub fn as_bytes(&self) -> &[u8] {
135 unsafe { slice::from_raw_parts(self.as_ptr(), self.len()) }
136 }
137
138 #[inline]
140 pub fn as_bytes_seal(this: Seal<'_, Self>) -> Seal<'_, [u8]> {
141 let len = this.len();
142 let slice =
143 unsafe { slice::from_raw_parts_mut(Self::as_mut_ptr(this), len) };
144 Seal::new(slice)
145 }
146
147 #[inline]
149 pub fn as_str(&self) -> &str {
150 unsafe { str::from_utf8_unchecked(self.as_bytes()) }
151 }
152
153 #[inline]
155 pub fn as_str_seal(this: Seal<'_, Self>) -> Seal<'_, str> {
156 let bytes =
157 unsafe { Seal::unseal_unchecked(Self::as_bytes_seal(this)) };
158 Seal::new(unsafe { str::from_utf8_unchecked_mut(bytes) })
159 }
160
161 #[inline]
173 pub unsafe fn emplace_inline(value: &str, out: *mut Self) {
174 debug_assert!(value.len() <= INLINE_CAPACITY);
175
176 let out_bytes = unsafe { ptr::addr_of_mut!((*out).inline.bytes) };
179
180 unsafe {
185 write_bytes(out_bytes, 0xff, 1);
186 copy_nonoverlapping(
187 value.as_bytes().as_ptr(),
188 out_bytes.cast(),
189 value.len(),
190 );
191 }
192 }
193
194 pub unsafe fn try_emplace_out_of_line<E: Source>(
201 value: &str,
202 target: usize,
203 out: Place<Self>,
204 ) -> Result<(), E> {
205 let (len, offset) = unsafe {
206 munge! {
207 let ArchivedStringRepr {
208 out_of_line: OutOfLineRepr { len, offset, _phantom: _ }
209 } = out;
210 }
211 (len, offset)
212 };
213
214 let l = value.len() as FixedUsize;
215 #[cfg(not(feature = "big_endian"))]
217 let l = (l & 0b0011_1111) | 0b1000_0000 | ((l & !0b0011_1111) << 2);
218 #[cfg(feature = "big_endian")]
220 let l = l & (FixedUsize::MAX >> 2) | (1 << FixedUsize::BITS - 1);
221 len.write(ArchivedUsize::from_native(l));
222
223 let off = crate::rel_ptr::signed_offset(out.pos(), target)?;
224 offset.write(ArchivedIsize::from_native(off as FixedIsize));
225
226 Ok(())
227 }
228
229 #[inline]
241 pub unsafe fn emplace_out_of_line(
242 value: &str,
243 target: usize,
244 out: Place<Self>,
245 ) {
246 unsafe {
249 Self::try_emplace_out_of_line::<Panic>(value, target, out)
250 .always_ok()
251 }
252 }
253}
254
255#[cfg(feature = "bytecheck")]
256const _: () = {
257 use core::{error::Error, fmt};
258
259 use bytecheck::{rancor::Fallible, CheckBytes};
260 use rancor::fail;
261
262 #[derive(Debug)]
267 pub struct CheckStringReprError;
268
269 impl fmt::Display for CheckStringReprError {
270 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
271 write!(
272 f,
273 "String representation was out-of-line but the length was too \
274 short",
275 )
276 }
277 }
278
279 impl Error for CheckStringReprError {}
280
281 unsafe impl<C> CheckBytes<C> for ArchivedStringRepr
282 where
283 C: Fallible + ?Sized,
284 C::Error: Source,
285 {
286 unsafe fn check_bytes(
287 value: *const Self,
288 _: &mut C,
289 ) -> Result<(), C::Error> {
290 let repr = unsafe { &*value };
293
294 if !repr.is_inline() && repr.len() <= INLINE_CAPACITY {
295 fail!(CheckStringReprError);
296 } else {
297 Ok(())
298 }
299 }
300 }
301};