Skip to main content

core/bstr/
mod.rs

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