Skip to main content

core/bstr/
mod.rs

1//! The `ByteStr` type and trait implementations.
2
3#[cfg(not(feature = "ferrocene_subset"))]
4mod traits;
5
6#[unstable(feature = "bstr_internals", issue = "none")]
7#[cfg(not(feature = "ferrocene_subset"))]
8pub use traits::{impl_partial_eq, impl_partial_eq_n, impl_partial_eq_ord};
9
10#[cfg(not(feature = "ferrocene_subset"))]
11use crate::borrow::{Borrow, BorrowMut};
12use crate::fmt;
13use crate::ops::{Deref, DerefMut, DerefPure};
14
15/// A wrapper for `&[u8]` representing a human-readable string that's conventionally, but not
16/// always, UTF-8.
17///
18/// Unlike `&str`, this type permits non-UTF-8 contents, making it suitable for user input,
19/// non-native filenames (as `Path` only supports native filenames), and other applications that
20/// need to round-trip whatever data the user provides.
21///
22/// For an owned, growable byte string buffer, use
23/// [`ByteString`](../../std/bstr/struct.ByteString.html).
24///
25/// `ByteStr` implements `Deref` to `[u8]`, so all methods available on `[u8]` are available on
26/// `ByteStr`.
27///
28/// # Representation
29///
30/// A `&ByteStr` has the same representation as a `&str`. That is, a `&ByteStr` is a wide pointer
31/// which includes a pointer to some bytes and a length.
32///
33/// # Trait implementations
34///
35/// The `ByteStr` type has a number of trait implementations, and in particular, defines equality
36/// and comparisons between `&ByteStr`, `&str`, and `&[u8]`, for convenience.
37///
38/// The `Debug` implementation for `ByteStr` shows its bytes as a normal string, with invalid UTF-8
39/// presented as hex escape sequences.
40///
41/// The `Display` implementation behaves as if the `ByteStr` were first lossily converted to a
42/// `str`, with invalid UTF-8 presented as the Unicode replacement character (�).
43#[unstable(feature = "bstr", issue = "134915")]
44#[repr(transparent)]
45#[doc(alias = "BStr")]
46pub struct ByteStr(pub [u8]);
47
48impl ByteStr {
49    /// Creates a `ByteStr` slice from anything that can be converted to a byte slice.
50    ///
51    /// This is a zero-cost conversion.
52    ///
53    /// # Example
54    ///
55    /// You can create a `ByteStr` from a byte array, a byte slice or a string slice:
56    ///
57    /// ```
58    /// # #![feature(bstr)]
59    /// # use std::bstr::ByteStr;
60    /// let a = ByteStr::new(b"abc");
61    /// let b = ByteStr::new(&b"abc"[..]);
62    /// let c = ByteStr::new("abc");
63    ///
64    /// assert_eq!(a, b);
65    /// assert_eq!(a, c);
66    /// ```
67    #[cfg(not(feature = "ferrocene_subset"))]
68    #[inline]
69    #[unstable(feature = "bstr", issue = "134915")]
70    #[rustc_const_unstable(feature = "const_convert", issue = "143773")]
71    pub const fn new<B: ?Sized + [const] AsRef<[u8]>>(bytes: &B) -> &Self {
72        ByteStr::from_bytes(bytes.as_ref())
73    }
74
75    /// Returns the same string as `&ByteStr`.
76    ///
77    /// This method is redundant when used directly on `&ByteStr`, but
78    /// it helps dereferencing other "container" types,
79    /// for example `Box<ByteStr>` or `Arc<ByteStr>`.
80    #[cfg(not(feature = "ferrocene_subset"))]
81    #[inline]
82    // #[unstable(feature = "str_as_str", issue = "130366")]
83    #[unstable(feature = "bstr", issue = "134915")]
84    pub const fn as_byte_str(&self) -> &ByteStr {
85        self
86    }
87
88    /// Returns the same string as `&mut ByteStr`.
89    ///
90    /// This method is redundant when used directly on `&mut ByteStr`, but
91    /// it helps dereferencing other "container" types,
92    /// for example `Box<ByteStr>` or `MutexGuard<ByteStr>`.
93    #[cfg(not(feature = "ferrocene_subset"))]
94    #[inline]
95    // #[unstable(feature = "str_as_str", issue = "130366")]
96    #[unstable(feature = "bstr", issue = "134915")]
97    pub const fn as_mut_byte_str(&mut self) -> &mut ByteStr {
98        self
99    }
100
101    #[doc(hidden)]
102    #[unstable(feature = "bstr_internals", issue = "none")]
103    #[inline]
104    #[rustc_const_unstable(feature = "bstr_internals", issue = "none")]
105    pub const fn from_bytes(slice: &[u8]) -> &Self {
106        // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`, so we can turn a reference to
107        // the wrapped type into a reference to the wrapper type.
108        unsafe { &*(slice as *const [u8] as *const Self) }
109    }
110
111    #[cfg(not(feature = "ferrocene_subset"))]
112    #[doc(hidden)]
113    #[unstable(feature = "bstr_internals", issue = "none")]
114    #[inline]
115    #[rustc_const_unstable(feature = "bstr_internals", issue = "none")]
116    pub const fn from_bytes_mut(slice: &mut [u8]) -> &mut Self {
117        // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`, so we can turn a reference to
118        // the wrapped type into a reference to the wrapper type.
119        unsafe { &mut *(slice as *mut [u8] as *mut Self) }
120    }
121
122    #[cfg(not(feature = "ferrocene_subset"))]
123    #[doc(hidden)]
124    #[unstable(feature = "bstr_internals", issue = "none")]
125    #[inline]
126    #[rustc_const_unstable(feature = "bstr_internals", issue = "none")]
127    pub const fn as_bytes(&self) -> &[u8] {
128        &self.0
129    }
130
131    #[cfg(not(feature = "ferrocene_subset"))]
132    #[doc(hidden)]
133    #[unstable(feature = "bstr_internals", issue = "none")]
134    #[inline]
135    #[rustc_const_unstable(feature = "bstr_internals", issue = "none")]
136    pub const fn as_bytes_mut(&mut self) -> &mut [u8] {
137        &mut self.0
138    }
139}
140
141#[unstable(feature = "bstr", issue = "134915")]
142#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
143impl const Deref for ByteStr {
144    type Target = [u8];
145
146    #[inline]
147    fn deref(&self) -> &[u8] {
148        &self.0
149    }
150}
151
152#[unstable(feature = "bstr", issue = "134915")]
153#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
154impl const DerefMut for ByteStr {
155    #[inline]
156    fn deref_mut(&mut self) -> &mut [u8] {
157        &mut self.0
158    }
159}
160
161#[unstable(feature = "deref_pure_trait", issue = "87121")]
162unsafe impl DerefPure for ByteStr {}
163
164#[unstable(feature = "bstr", issue = "134915")]
165impl fmt::Debug for ByteStr {
166    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167        write!(f, "\"")?;
168        for chunk in self.utf8_chunks() {
169            for c in chunk.valid().chars() {
170                match c {
171                    '\0' => write!(f, "\\0")?,
172                    '\x01'..='\x7f' => write!(f, "{}", (c as u8).escape_ascii())?,
173                    _ => write!(f, "{}", c.escape_debug())?,
174                }
175            }
176            write!(f, "{}", chunk.invalid().escape_ascii())?;
177        }
178        write!(f, "\"")?;
179        Ok(())
180    }
181}
182
183#[cfg(not(feature = "ferrocene_subset"))]
184#[unstable(feature = "bstr", issue = "134915")]
185impl fmt::Display for ByteStr {
186    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187        fn fmt_nopad(this: &ByteStr, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188            for chunk in this.utf8_chunks() {
189                f.write_str(chunk.valid())?;
190                if !chunk.invalid().is_empty() {
191                    f.write_str("\u{FFFD}")?;
192                }
193            }
194            Ok(())
195        }
196
197        let Some(align) = f.align() else {
198            return fmt_nopad(self, f);
199        };
200        let nchars: usize = self
201            .utf8_chunks()
202            .map(|chunk| {
203                chunk.valid().chars().count() + if chunk.invalid().is_empty() { 0 } else { 1 }
204            })
205            .sum();
206        let padding = f.width().unwrap_or(0).saturating_sub(nchars);
207        let fill = f.fill();
208        let (lpad, rpad) = match align {
209            fmt::Alignment::Left => (0, padding),
210            fmt::Alignment::Right => (padding, 0),
211            fmt::Alignment::Center => {
212                let half = padding / 2;
213                (half, half + padding % 2)
214            }
215        };
216        for _ in 0..lpad {
217            write!(f, "{fill}")?;
218        }
219        fmt_nopad(self, f)?;
220        for _ in 0..rpad {
221            write!(f, "{fill}")?;
222        }
223
224        Ok(())
225    }
226}
227
228#[cfg(not(feature = "ferrocene_subset"))]
229#[unstable(feature = "bstr", issue = "134915")]
230#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
231impl const AsRef<[u8]> for ByteStr {
232    #[inline]
233    fn as_ref(&self) -> &[u8] {
234        &self.0
235    }
236}
237
238#[cfg(not(feature = "ferrocene_subset"))]
239#[unstable(feature = "bstr", issue = "134915")]
240#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
241impl const AsRef<ByteStr> for ByteStr {
242    #[inline]
243    fn as_ref(&self) -> &ByteStr {
244        self
245    }
246}
247
248// `impl AsRef<ByteStr> for [u8]` omitted to avoid widespread inference failures
249
250#[cfg(not(feature = "ferrocene_subset"))]
251#[unstable(feature = "bstr", issue = "134915")]
252#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
253impl const AsRef<ByteStr> for str {
254    #[inline]
255    fn as_ref(&self) -> &ByteStr {
256        ByteStr::new(self)
257    }
258}
259
260#[cfg(not(feature = "ferrocene_subset"))]
261#[unstable(feature = "bstr", issue = "134915")]
262#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
263impl const AsMut<[u8]> for ByteStr {
264    #[inline]
265    fn as_mut(&mut self) -> &mut [u8] {
266        &mut self.0
267    }
268}
269
270// `impl AsMut<ByteStr> for [u8]` omitted to avoid widespread inference failures
271
272// `impl Borrow<ByteStr> for [u8]` omitted to avoid widespread inference failures
273
274// `impl Borrow<ByteStr> for str` omitted to avoid widespread inference failures
275
276#[cfg(not(feature = "ferrocene_subset"))]
277#[unstable(feature = "bstr", issue = "134915")]
278#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
279impl const Borrow<[u8]> for ByteStr {
280    #[inline]
281    fn borrow(&self) -> &[u8] {
282        &self.0
283    }
284}
285
286// `impl BorrowMut<ByteStr> for [u8]` omitted to avoid widespread inference failures
287
288#[cfg(not(feature = "ferrocene_subset"))]
289#[unstable(feature = "bstr", issue = "134915")]
290#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
291impl const BorrowMut<[u8]> for ByteStr {
292    #[inline]
293    fn borrow_mut(&mut self) -> &mut [u8] {
294        &mut self.0
295    }
296}
297
298#[cfg(not(feature = "ferrocene_subset"))]
299#[unstable(feature = "bstr", issue = "134915")]
300impl<'a> Default for &'a ByteStr {
301    fn default() -> Self {
302        ByteStr::from_bytes(b"")
303    }
304}
305
306#[cfg(not(feature = "ferrocene_subset"))]
307#[unstable(feature = "bstr", issue = "134915")]
308impl<'a> Default for &'a mut ByteStr {
309    fn default() -> Self {
310        ByteStr::from_bytes_mut(&mut [])
311    }
312}
313
314// Omitted due to inference failures
315//
316// #[unstable(feature = "bstr", issue = "134915")]
317// impl<'a, const N: usize> From<&'a [u8; N]> for &'a ByteStr {
318//     #[inline]
319//     fn from(s: &'a [u8; N]) -> Self {
320//         ByteStr::from_bytes(s)
321//     }
322// }
323//
324// #[unstable(feature = "bstr", issue = "134915")]
325// impl<'a> From<&'a [u8]> for &'a ByteStr {
326//     #[inline]
327//     fn from(s: &'a [u8]) -> Self {
328//         ByteStr::from_bytes(s)
329//     }
330// }
331
332// Omitted due to slice-from-array-issue-113238:
333//
334// #[unstable(feature = "bstr", issue = "134915")]
335// impl<'a> From<&'a ByteStr> for &'a [u8] {
336//     #[inline]
337//     fn from(s: &'a ByteStr) -> Self {
338//         &s.0
339//     }
340// }
341//
342// #[unstable(feature = "bstr", issue = "134915")]
343// impl<'a> From<&'a mut ByteStr> for &'a mut [u8] {
344//     #[inline]
345//     fn from(s: &'a mut ByteStr) -> Self {
346//         &mut s.0
347//     }
348// }
349
350// Omitted due to inference failures
351//
352// #[unstable(feature = "bstr", issue = "134915")]
353// impl<'a> From<&'a str> for &'a ByteStr {
354//     #[inline]
355//     fn from(s: &'a str) -> Self {
356//         ByteStr::from_bytes(s.as_bytes())
357//     }
358// }
359
360#[cfg(not(feature = "ferrocene_subset"))]
361#[unstable(feature = "bstr", issue = "134915")]
362#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
363impl<'a> const TryFrom<&'a ByteStr> for &'a str {
364    type Error = crate::str::Utf8Error;
365
366    #[inline]
367    fn try_from(s: &'a ByteStr) -> Result<Self, Self::Error> {
368        crate::str::from_utf8(&s.0)
369    }
370}
371
372#[cfg(not(feature = "ferrocene_subset"))]
373#[unstable(feature = "bstr", issue = "134915")]
374#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
375impl<'a> const TryFrom<&'a mut ByteStr> for &'a mut str {
376    type Error = crate::str::Utf8Error;
377
378    #[inline]
379    fn try_from(s: &'a mut ByteStr) -> Result<Self, Self::Error> {
380        crate::str::from_utf8_mut(&mut s.0)
381    }
382}