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