1use crate::prelude::*;
2
3use crate::num::*;
4
5const STRING_SIZE_BYTES: usize = size_of::<usize>() * 3;
6const MAX_INLINE_LEN: usize = {
7 let container_size = STRING_SIZE_BYTES - 1;
11 #[expect(clippy::as_conversions, reason = "u8 to usize is fine (also we are in const context)")]
12 let len_size = (u8::MAX >> 1) as usize;
13
14 if len_size < container_size {
15 len_size
16 } else {
17 container_size
18 }
19};
20const CAP_MARKER_BE: usize = (!(usize::MAX >> 1)).to_be();
21const CAP_MARKER_U8: u8 = !(u8::MAX >> 1);
22
23const _: () = assert!(size_of::<StringInlineable>() == STRING_SIZE_BYTES);
24const _: () = assert!(size_of::<StringInline>() == STRING_SIZE_BYTES);
25const _: () = assert!(size_of::<StringHeap>() == STRING_SIZE_BYTES);
26const _: () = assert!(MAX_INLINE_LEN > 0);
27
28pub union StringInlineable {
29 inline: ManuallyDrop<StringInline>,
30 heap: ManuallyDrop<StringHeap>
31}
32
33impl StringInlineable {
34 #[inline]
35 pub const fn new() -> Self {
36 let inline = StringInline::new();
37 Self { inline: ManuallyDrop::new(inline) }
38 }
39}
40
41impl StringInlineable {
42 #[inline]
43 pub fn len(&self) -> usize {
44 self.do_thing(|s| s.len(), |s| s.len())
45 }
46
47 #[inline]
48 pub fn capacity(&self) -> usize {
49 self.do_thing(|s| s.capacity(), |s| s.capacity())
50 }
51
52 #[inline]
53 pub fn is_empty(&self) -> bool {
54 self.len() == 0
55 }
56
57 #[inline]
58 pub fn as_str(&self) -> &str {
59 self.do_thing(|s| s.as_str(), |s| s.as_str())
60 }
61
62 #[inline]
63 pub fn as_str_mut(&mut self) -> &mut str {
64 self.do_thing_mut(|s| s.as_str_mut(), |s| s.as_str_mut())
65 }
66}
67
68impl StringInlineable {
69 #[inline]
70 fn is_inline(&self) -> bool {
71 let len = unsafe { self.inline.len };
74
75 len & CAP_MARKER_U8 == 0
76 }
77
78 #[inline]
79 fn do_thing<'h, T, FInline, FHeap>(&'h self, f_inline: FInline, f_heap: FHeap) -> T
80 where
81 FInline: FnOnce(&'h StringInline) -> T,
82 FHeap: FnOnce(&'h StringHeap) -> T
83 {
84 match self.is_inline() {
85 true => unsafe { f_inline(&self.inline) }
87 false => unsafe { f_heap(&self.heap) }
89 }
90 }
91
92 #[inline]
93 fn do_thing_mut<'h, T, FInline, FHeap>(&'h mut self, f_inline: FInline, f_heap: FHeap) -> T
94 where
95 FInline: FnOnce(&'h mut StringInline) -> T,
96 FHeap: FnOnce(&'h mut StringHeap) -> T
97 {
98 match self.is_inline() {
99 true => unsafe { f_inline(&mut self.inline) }
101 false => unsafe { f_heap(&mut self.heap) }
103 }
104 }
105}
106
107impl Default for StringInlineable {
108 #[inline]
109 fn default() -> Self {
110 Self::new()
111 }
112}
113
114impl Deref for StringInlineable {
115 type Target = str;
116
117 #[inline]
118 fn deref(&self) -> &str {
119 self.as_str()
120 }
121}
122
123impl From<&str> for StringInlineable {
124 #[inline]
125 fn from(s: &str) -> Self {
126 match s.len() <= MAX_INLINE_LEN {
127 true => {
128 let inline = unsafe { StringInline::from_str_unchecked(s) };
130 Self { inline: ManuallyDrop::new(inline) }
131 }
132 false => {
133 let heap = unsafe { StringHeap::from_str_unchecked(s) };
136 Self { heap: ManuallyDrop::new(heap) }
137 }
138 }
139 }
140}
141
142#[repr(C)]
143struct StringInline {
144 len: u8,
146 rest: MaybeUninit<[u8; MAX_INLINE_LEN]>
147}
148
149impl StringInline {
150 #[inline]
151 const fn new() -> Self {
152 Self { len: 0, rest: MaybeUninit::uninit() }
153 }
154
155 #[inline]
159 unsafe fn from_str_unchecked(s: &str) -> Self {
160 debug_assert!(s.len() <= MAX_INLINE_LEN);
161
162 let mut inline = Self {
163 len: 0,
164 rest: MaybeUninit::uninit()
165 };
166
167 unsafe {
175 ptr::copy_nonoverlapping(
176 s.as_ptr(),
177 inline.rest.as_mut_ptr().cast::<u8>(),
178 s.len()
179 )
180 }
181
182 inline.len = s.len().into_u8_lossy();
185
186 inline
187 }
188}
189
190impl StringInline {
191 #[inline]
192 fn len(&self) -> usize {
193 usize::from_u8(self.len)
194 }
195
196 #[inline]
197 fn capacity(&self) -> usize {
198 MAX_INLINE_LEN
199 }
200
201 #[inline]
202 fn as_str(&self) -> &str {
203 let ptr = self.rest.as_ptr().cast::<u8>();
204 let len = self.len.into_usize();
205
206 let slice = unsafe { slice::from_raw_parts(ptr, len) };
209 unsafe { str::from_utf8_unchecked(slice) }
211 }
212
213 #[inline]
214 fn as_str_mut(&mut self) -> &mut str {
215 let ptr = self.rest.as_ptr().cast::<u8>();
216 let len = self.len.into_usize();
217
218 let slice = unsafe { slice::from_raw_parts_mut(ptr.cast_mut(), len) };
221 unsafe { str::from_utf8_unchecked_mut(slice) }
223 }
224}
225
226#[repr(C)]
227struct StringHeap {
228 cap_be_and_marker: usize,
233 len: usize,
234 ptr: *const u8
235}
236
237impl StringHeap {
238 unsafe fn from_str_unchecked(s: &str) -> Self {
244 let layout = alloc_mod::Layout::for_value(s);
245 let ptr = unsafe { alloc(layout) };
247
248 let mut heap = Self {
249 cap_be_and_marker: 0,
250 len: 0,
251 ptr
252 };
253 unsafe { heap.set_capacity(layout.size()) }
257
258 unsafe {
265 ptr::copy_nonoverlapping(
266 s.as_ptr(),
267 heap.ptr.cast_mut(),
268 s.len()
269 )
270 }
271
272 heap.len = s.len();
275
276 heap
277 }
278}
279
280impl StringHeap {
281 #[inline]
282 fn len(&self) -> usize {
283 self.len
284 }
285
286 #[inline]
287 fn capacity(&self) -> usize {
288 usize::from_be(self.cap_be_and_marker ^ CAP_MARKER_BE)
289 }
290
291 #[inline]
300 unsafe fn set_capacity(&mut self, capacity: usize) {
301 self.cap_be_and_marker = capacity.to_be() ^ CAP_MARKER_BE
302 }
303
304 #[inline]
305 fn as_str(&self) -> &str {
306 let slice = unsafe { slice::from_raw_parts(self.ptr, self.len) };
309 unsafe { str::from_utf8_unchecked(slice) }
311 }
312
313 #[inline]
314 fn as_str_mut(&mut self) -> &mut str {
315 let slice = unsafe { slice::from_raw_parts_mut(self.ptr.cast_mut(), self.len) };
318 unsafe { str::from_utf8_unchecked_mut(slice) }
320 }
321}