minibytes/
text.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8use std::any::Any;
9use std::borrow::Cow;
10
11use super::bytes::AbstractBytes;
12use super::bytes::AbstractOwner;
13use super::bytes::SliceLike;
14use crate::Bytes;
15
16pub type Text = AbstractBytes<str>;
17pub trait TextOwner: AsRef<str> + Send + Sync + 'static {}
18
19impl<T: TextOwner> AbstractOwner<str> for T {
20    fn as_any_mut(&mut self) -> &mut dyn Any {
21        self
22    }
23}
24
25impl Text {
26    /// Creates `Text` from a static str.
27    pub const fn from_static(slice: &'static str) -> Self {
28        Self {
29            ptr: slice.as_ptr(),
30            len: slice.len(),
31            owner: None,
32        }
33    }
34
35    /// Creates `Text` from utf-8 encoded `Bytes`.
36    /// Zero-copy if possible.
37    pub fn from_utf8_lossy(bytes: Bytes) -> Self {
38        match String::from_utf8_lossy(bytes.as_slice()) {
39            Cow::Borrowed(..) => {
40                // safety: utf-8 is checked by `from_utf8_lossy`.
41                unsafe { Self::from_utf8_unchecked(bytes) }
42            }
43            Cow::Owned(s) => Self::from_owner(s),
44        }
45    }
46
47    /// Creates `Text` from utf-8 `Bytes` in a zero-copy way.
48    /// Safety: the `bytes` must be valid UTF-8.
49    pub unsafe fn from_utf8_unchecked(bytes: Bytes) -> Self {
50        struct Utf8Bytes(Bytes);
51        impl AsRef<str> for Utf8Bytes {
52            fn as_ref(&self) -> &str {
53                // safety: `Utf8Bytes` is only constructed by
54                // `from_utf8_unchecked`, which is marked `unsafe`.
55                unsafe { std::str::from_utf8_unchecked(self.0.as_slice()) }
56            }
57        }
58        impl TextOwner for Utf8Bytes {}
59        Self::from_owner(Utf8Bytes(bytes))
60    }
61
62    #[inline]
63    pub(crate) fn as_slice(&self) -> &str {
64        let bytes = self.as_bytes();
65        // bytes was validated as utf-8.
66        unsafe { std::str::from_utf8_unchecked(bytes) }
67    }
68}
69
70impl Bytes {
71    /// Same as `Text::from_utf8_lossy`.
72    pub fn into_text_lossy(self) -> Text {
73        Text::from_utf8_lossy(self)
74    }
75}
76
77impl SliceLike for str {
78    type Owned = String;
79    const EMPTY: &'static Self = "";
80
81    #[inline]
82    fn check_slice_bytes(bytes: &[u8], start: usize, end: usize) {
83        // called by AbstractBytes::slice, bytes was validated as utf-8.
84        let s = unsafe { std::str::from_utf8_unchecked(bytes) };
85        // check whether the slicing is valid.
86        let _ = s[start..end];
87    }
88    #[inline]
89    fn as_bytes(&self) -> &[u8] {
90        self.as_bytes()
91    }
92    #[inline]
93    fn to_owned(&self) -> Self::Owned {
94        self.to_string()
95    }
96}