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        let nchars: usize = self
188            .utf8_chunks()
189            .map(|chunk| {
190                chunk.valid().chars().count() + if chunk.invalid().is_empty() { 0 } else { 1 }
191            })
192            .sum();
193
194        let padding = f.width().unwrap_or(0).saturating_sub(nchars);
195        let fill = f.fill();
196
197        let (lpad, rpad) = match f.align() {
198            Some(fmt::Alignment::Right) => (padding, 0),
199            Some(fmt::Alignment::Center) => {
200                let half = padding / 2;
201                (half, half + padding % 2)
202            }
203            // Either alignment is not specified or it's left aligned
204            // which behaves the same with padding
205            _ => (0, padding),
206        };
207
208        for _ in 0..lpad {
209            write!(f, "{fill}")?;
210        }
211
212        for chunk in self.utf8_chunks() {
213            f.write_str(chunk.valid())?;
214            if !chunk.invalid().is_empty() {
215                f.write_str("\u{FFFD}")?;
216            }
217        }
218
219        for _ in 0..rpad {
220            write!(f, "{fill}")?;
221        }
222
223        Ok(())
224    }
225}
226
227#[cfg(not(feature = "ferrocene_subset"))]
228#[unstable(feature = "bstr", issue = "134915")]
229#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
230impl const AsRef<[u8]> for ByteStr {
231    #[inline]
232    fn as_ref(&self) -> &[u8] {
233        &self.0
234    }
235}
236
237#[cfg(not(feature = "ferrocene_subset"))]
238#[unstable(feature = "bstr", issue = "134915")]
239#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
240impl const AsRef<ByteStr> for ByteStr {
241    #[inline]
242    fn as_ref(&self) -> &ByteStr {
243        self
244    }
245}
246
247// `impl AsRef<ByteStr> for [u8]` omitted to avoid widespread inference failures
248
249#[cfg(not(feature = "ferrocene_subset"))]
250#[unstable(feature = "bstr", issue = "134915")]
251#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
252impl const AsRef<ByteStr> for str {
253    #[inline]
254    fn as_ref(&self) -> &ByteStr {
255        ByteStr::new(self)
256    }
257}
258
259#[cfg(not(feature = "ferrocene_subset"))]
260#[unstable(feature = "bstr", issue = "134915")]
261#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
262impl const AsMut<[u8]> for ByteStr {
263    #[inline]
264    fn as_mut(&mut self) -> &mut [u8] {
265        &mut self.0
266    }
267}
268
269// `impl AsMut<ByteStr> for [u8]` omitted to avoid widespread inference failures
270
271// `impl Borrow<ByteStr> for [u8]` omitted to avoid widespread inference failures
272
273// `impl Borrow<ByteStr> for str` omitted to avoid widespread inference failures
274
275#[cfg(not(feature = "ferrocene_subset"))]
276#[unstable(feature = "bstr", issue = "134915")]
277#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
278impl const Borrow<[u8]> for ByteStr {
279    #[inline]
280    fn borrow(&self) -> &[u8] {
281        &self.0
282    }
283}
284
285// `impl BorrowMut<ByteStr> for [u8]` omitted to avoid widespread inference failures
286
287#[cfg(not(feature = "ferrocene_subset"))]
288#[unstable(feature = "bstr", issue = "134915")]
289#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
290impl const BorrowMut<[u8]> for ByteStr {
291    #[inline]
292    fn borrow_mut(&mut self) -> &mut [u8] {
293        &mut self.0
294    }
295}
296
297#[cfg(not(feature = "ferrocene_subset"))]
298#[unstable(feature = "bstr", issue = "134915")]
299impl<'a> Default for &'a ByteStr {
300    fn default() -> Self {
301        ByteStr::from_bytes(b"")
302    }
303}
304
305#[cfg(not(feature = "ferrocene_subset"))]
306#[unstable(feature = "bstr", issue = "134915")]
307impl<'a> Default for &'a mut ByteStr {
308    fn default() -> Self {
309        ByteStr::from_bytes_mut(&mut [])
310    }
311}
312
313// Omitted due to inference failures
314//
315// #[unstable(feature = "bstr", issue = "134915")]
316// impl<'a, const N: usize> From<&'a [u8; N]> for &'a ByteStr {
317//     #[inline]
318//     fn from(s: &'a [u8; N]) -> Self {
319//         ByteStr::from_bytes(s)
320//     }
321// }
322//
323// #[unstable(feature = "bstr", issue = "134915")]
324// impl<'a> From<&'a [u8]> for &'a ByteStr {
325//     #[inline]
326//     fn from(s: &'a [u8]) -> Self {
327//         ByteStr::from_bytes(s)
328//     }
329// }
330
331// Omitted due to slice-from-array-issue-113238:
332//
333// #[unstable(feature = "bstr", issue = "134915")]
334// impl<'a> From<&'a ByteStr> for &'a [u8] {
335//     #[inline]
336//     fn from(s: &'a ByteStr) -> Self {
337//         &s.0
338//     }
339// }
340//
341// #[unstable(feature = "bstr", issue = "134915")]
342// impl<'a> From<&'a mut ByteStr> for &'a mut [u8] {
343//     #[inline]
344//     fn from(s: &'a mut ByteStr) -> Self {
345//         &mut s.0
346//     }
347// }
348
349// Omitted due to inference failures
350//
351// #[unstable(feature = "bstr", issue = "134915")]
352// impl<'a> From<&'a str> for &'a ByteStr {
353//     #[inline]
354//     fn from(s: &'a str) -> Self {
355//         ByteStr::from_bytes(s.as_bytes())
356//     }
357// }
358
359#[cfg(not(feature = "ferrocene_subset"))]
360#[unstable(feature = "bstr", issue = "134915")]
361#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
362impl<'a> const TryFrom<&'a ByteStr> for &'a str {
363    type Error = crate::str::Utf8Error;
364
365    #[inline]
366    fn try_from(s: &'a ByteStr) -> Result<Self, Self::Error> {
367        crate::str::from_utf8(&s.0)
368    }
369}
370
371#[cfg(not(feature = "ferrocene_subset"))]
372#[unstable(feature = "bstr", issue = "134915")]
373#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
374impl<'a> const TryFrom<&'a mut ByteStr> for &'a mut str {
375    type Error = crate::str::Utf8Error;
376
377    #[inline]
378    fn try_from(s: &'a mut ByteStr) -> Result<Self, Self::Error> {
379        crate::str::from_utf8_mut(&mut s.0)
380    }
381}