mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 21:21:41 +00:00
rust: str: implement several traits for CStr
Implement `Debug`, `Display`, `Deref` (into `BStr`), `AsRef<BStr>` and a set of `Index<...>` traits. This makes it `CStr` more convenient to use (and closer to `str`). Co-developed-by: Alex Gaynor <alex.gaynor@gmail.com> Signed-off-by: Alex Gaynor <alex.gaynor@gmail.com> Co-developed-by: Morgan Bartlett <mjmouse9999@gmail.com> Signed-off-by: Morgan Bartlett <mjmouse9999@gmail.com> Signed-off-by: Gary Guo <gary@garyguo.net> [Reworded, adapted for upstream and applied latest changes] Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
This commit is contained in:
parent
d126d23801
commit
c07e67bd2d
@ -2,7 +2,8 @@
|
||||
|
||||
//! String representations.
|
||||
|
||||
use core::fmt;
|
||||
use core::fmt::{self, Write};
|
||||
use core::ops::{self, Deref, Index};
|
||||
|
||||
use crate::{
|
||||
bindings,
|
||||
@ -199,6 +200,127 @@ impl CStr {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for CStr {
|
||||
/// Formats printable ASCII characters, escaping the rest.
|
||||
///
|
||||
/// ```
|
||||
/// # use kernel::c_str;
|
||||
/// # use kernel::str::CStr;
|
||||
/// # use kernel::str::CString;
|
||||
/// let penguin = c_str!("🐧");
|
||||
/// let s = CString::try_from_fmt(fmt!("{}", penguin)).unwrap();
|
||||
/// assert_eq!(s.as_bytes_with_nul(), "\\xf0\\x9f\\x90\\xa7\0".as_bytes());
|
||||
///
|
||||
/// let ascii = c_str!("so \"cool\"");
|
||||
/// let s = CString::try_from_fmt(fmt!("{}", ascii)).unwrap();
|
||||
/// assert_eq!(s.as_bytes_with_nul(), "so \"cool\"\0".as_bytes());
|
||||
/// ```
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
for &c in self.as_bytes() {
|
||||
if (0x20..0x7f).contains(&c) {
|
||||
// Printable character.
|
||||
f.write_char(c as char)?;
|
||||
} else {
|
||||
write!(f, "\\x{:02x}", c)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for CStr {
|
||||
/// Formats printable ASCII characters with a double quote on either end, escaping the rest.
|
||||
///
|
||||
/// ```
|
||||
/// # use kernel::c_str;
|
||||
/// # use kernel::str::CStr;
|
||||
/// # use kernel::str::CString;
|
||||
/// let penguin = c_str!("🐧");
|
||||
/// let s = CString::try_from_fmt(fmt!("{:?}", penguin)).unwrap();
|
||||
/// assert_eq!(s.as_bytes_with_nul(), "\"\\xf0\\x9f\\x90\\xa7\"\0".as_bytes());
|
||||
///
|
||||
/// // Embedded double quotes are escaped.
|
||||
/// let ascii = c_str!("so \"cool\"");
|
||||
/// let s = CString::try_from_fmt(fmt!("{:?}", ascii)).unwrap();
|
||||
/// assert_eq!(s.as_bytes_with_nul(), "\"so \\\"cool\\\"\"\0".as_bytes());
|
||||
/// ```
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("\"")?;
|
||||
for &c in self.as_bytes() {
|
||||
match c {
|
||||
// Printable characters.
|
||||
b'\"' => f.write_str("\\\"")?,
|
||||
0x20..=0x7e => f.write_char(c as char)?,
|
||||
_ => write!(f, "\\x{:02x}", c)?,
|
||||
}
|
||||
}
|
||||
f.write_str("\"")
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<BStr> for CStr {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &BStr {
|
||||
self.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for CStr {
|
||||
type Target = BStr;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<ops::RangeFrom<usize>> for CStr {
|
||||
type Output = CStr;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: ops::RangeFrom<usize>) -> &Self::Output {
|
||||
// Delegate bounds checking to slice.
|
||||
// Assign to _ to mute clippy's unnecessary operation warning.
|
||||
let _ = &self.as_bytes()[index.start..];
|
||||
// SAFETY: We just checked the bounds.
|
||||
unsafe { Self::from_bytes_with_nul_unchecked(&self.0[index.start..]) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<ops::RangeFull> for CStr {
|
||||
type Output = CStr;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, _index: ops::RangeFull) -> &Self::Output {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
mod private {
|
||||
use core::ops;
|
||||
|
||||
// Marker trait for index types that can be forward to `BStr`.
|
||||
pub trait CStrIndex {}
|
||||
|
||||
impl CStrIndex for usize {}
|
||||
impl CStrIndex for ops::Range<usize> {}
|
||||
impl CStrIndex for ops::RangeInclusive<usize> {}
|
||||
impl CStrIndex for ops::RangeToInclusive<usize> {}
|
||||
}
|
||||
|
||||
impl<Idx> Index<Idx> for CStr
|
||||
where
|
||||
Idx: private::CStrIndex,
|
||||
BStr: Index<Idx>,
|
||||
{
|
||||
type Output = <BStr as Index<Idx>>::Output;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: Idx) -> &Self::Output {
|
||||
&self.as_bytes()[index]
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows formatting of [`fmt::Arguments`] into a raw buffer.
|
||||
///
|
||||
/// It does not fail if callers write past the end of the buffer so that they can calculate the
|
||||
|
Loading…
Reference in New Issue
Block a user