Skip to content
/ linux Public
forked from torvalds/linux

Commit

Permalink
rust: str: implement several traits for CStr
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Alex Gaynor <[email protected]>
Co-developed-by: Morgan Bartlett <[email protected]>
Signed-off-by: Morgan Bartlett <[email protected]>
Signed-off-by: Gary Guo <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
  • Loading branch information
nbdd0121 authored and ojeda committed Dec 4, 2022
1 parent d126d23 commit c07e67b
Showing 1 changed file with 123 additions and 1 deletion.
124 changes: 123 additions & 1 deletion rust/kernel/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

//! String representations.

use core::fmt;
use core::fmt::{self, Write};
use core::ops::{self, Deref, Index};

use crate::{
bindings,
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit c07e67b

Please sign in to comment.