ntex_bytes/
string.rs

1//! A UTF-8 encoded read-only string using Bytes as storage.
2use std::{borrow, fmt, hash, ops, slice, str};
3
4use crate::{Bytes, BytesMut};
5
6/// An immutable UTF-8 encoded string with [`Bytes`] as a storage.
7#[derive(Clone, Default, Eq, PartialOrd, Ord)]
8pub struct ByteString(Bytes);
9
10impl ByteString {
11    /// Creates a new empty `ByteString`.
12    #[inline]
13    pub const fn new() -> Self {
14        ByteString(Bytes::new())
15    }
16
17    /// Get a str slice.
18    #[inline]
19    pub fn as_str(&self) -> &str {
20        self
21    }
22
23    /// Get a reference to the underlying bytes.
24    #[inline]
25    pub fn as_slice(&self) -> &[u8] {
26        self.0.as_ref()
27    }
28
29    /// Get a reference to the underlying `Bytes` object.
30    #[inline]
31    pub fn as_bytes(&self) -> &Bytes {
32        &self.0
33    }
34
35    /// Unwraps this `ByteString` into the underlying `Bytes` object.
36    #[inline]
37    pub fn into_bytes(self) -> Bytes {
38        self.0
39    }
40
41    /// Creates a new `ByteString` from a `&'static str`.
42    #[inline]
43    pub const fn from_static(src: &'static str) -> ByteString {
44        Self(Bytes::from_static(src.as_bytes()))
45    }
46
47    /// Returns a slice of self for the provided range.
48    ///
49    /// This will increment the reference count for the underlying memory and
50    /// return a new `ByteString` handle set to the slice.
51    ///
52    /// This operation is `O(1)`.
53    ///
54    /// # Examples
55    ///
56    /// ```
57    /// use ntex_bytes::ByteString;
58    ///
59    /// let a = ByteString::from("hello world");
60    /// let b = a.slice(2..5);
61    ///
62    /// assert_eq!(b, "llo");
63    /// ```
64    ///
65    /// # Panics
66    ///
67    /// Requires that `begin <= end` and `end <= self.len()`, otherwise slicing
68    /// will panic.
69    pub fn slice(
70        &self,
71        range: impl ops::RangeBounds<usize> + slice::SliceIndex<str> + Clone,
72    ) -> ByteString {
73        ops::Index::index(self.as_ref(), range.clone());
74        ByteString(self.0.slice(range))
75    }
76
77    /// Splits the bytestring into two at the given index.
78    ///
79    /// Afterwards `self` contains elements `[0, at)`, and the returned `ByteString`
80    /// contains elements `[at, len)`.
81    ///
82    /// This is an `O(1)` operation that just increases the reference count and
83    /// sets a few indices.
84    ///
85    /// # Examples
86    ///
87    /// ```
88    /// use ntex_bytes::ByteString;
89    ///
90    /// let mut a = ByteString::from("hello world");
91    /// let b = a.split_off(5);
92    ///
93    /// assert_eq!(a, "hello");
94    /// assert_eq!(b, " world");
95    /// ```
96    ///
97    /// # Panics
98    ///
99    /// Panics if `at > len`.
100    pub fn split_off(&mut self, at: usize) -> ByteString {
101        // check str
102        let _ = self.split_at(at);
103
104        ByteString(self.0.split_off(at))
105    }
106
107    /// Splits the bytestring into two at the given index.
108    ///
109    /// Afterwards `self` contains elements `[at, len)`, and the returned
110    /// `Bytes` contains elements `[0, at)`.
111    ///
112    /// This is an `O(1)` operation that just increases the reference count and
113    /// sets a few indices.
114    ///
115    /// # Examples
116    ///
117    /// ```
118    /// use ntex_bytes::ByteString;
119    ///
120    /// let mut a = ByteString::from("hello world");
121    /// let b = a.split_to(5);
122    ///
123    /// assert_eq!(a, " world");
124    /// assert_eq!(b, "hello");
125    /// ```
126    ///
127    /// # Panics
128    ///
129    /// Panics if `at > len`.
130    pub fn split_to(&mut self, at: usize) -> ByteString {
131        // check str
132        let _ = self.split_at(at);
133
134        ByteString(self.0.split_to(at))
135    }
136
137    /// Shortens the buffer to `len` bytes and dropping the rest.
138    #[inline]
139    pub fn trimdown(&mut self) {
140        self.0.trimdown()
141    }
142
143    /// Clears the buffer, removing all data.
144    ///
145    /// # Examples
146    ///
147    /// ```
148    /// use ntex_bytes::ByteString;
149    ///
150    /// let mut a = ByteString::from("hello world");
151    /// a.clear();
152    ///
153    /// assert!(a.is_empty());
154    /// ```
155    #[inline]
156    pub fn clear(&mut self) {
157        self.0.clear()
158    }
159
160    /// Creates a new `ByteString` from a Bytes.
161    ///
162    /// # Safety
163    /// This function is unsafe because it does not check the bytes passed to it are valid UTF-8.
164    /// If this constraint is violated, it may cause memory unsafety issues with future users of
165    /// the `ByteString`, as we assume that `ByteString`s are valid UTF-8. However, the most likely
166    /// issue is that the data gets corrupted.
167    #[inline]
168    pub const unsafe fn from_bytes_unchecked(src: Bytes) -> ByteString {
169        Self(src)
170    }
171}
172
173impl PartialEq<str> for ByteString {
174    fn eq(&self, other: &str) -> bool {
175        &self[..] == other
176    }
177}
178
179impl<T: AsRef<str>> PartialEq<T> for ByteString {
180    fn eq(&self, other: &T) -> bool {
181        &self[..] == other.as_ref()
182    }
183}
184
185impl PartialEq<ByteString> for &ByteString {
186    fn eq(&self, other: &ByteString) -> bool {
187        self.as_str() == other.as_str()
188    }
189}
190
191impl PartialEq<ByteString> for str {
192    fn eq(&self, other: &ByteString) -> bool {
193        self == other.as_str()
194    }
195}
196
197impl PartialEq<ByteString> for &str {
198    fn eq(&self, other: &ByteString) -> bool {
199        *self == other.as_str()
200    }
201}
202
203impl AsRef<str> for ByteString {
204    #[inline]
205    fn as_ref(&self) -> &str {
206        self
207    }
208}
209
210impl hash::Hash for ByteString {
211    fn hash<H: hash::Hasher>(&self, state: &mut H) {
212        (**self).hash(state);
213    }
214}
215
216impl ops::Deref for ByteString {
217    type Target = str;
218
219    #[inline]
220    fn deref(&self) -> &str {
221        let bytes = self.0.as_ref();
222        // SAFETY:
223        // UTF-8 validity is guaranteed during construction.
224        unsafe { str::from_utf8_unchecked(bytes) }
225    }
226}
227
228impl borrow::Borrow<str> for ByteString {
229    #[inline]
230    fn borrow(&self) -> &str {
231        self
232    }
233}
234
235impl From<String> for ByteString {
236    #[inline]
237    fn from(value: String) -> Self {
238        Self(Bytes::from(value))
239    }
240}
241
242impl From<&str> for ByteString {
243    #[inline]
244    fn from(value: &str) -> Self {
245        Self(Bytes::copy_from_slice(value.as_ref()))
246    }
247}
248
249impl From<&ByteString> for ByteString {
250    #[inline]
251    fn from(value: &ByteString) -> Self {
252        value.clone()
253    }
254}
255
256impl<'a> From<borrow::Cow<'a, str>> for ByteString {
257    #[inline]
258    fn from(value: borrow::Cow<'a, str>) -> Self {
259        match value {
260            borrow::Cow::Owned(s) => Self::from(s),
261            borrow::Cow::Borrowed(s) => Self::from(s),
262        }
263    }
264}
265
266impl TryFrom<&[u8]> for ByteString {
267    type Error = ();
268
269    #[inline]
270    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
271        if utf8::is_valid(value) {
272            Ok(ByteString(Bytes::copy_from_slice(value)))
273        } else {
274            Err(())
275        }
276    }
277}
278
279impl TryFrom<Vec<u8>> for ByteString {
280    type Error = ();
281
282    #[inline]
283    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
284        if utf8::is_valid(&value) {
285            Ok(ByteString(Bytes::from(value)))
286        } else {
287            Err(())
288        }
289    }
290}
291
292impl TryFrom<Bytes> for ByteString {
293    type Error = ();
294
295    #[inline]
296    fn try_from(value: Bytes) -> Result<Self, Self::Error> {
297        if utf8::is_valid(&value) {
298            Ok(ByteString(value))
299        } else {
300            Err(())
301        }
302    }
303}
304
305impl TryFrom<&Bytes> for ByteString {
306    type Error = ();
307
308    #[inline]
309    fn try_from(value: &Bytes) -> Result<Self, Self::Error> {
310        if utf8::is_valid(value) {
311            Ok(ByteString(value.clone()))
312        } else {
313            Err(())
314        }
315    }
316}
317
318impl TryFrom<BytesMut> for ByteString {
319    type Error = ();
320
321    #[inline]
322    fn try_from(value: BytesMut) -> Result<Self, Self::Error> {
323        if utf8::is_valid(&value) {
324            Ok(ByteString(value.freeze()))
325        } else {
326            Err(())
327        }
328    }
329}
330
331impl fmt::Debug for ByteString {
332    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
333        (**self).fmt(fmt)
334    }
335}
336
337impl fmt::Display for ByteString {
338    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
339        (**self).fmt(fmt)
340    }
341}
342
343mod serde {
344    use serde::de::{Deserialize, Deserializer};
345    use serde::ser::{Serialize, Serializer};
346
347    use super::ByteString;
348
349    impl Serialize for ByteString {
350        #[inline]
351        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
352        where
353            S: Serializer,
354        {
355            serializer.serialize_str(self.as_ref())
356        }
357    }
358
359    impl<'de> Deserialize<'de> for ByteString {
360        #[inline]
361        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
362        where
363            D: Deserializer<'de>,
364        {
365            String::deserialize(deserializer).map(ByteString::from)
366        }
367    }
368}
369
370#[cfg(feature = "simd")]
371mod utf8 {
372    pub(super) fn is_valid(input: &[u8]) -> bool {
373        simdutf8::basic::from_utf8(input).is_ok()
374    }
375}
376
377#[cfg(not(feature = "simd"))]
378mod utf8 {
379    pub(super) fn is_valid(input: &[u8]) -> bool {
380        std::str::from_utf8(input).is_ok()
381    }
382}
383
384#[cfg(test)]
385mod test {
386    use std::borrow::{Borrow, Cow};
387    use std::collections::hash_map::DefaultHasher;
388    use std::hash::{Hash, Hasher};
389
390    use super::*;
391
392    #[test]
393    fn test_basics() {
394        let mut s = ByteString::from_static("test");
395        s.trimdown();
396        assert_eq!(s, "test");
397        assert_eq!(s, *"test");
398        assert_eq!(s, "test".to_owned());
399        assert_eq!(s.as_str(), "test");
400        assert_eq!(s.as_slice(), b"test");
401        assert_eq!(s.as_bytes(), &Bytes::copy_from_slice(b"test"));
402        assert_eq!(Borrow::<str>::borrow(&s), "test");
403        assert_eq!("test", s);
404        assert_eq!("test", &s);
405
406        assert_eq!(format!("{s}"), "test");
407        assert_eq!(format!("{s:?}"), "\"test\"");
408
409        let b = s.into_bytes();
410        assert_eq!(b, Bytes::copy_from_slice(b"test"));
411
412        let s = unsafe { ByteString::from_bytes_unchecked(b) };
413        assert_eq!(s, "test");
414        assert_eq!(s.slice(0..2), "te");
415
416        let s = ByteString::from(Cow::Borrowed("test"));
417        assert_eq!(s, "test");
418        let mut s = ByteString::from(Cow::Owned("test".to_string()));
419        assert_eq!(s, "test");
420
421        s.clear();
422        assert_eq!(s, "");
423    }
424
425    #[test]
426    fn test_split() {
427        let mut s = ByteString::from_static("helloworld");
428        let s1 = s.split_off(5);
429        assert_eq!(s, "hello");
430        assert_eq!(s1, "world");
431
432        let mut s = ByteString::from_static("helloworld");
433        let s1 = s.split_to(5);
434        assert_eq!(s, "world");
435        assert_eq!(s1, "hello");
436    }
437
438    #[test]
439    fn test_new() {
440        let _: ByteString = ByteString::new();
441    }
442
443    #[test]
444    fn test_hash() {
445        let mut hasher1 = DefaultHasher::default();
446        "str".hash(&mut hasher1);
447
448        let mut hasher2 = DefaultHasher::default();
449        let s = ByteString::from_static("str");
450        s.hash(&mut hasher2);
451        assert_eq!(hasher1.finish(), hasher2.finish());
452    }
453
454    #[test]
455    fn test_from() {
456        // String
457        let s: ByteString = "hello".to_owned().into();
458        assert_eq!(&s, "hello");
459        let t: &str = s.as_ref();
460        assert_eq!(t, "hello");
461
462        // str
463        let _: ByteString = "str".into();
464
465        // static str
466        static _S: ByteString = ByteString::from_static("hello");
467        let _ = ByteString::from_static("str");
468
469        let s = ByteString::from_static("hello");
470        let s1 = ByteString::from(&s);
471        assert_eq!(s1, "hello");
472    }
473
474    #[test]
475    fn test_try_from() {
476        let _ = ByteString::try_from(&b"nice bytes"[..]).unwrap();
477        assert!(ByteString::try_from(b"\xc3\x28".as_ref()).is_err());
478
479        let _ = ByteString::try_from(b"nice bytes".to_vec()).unwrap();
480        assert!(ByteString::try_from(vec![b'\xc3']).is_err());
481
482        let _ = ByteString::try_from(Bytes::from_static(b"nice bytes")).unwrap();
483        assert!(ByteString::try_from(Bytes::from_static(b"\xc3\x28")).is_err());
484
485        let _ = ByteString::try_from(&Bytes::from_static(b"nice bytes")).unwrap();
486        assert!(ByteString::try_from(&Bytes::from_static(b"\xc3\x28")).is_err());
487
488        let _ =
489            ByteString::try_from(BytesMut::copy_from_slice(&b"nice bytes"[..])).unwrap();
490        assert!(ByteString::try_from(BytesMut::copy_from_slice(b"\xc3\x28")).is_err());
491    }
492
493    #[test]
494    fn test_serialize() {
495        let s: ByteString = serde_json::from_str(r#""nice bytes""#).unwrap();
496        assert_eq!(s, "nice bytes");
497    }
498
499    #[test]
500    fn test_deserialize() {
501        let s = serde_json::to_string(&ByteString::from_static("nice bytes")).unwrap();
502        assert_eq!(s, r#""nice bytes""#);
503    }
504}