Skip to main content

toml_spanner/
str.rs

1#![allow(unsafe_code)]
2
3//! A borrowed string type used throughout the parser.
4
5use std::borrow::{Borrow, Cow};
6use std::hash::{Hash, Hasher};
7use std::marker::PhantomData;
8use std::ops::Deref;
9use std::ptr::NonNull;
10use std::{fmt, str};
11
12/// A borrowed string that references either the TOML source or the parser
13/// [`Arena`](crate::Arena).
14///
15/// `Str` is [`Copy`] and never owns its data. It dereferences to `str` and
16/// can be converted into [`String`], [`Box<str>`], or [`Cow<str>`](std::borrow::Cow).
17#[derive(Copy, Clone)]
18pub struct Str<'de> {
19    ptr: NonNull<u8>,
20    len: usize,
21    _marker: PhantomData<&'de str>,
22}
23
24const _: () = assert!(std::mem::size_of::<Str<'_>>() == 16);
25
26// SAFETY: The inner data is a `&str` (Send+Sync).
27unsafe impl Send for Str<'_> {}
28unsafe impl Sync for Str<'_> {}
29
30impl Deref for Str<'_> {
31    type Target = str;
32
33    #[inline]
34    fn deref(&self) -> &str {
35        unsafe {
36            let slice = std::slice::from_raw_parts(self.ptr.as_ptr(), self.len);
37            str::from_utf8_unchecked(slice)
38        }
39    }
40}
41
42impl AsRef<str> for Str<'_> {
43    #[inline]
44    fn as_ref(&self) -> &str {
45        self
46    }
47}
48
49impl Borrow<str> for Str<'_> {
50    #[inline]
51    fn borrow(&self) -> &str {
52        self
53    }
54}
55
56impl fmt::Display for Str<'_> {
57    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58        f.write_str(self)
59    }
60}
61
62impl fmt::Debug for Str<'_> {
63    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64        fmt::Debug::fmt(&**self, f)
65    }
66}
67
68impl PartialEq for Str<'_> {
69    #[inline]
70    fn eq(&self, other: &Self) -> bool {
71        **self == **other
72    }
73}
74
75impl Eq for Str<'_> {}
76
77impl PartialOrd for Str<'_> {
78    #[inline]
79    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
80        Some(self.cmp(other))
81    }
82}
83
84impl Ord for Str<'_> {
85    #[inline]
86    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
87        (**self).cmp(&**other)
88    }
89}
90
91impl PartialEq<str> for Str<'_> {
92    #[inline]
93    fn eq(&self, other: &str) -> bool {
94        **self == *other
95    }
96}
97
98impl PartialEq<&str> for Str<'_> {
99    #[inline]
100    fn eq(&self, other: &&str) -> bool {
101        **self == **other
102    }
103}
104
105impl Hash for Str<'_> {
106    #[inline]
107    fn hash<H: Hasher>(&self, state: &mut H) {
108        (**self).hash(state);
109    }
110}
111
112impl Default for Str<'_> {
113    #[inline]
114    fn default() -> Self {
115        Self::from_borrowed("")
116    }
117}
118
119impl<'de> Str<'de> {
120    /// Returns the string slice with the full `'de` lifetime.
121    pub fn as_str(&self) -> &'de str {
122        unsafe {
123            let slice = std::slice::from_raw_parts(self.ptr.as_ptr(), self.len);
124            str::from_utf8_unchecked(slice)
125        }
126    }
127
128    #[inline]
129    pub(crate) fn from_borrowed(s: &'de str) -> Self {
130        // SAFETY: str::as_ptr() is non-null for any valid &str (even "" points to a dangling-but-non-null address).
131        let ptr = unsafe { NonNull::new_unchecked(s.as_ptr() as *mut u8) };
132        Self {
133            ptr,
134            len: s.len(),
135            _marker: PhantomData,
136        }
137    }
138
139    /// Copies the contents into a heap-allocated [`Box<str>`].
140    pub fn into_boxed_str(self) -> Box<str> {
141        (&*self).into()
142    }
143}
144
145impl<'de> From<&'de str> for Str<'de> {
146    #[inline]
147    fn from(s: &'de str) -> Self {
148        Self::from_borrowed(s)
149    }
150}
151
152impl<'de> From<Str<'de>> for Cow<'de, str> {
153    #[inline]
154    fn from(s: Str<'de>) -> Self {
155        // Safety: Str's pointer is valid for 'de and contains valid UTF-8.
156        let borrowed: &'de str =
157            unsafe { str::from_utf8_unchecked(std::slice::from_raw_parts(s.ptr.as_ptr(), s.len)) };
158        Cow::Borrowed(borrowed)
159    }
160}
161
162impl From<Str<'_>> for Box<str> {
163    #[inline]
164    fn from(s: Str<'_>) -> Self {
165        (&*s).into()
166    }
167}
168
169impl From<Str<'_>> for String {
170    #[inline]
171    fn from(s: Str<'_>) -> Self {
172        (*s).to_owned()
173    }
174}
175
176#[cfg(test)]
177#[path = "./str_tests.rs"]
178mod tests;