Skip to main content

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