rkyv_test/string/
repr.rs

1//! An archived string representation that supports inlining short strings.
2
3use crate::{Archived, FixedIsize, FixedUsize};
4use core::{mem, ptr, slice, str};
5
6const OFFSET_BYTES: usize = mem::size_of::<FixedIsize>();
7
8#[derive(Clone, Copy)]
9#[repr(C)]
10struct OutOfLineRepr {
11    len: Archived<usize>,
12    // offset is always stored in little-endian format to put the sign bit at the end
13    // this representation is optimized for little-endian architectures
14    offset: [u8; OFFSET_BYTES],
15}
16
17/// The maximum number of bytes that can be inlined.
18pub const INLINE_CAPACITY: usize = mem::size_of::<OutOfLineRepr>() - 1;
19
20#[derive(Clone, Copy)]
21#[repr(C)]
22struct InlineRepr {
23    bytes: [u8; INLINE_CAPACITY],
24    len: u8,
25}
26
27/// An archived string representation that can inline short strings.
28pub union ArchivedStringRepr {
29    out_of_line: OutOfLineRepr,
30    inline: InlineRepr,
31}
32
33impl ArchivedStringRepr {
34    /// Returns whether the representation is inline.
35    #[inline]
36    pub fn is_inline(&self) -> bool {
37        unsafe { self.inline.len & 0x80 == 0 }
38    }
39
40    /// Returns the offset of the representation.
41    ///
42    /// # Safety
43    ///
44    /// The internal representation must be out-of-line.
45    #[inline]
46    pub unsafe fn out_of_line_offset(&self) -> isize {
47        FixedIsize::from_le_bytes(self.out_of_line.offset) as isize
48    }
49
50    /// Returns a pointer to the bytes of the string.
51    #[inline]
52    pub fn as_ptr(&self) -> *const u8 {
53        unsafe {
54            if self.is_inline() {
55                self.inline.bytes.as_ptr()
56            } else {
57                (self as *const Self)
58                    .cast::<u8>()
59                    .offset(self.out_of_line_offset())
60            }
61        }
62    }
63
64    /// Returns a mutable pointer to the bytes of the string.
65    #[inline]
66    pub fn as_mut_ptr(&mut self) -> *mut u8 {
67        unsafe {
68            if self.is_inline() {
69                self.inline.bytes.as_mut_ptr()
70            } else {
71                (self as *mut Self)
72                    .cast::<u8>()
73                    .offset(self.out_of_line_offset())
74            }
75        }
76    }
77
78    /// Returns the length of the string.
79    #[inline]
80    pub fn len(&self) -> usize {
81        unsafe {
82            if self.is_inline() {
83                self.inline.len as usize
84            } else {
85                from_archived!(self.out_of_line.len) as usize
86            }
87        }
88    }
89
90    /// Returns whether the string is empty.
91    #[inline]
92    pub fn is_empty(&self) -> bool {
93        self.len() == 0
94    }
95
96    /// Returns a pointer to the string as a `str`.
97    #[cfg(feature = "validation")]
98    #[inline]
99    pub fn as_str_ptr(&self) -> *const str {
100        ptr_meta::from_raw_parts(self.as_ptr().cast(), self.len())
101    }
102
103    /// Returns a slice of the bytes of the string.
104    #[inline]
105    pub fn bytes(&self) -> &[u8] {
106        unsafe { slice::from_raw_parts(self.as_ptr(), self.len()) }
107    }
108
109    /// Returns a mutable slice of the bytes of the string.
110    #[inline]
111    pub fn bytes_mut(&mut self) -> &mut [u8] {
112        unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len()) }
113    }
114
115    /// Returns a reference to the string as a `str`.
116    #[inline]
117    pub fn as_str(&self) -> &str {
118        unsafe { str::from_utf8_unchecked(self.bytes()) }
119    }
120
121    /// Returns a mutable reference to the string as a `str`.
122    #[inline]
123    pub fn as_mut_str(&mut self) -> &mut str {
124        unsafe { str::from_utf8_unchecked_mut(self.bytes_mut()) }
125    }
126
127    /// Emplaces a new inline representation for the given `str`.
128    ///
129    /// # Safety
130    ///
131    /// - The length of `str` must be less than or equal to [`INLINE_CAPACITY`].
132    /// - `out` must point to a valid location to write the inline representation.
133    #[inline]
134    pub unsafe fn emplace_inline(value: &str, out: *mut Self) {
135        let out_bytes = ptr::addr_of_mut!((*out).inline.bytes);
136        ptr::copy_nonoverlapping(value.as_bytes().as_ptr(), out_bytes.cast(), value.len());
137
138        let out_len = ptr::addr_of_mut!((*out).inline.len);
139        *out_len = value.len() as u8;
140    }
141
142    /// Rmplaces a new out-of-line representation for the given `str`.
143    ///
144    /// # Safety
145    ///
146    /// - The length of `str` must be greater than [`INLINE_CAPACITY`].
147    /// - `pos` must be the location of the representation within the archive.
148    /// - `target` must be the location of the serialized bytes of the string.
149    /// - `out` must point to a valid location to write the out-of-line representation.
150    #[inline]
151    pub unsafe fn emplace_out_of_line(value: &str, pos: usize, target: usize, out: *mut Self) {
152        let out_len = ptr::addr_of_mut!((*out).out_of_line.len);
153        out_len.write(to_archived!(value.len() as FixedUsize));
154
155        let out_offset = ptr::addr_of_mut!((*out).out_of_line.offset);
156        let offset = crate::rel_ptr::signed_offset(pos, target).unwrap();
157        *out_offset = (offset as FixedIsize).to_le_bytes();
158    }
159}