1use alloc::borrow::{Borrow, Cow};
14use alloc::boxed::Box;
15use alloc::string::String;
16use core::cmp::Ordering;
17use core::fmt;
18use core::hash::{Hash, Hasher};
19use core::marker::PhantomData;
20use core::ops::Deref;
21use core::ptr::NonNull;
22
23const OWNED: usize = 1 << (usize::BITS - 1);
25
26const TAG_SHIFT: u32 = usize::BITS - 4;
28
29const TAG: usize = 0b111 << TAG_SHIFT;
31
32const LEN: usize = !(OWNED | TAG);
38
39pub(crate) struct Slice<'a> {
41 ptr: NonNull<u8>,
42 len: usize,
44 _marker: PhantomData<&'a [u8]>,
45}
46
47unsafe impl Send for Slice<'_> {}
50unsafe impl Sync for Slice<'_> {}
52
53impl<'a> Slice<'a> {
54 #[inline]
55 pub(crate) const fn borrowed(data: &'a [u8]) -> Slice<'a> {
56 assert!(data.len() <= LEN, "slice too large");
57 Slice {
58 ptr: unsafe { NonNull::new_unchecked(data.as_ptr().cast_mut()) },
61 len: data.len(),
62 _marker: PhantomData,
63 }
64 }
65
66 #[inline]
67 pub(crate) fn owned(data: Box<[u8]>) -> Slice<'a> {
68 let len = data.len();
69 assert!(len <= LEN, "slice too large");
70 let ptr = Box::into_raw(data).cast::<u8>();
71 Slice {
72 ptr: unsafe { NonNull::new_unchecked(ptr) },
74 len: len | OWNED,
75 _marker: PhantomData,
76 }
77 }
78
79 #[inline]
80 pub(crate) fn len(&self) -> usize {
81 self.len & LEN
82 }
83
84 #[inline]
86 pub(crate) fn tag(&self) -> u8 {
87 ((self.len & TAG) >> TAG_SHIFT) as u8
88 }
89
90 #[inline]
92 pub(crate) fn with_tag(mut self, tag: u8) -> Slice<'a> {
93 debug_assert!(tag <= 7);
94 self.len = (self.len & !TAG) | ((tag as usize) << TAG_SHIFT) & TAG;
95 self
96 }
97
98 #[inline]
99 pub(crate) fn is_owned(&self) -> bool {
100 self.len & OWNED != 0
101 }
102
103 #[inline]
104 pub(crate) fn as_slice(&self) -> &[u8] {
105 unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.len()) }
108 }
109
110 #[inline]
112 pub(crate) fn borrowed_slice(&self) -> Option<&'a [u8]> {
113 if self.is_owned() {
114 None
115 } else {
116 Some(unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.len()) })
118 }
119 }
120
121 #[inline]
123 pub(crate) fn reborrow(&self) -> Slice<'_> {
124 Slice::borrowed(self.as_slice())
125 }
126
127 #[inline]
129 pub(crate) fn into_box(self) -> Box<[u8]> {
130 if self.is_owned() {
131 let len = self.len();
132 let ptr = self.ptr.as_ptr();
133 core::mem::forget(self);
134 unsafe { Box::from_raw(core::ptr::slice_from_raw_parts_mut(ptr, len)) }
137 } else {
138 Box::from(self.as_slice())
139 }
140 }
141
142 #[inline]
144 pub(crate) fn into_cow(self) -> Cow<'a, [u8]> {
145 match self.borrowed_slice() {
146 Some(data) => Cow::Borrowed(data),
147 None => Cow::Owned(self.into_box().into_vec()),
148 }
149 }
150
151 #[inline]
152 pub(crate) fn from_cow(data: Cow<'a, [u8]>) -> Slice<'a> {
153 match data {
154 Cow::Borrowed(data) => Slice::borrowed(data),
155 Cow::Owned(data) => Slice::owned(data.into_boxed_slice()),
156 }
157 }
158
159 #[inline]
160 pub(crate) fn to_static(&self) -> Slice<'static> {
161 Slice::owned(Box::from(self.as_slice()))
162 }
163}
164
165impl Drop for Slice<'_> {
166 #[inline]
167 fn drop(&mut self) {
168 if self.is_owned() {
169 drop(unsafe {
172 Box::from_raw(core::ptr::slice_from_raw_parts_mut(
173 self.ptr.as_ptr(),
174 self.len(),
175 ))
176 });
177 }
178 }
179}
180
181impl Clone for Slice<'_> {
182 #[inline]
183 fn clone(&self) -> Self {
184 if self.is_owned() {
185 Slice::owned(Box::from(self.as_slice())).with_tag(self.tag())
186 } else {
187 Slice {
188 ptr: self.ptr,
189 len: self.len,
190 _marker: PhantomData,
191 }
192 }
193 }
194}
195
196#[derive(Clone)]
216pub struct Text<'a>(Slice<'a>);
217
218impl<'a> Text<'a> {
219 #[inline]
221 pub const fn borrowed(text: &'a str) -> Text<'a> {
222 Text(Slice::borrowed(text.as_bytes()))
223 }
224
225 #[inline]
230 pub fn owned<S: Into<Box<str>>>(text: S) -> Text<'a> {
231 Text(Slice::owned(text.into().into_boxed_bytes()))
232 }
233
234 #[inline]
236 pub(crate) fn tag(&self) -> u8 {
237 self.0.tag()
238 }
239
240 #[inline]
242 pub(crate) fn with_tag(self, tag: u8) -> Text<'a> {
243 Text(self.0.with_tag(tag))
244 }
245
246 #[inline]
248 pub fn as_str(&self) -> &str {
249 unsafe { core::str::from_utf8_unchecked(self.0.as_slice()) }
251 }
252
253 #[inline]
258 pub fn borrowed_str(&self) -> Option<&'a str> {
259 self.0
261 .borrowed_slice()
262 .map(|data| unsafe { core::str::from_utf8_unchecked(data) })
263 }
264
265 #[inline]
267 pub fn is_borrowed(&self) -> bool {
268 !self.0.is_owned()
269 }
270
271 #[inline]
273 pub fn as_borrowed(&self) -> Text<'_> {
274 Text(self.0.reborrow())
275 }
276
277 #[inline]
279 pub fn to_static(&self) -> Text<'static> {
280 Text(self.0.to_static())
281 }
282
283 #[inline]
287 pub fn into_cow(self) -> Cow<'a, str> {
288 match self.0.into_cow() {
289 Cow::Borrowed(data) => Cow::Borrowed(unsafe { core::str::from_utf8_unchecked(data) }),
291 Cow::Owned(data) => Cow::Owned(unsafe { String::from_utf8_unchecked(data) }),
292 }
293 }
294
295 #[inline]
299 pub fn into_owned(self) -> String {
300 self.into_cow().into_owned()
301 }
302}
303
304impl Default for Text<'_> {
305 #[inline]
306 fn default() -> Self {
307 Text::borrowed("")
308 }
309}
310
311impl Deref for Text<'_> {
312 type Target = str;
313
314 #[inline]
315 fn deref(&self) -> &str {
316 self.as_str()
317 }
318}
319
320impl AsRef<str> for Text<'_> {
321 #[inline]
322 fn as_ref(&self) -> &str {
323 self.as_str()
324 }
325}
326
327impl Borrow<str> for Text<'_> {
328 #[inline]
329 fn borrow(&self) -> &str {
330 self.as_str()
331 }
332}
333
334impl<'a> From<&'a str> for Text<'a> {
335 #[inline]
336 fn from(text: &'a str) -> Text<'a> {
337 Text::borrowed(text)
338 }
339}
340
341impl<'a> From<&'a String> for Text<'a> {
342 #[inline]
343 fn from(text: &'a String) -> Text<'a> {
344 Text::borrowed(text)
345 }
346}
347
348impl From<String> for Text<'_> {
349 #[inline]
350 fn from(text: String) -> Self {
351 Text::owned(text)
352 }
353}
354
355impl From<Box<str>> for Text<'_> {
356 #[inline]
357 fn from(text: Box<str>) -> Self {
358 Text::owned(text)
359 }
360}
361
362impl<'a> From<Cow<'a, str>> for Text<'a> {
363 #[inline]
364 fn from(text: Cow<'a, str>) -> Text<'a> {
365 match text {
366 Cow::Borrowed(text) => Text::borrowed(text),
367 Cow::Owned(text) => Text::owned(text),
368 }
369 }
370}
371
372impl<'a> From<Text<'a>> for Cow<'a, str> {
373 #[inline]
374 fn from(text: Text<'a>) -> Cow<'a, str> {
375 text.into_cow()
376 }
377}
378
379impl From<Text<'_>> for String {
380 #[inline]
381 fn from(text: Text<'_>) -> String {
382 text.into_owned()
383 }
384}
385
386impl fmt::Debug for Text<'_> {
387 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388 fmt::Debug::fmt(self.as_str(), f)
389 }
390}
391
392impl fmt::Display for Text<'_> {
393 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
394 fmt::Display::fmt(self.as_str(), f)
395 }
396}
397
398impl PartialEq for Text<'_> {
399 #[inline]
400 fn eq(&self, other: &Self) -> bool {
401 self.as_str() == other.as_str()
402 }
403}
404
405impl Eq for Text<'_> {}
406
407impl PartialOrd for Text<'_> {
408 #[inline]
409 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
410 Some(self.cmp(other))
411 }
412}
413
414impl Ord for Text<'_> {
415 #[inline]
416 fn cmp(&self, other: &Self) -> Ordering {
417 self.as_str().cmp(other.as_str())
418 }
419}
420
421impl Hash for Text<'_> {
422 #[inline]
423 fn hash<H: Hasher>(&self, state: &mut H) {
424 self.as_str().hash(state)
425 }
426}
427
428impl PartialEq<str> for Text<'_> {
429 #[inline]
430 fn eq(&self, other: &str) -> bool {
431 self.as_str() == other
432 }
433}
434
435impl PartialEq<&str> for Text<'_> {
436 #[inline]
437 fn eq(&self, other: &&str) -> bool {
438 self.as_str() == *other
439 }
440}
441
442impl PartialEq<String> for Text<'_> {
443 #[inline]
444 fn eq(&self, other: &String) -> bool {
445 self.as_str() == other
446 }
447}
448
449impl PartialEq<Text<'_>> for str {
450 #[inline]
451 fn eq(&self, other: &Text<'_>) -> bool {
452 self == other.as_str()
453 }
454}
455
456impl PartialEq<Text<'_>> for &str {
457 #[inline]
458 fn eq(&self, other: &Text<'_>) -> bool {
459 *self == other.as_str()
460 }
461}
462
463#[test]
464fn test_text() {
465 assert_eq!(core::mem::size_of::<Text>(), 16);
466 assert_eq!(core::mem::size_of::<Option<Text>>(), 16);
467
468 let borrowed = Text::from("borrowed");
469 assert!(borrowed.is_borrowed());
470 assert_eq!(borrowed.borrowed_str(), Some("borrowed"));
471 assert_eq!(borrowed.clone(), "borrowed");
472 assert_eq!(borrowed.to_static(), "borrowed");
473 assert!(!borrowed.to_static().is_borrowed());
474
475 let mut owned = String::with_capacity(64);
476 owned.push_str("owned");
477 let owned = Text::from(owned);
478 assert!(!owned.is_borrowed());
479 assert_eq!(owned.borrowed_str(), None);
480 assert_eq!(owned.as_borrowed().borrowed_str(), Some("owned"));
481 assert_eq!(owned.clone(), owned);
482 assert_eq!(owned.clone().into_owned(), "owned");
483 assert_eq!(owned.into_cow(), Cow::<str>::Owned("owned".into()));
484
485 let empty = Text::from(String::new());
486 assert_eq!(empty, "");
487 assert_eq!(empty.into_owned(), "");
488 assert_eq!(Text::default(), "");
489}
490
491#[test]
492fn test_text_tag() {
493 for text in [Text::from("tagged"), Text::from(String::from("tagged"))] {
495 let owned = !text.is_borrowed();
496 let tagged = text.with_tag(5);
497 assert_eq!(tagged.tag(), 5);
498 assert_eq!(tagged, "tagged");
499 assert_eq!(tagged.len(), 6);
500 assert_eq!(tagged.is_borrowed(), !owned);
501 assert_eq!(tagged.clone().tag(), 5);
503 assert_eq!(tagged.as_borrowed().tag(), 0);
504 assert_eq!(tagged.to_static().tag(), 0);
505 let retagged = tagged.with_tag(2);
506 assert_eq!(retagged.tag(), 2);
507 assert_eq!(retagged.clone().into_owned(), "tagged");
508 assert_eq!(retagged.with_tag(0).tag(), 0);
509 }
510}