Skip to main content

der/
string.rs

1//! Common handling for types backed by `str` slices with enforcement of a
2//! library-level length limitation i.e. `Length::max()`.
3
4use crate::{BytesRef, DecodeValue, EncodeValue, Error, Header, Length, Reader, Result, Writer};
5use core::str;
6
7/// String slice newtype which respects the [`Length::max`] limit.
8#[derive(Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
9#[repr(transparent)]
10pub struct StringRef(str);
11
12impl StringRef {
13    /// Create a new [`StringRef`], ensuring that the byte representation of
14    /// the provided `str` value is shorter than `Length::max()`.
15    pub const fn new(s: &str) -> Result<&Self> {
16        match Length::new_usize(s.len()) {
17            Ok(_) => Ok(Self::new_unchecked(s)),
18            Err(err) => Err(err),
19        }
20    }
21
22    /// Perform a raw conversion of a `str` to `Self` without first performing a length check.
23    pub(crate) const fn new_unchecked(s: &str) -> &Self {
24        // SAFETY: `Self` is a `repr(transparent)` newtype for `str`
25        #[allow(unsafe_code)]
26        unsafe {
27            &*(core::ptr::from_ref::<str>(s) as *const Self)
28        }
29    }
30
31    /// Parse a [`StringRef`] from UTF-8 encoded bytes.
32    pub fn from_bytes(bytes: &[u8]) -> Result<&Self> {
33        Self::new(str::from_utf8(bytes)?)
34    }
35
36    /// Borrow the inner `str`.
37    pub fn as_str(&self) -> &str {
38        &self.0
39    }
40
41    /// Borrow the inner byte slice.
42    pub fn as_bytes(&self) -> &[u8] {
43        self.0.as_bytes()
44    }
45
46    /// Get the [`Length`] of this [`StringRef`].
47    pub fn len(&self) -> Length {
48        debug_assert!(u32::try_from(self.0.len()).is_ok());
49
50        #[allow(clippy::cast_possible_truncation)] // checked by constructors
51        Length::new(self.0.len() as u32)
52    }
53
54    /// Is this [`StringRef`] empty?
55    pub fn is_empty(&self) -> bool {
56        self.0.is_empty()
57    }
58}
59
60impl AsRef<str> for StringRef {
61    fn as_ref(&self) -> &str {
62        self.as_str()
63    }
64}
65
66impl AsRef<[u8]> for StringRef {
67    fn as_ref(&self) -> &[u8] {
68        self.as_bytes()
69    }
70}
71
72impl AsRef<BytesRef> for StringRef {
73    fn as_ref(&self) -> &BytesRef {
74        BytesRef::new_unchecked(self.as_bytes())
75    }
76}
77
78impl<'a> DecodeValue<'a> for &'a StringRef {
79    type Error = Error;
80
81    fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
82        StringRef::from_bytes(<&'a BytesRef>::decode_value(reader, header)?.as_slice())
83    }
84}
85
86impl EncodeValue for StringRef {
87    fn value_len(&self) -> Result<Length> {
88        Ok(self.len())
89    }
90
91    fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
92        writer.write(self.as_ref())
93    }
94}
95
96#[cfg(feature = "alloc")]
97pub(crate) mod allocating {
98    use super::StringRef;
99    use crate::{
100        BytesRef, DecodeValue, EncodeValue, Error, Header, Length, Reader, Result, Writer,
101    };
102    use alloc::{borrow::ToOwned, string::String};
103    use core::{borrow::Borrow, ops::Deref, str};
104
105    /// String newtype which respects the [`Length::max`] limit.
106    #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
107    pub struct StringOwned {
108        /// Inner value
109        pub(crate) inner: String,
110
111        /// Precomputed `Length` (avoids possible panicking conversions)
112        pub(crate) length: Length,
113    }
114
115    impl StringOwned {
116        /// Create a new [`StringOwned`], ensuring that the byte representation of
117        /// the provided `str` value is shorter than `Length::max()`.
118        pub fn new(s: String) -> Result<Self> {
119            let length = Length::try_from(s.len())?;
120
121            Ok(Self { inner: s, length })
122        }
123
124        /// Parse a [`String`] from UTF-8 encoded bytes.
125        pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
126            Ok(Self {
127                inner: String::from_utf8(bytes.to_vec())?,
128                length: Length::try_from(bytes.len())?,
129            })
130        }
131
132        /// Borrow the inner `str`
133        pub fn as_str(&self) -> &str {
134            &self.inner
135        }
136
137        /// Borrow the inner byte slice
138        pub fn as_bytes(&self) -> &[u8] {
139            self.inner.as_bytes()
140        }
141
142        /// Get the [`Length`] of this [`StringOwned`]
143        pub fn len(&self) -> Length {
144            self.length
145        }
146
147        /// Is this [`StringOwned`] empty?
148        pub fn is_empty(&self) -> bool {
149            self.len() == Length::ZERO
150        }
151    }
152
153    impl AsRef<str> for StringOwned {
154        fn as_ref(&self) -> &str {
155            self.as_str()
156        }
157    }
158
159    impl AsRef<[u8]> for StringOwned {
160        fn as_ref(&self) -> &[u8] {
161            self.as_bytes()
162        }
163    }
164
165    impl AsRef<BytesRef> for StringOwned {
166        fn as_ref(&self) -> &BytesRef {
167            BytesRef::new_unchecked(self.as_bytes())
168        }
169    }
170
171    impl AsRef<StringRef> for StringOwned {
172        fn as_ref(&self) -> &StringRef {
173            StringRef::new_unchecked(&self.inner)
174        }
175    }
176
177    impl Borrow<StringRef> for StringOwned {
178        fn borrow(&self) -> &StringRef {
179            StringRef::new_unchecked(&self.inner)
180        }
181    }
182
183    impl Deref for StringOwned {
184        type Target = StringRef;
185
186        fn deref(&self) -> &StringRef {
187            self.borrow()
188        }
189    }
190
191    impl<'a> DecodeValue<'a> for StringOwned {
192        type Error = Error;
193
194        fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
195            Self::from_bytes(<&'a BytesRef>::decode_value(reader, header)?.as_slice())
196        }
197    }
198
199    impl EncodeValue for StringOwned {
200        fn value_len(&self) -> Result<Length> {
201            Ok(self.length)
202        }
203
204        fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
205            writer.write(self.as_ref())
206        }
207    }
208
209    impl ToOwned for StringRef {
210        type Owned = StringOwned;
211
212        fn to_owned(&self) -> StringOwned {
213            StringOwned {
214                inner: self.as_str().into(),
215                length: self.len(),
216            }
217        }
218    }
219}