// SPDX-License-Identifier: GPL-2.0
//! String representations.
use crate::alloc::{flags::*, AllocError, KVec};
use core::fmt::{self, Write};
use core::ops::{self, Deref, DerefMut, Index};
use crate::error::{code::*, Error};
/// Byte string without UTF-8 validity guarantee.
#[repr(transparent)]
pub struct BStr([u8]);
impl BStr {
/// Returns the length of this string.
#[inline]
pub const fn len(&self) -> usize {
self.0.len()
}
/// Returns `true` if the string is empty.
#[inline]
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
/// Creates a [`BStr`] from a `[u8]`.
#[inline]
pub const fn from_bytes(bytes: &[u8]) -> &Self {
// SAFETY: `BStr` is transparent to `[u8]`.
unsafe { &*(bytes as *const [u8] as *const BStr) }
}
}
impl fmt::Display for BStr {
/// Formats printable ASCII characters, escaping the rest.
///
/// ```
/// # use kernel::{fmt, b_str, str::{BStr, CString}};
/// let ascii = b_str!("Hello, BStr!");
/// let s = CString::try_from_fmt(fmt!("{}", ascii)).unwrap();
/// assert_eq!(s.as_bytes(), "Hello, BStr!".as_bytes());
///
/// let non_ascii = b_str!("🦀");
/// let s = CString::try_from_fmt(fmt!("{}", non_ascii)).unwrap();
/// assert_eq!(s.as_bytes(), "\\xf0\\x9f\\xa6\\x80".as_bytes());
/// ```
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for &b in &self.0 {
match b {
// Common escape codes.
b'\t' => f.write_str("\\t")?,
b'\n' => f.write_str("\\n")?,
b'\r' => f.write_str("\\r")?,
// Printable characters.
0x20..=0x7e => f.write_char(b as char)?,
_ => write!(f, "\\x{:02x}", b)?,
}
}
Ok(())
}
}
impl fmt::Debug for BStr {
/// Formats printable ASCII characters with a double quote on either end,
/// escaping the rest.
///
/// ```
/// # use kernel::{fmt, b_str, str::{BStr, CString}};
/// // Embedded double quotes are escaped.
/// let ascii = b_str!("Hello, \"BStr\"!");
/// let s = CString::try_from_fmt(fmt!("{:?}", ascii)).unwrap();
/// assert_eq!(s.as_bytes(), "\"Hello, \\\"BStr\\\"!\"".as_bytes());
///
/// let non_ascii = b_str!("😺");
/// let s = CString::try_from_fmt(fmt!("{:?}", non_ascii)).unwrap();
/// assert_eq!(s.as_bytes(), "\"\\xf0\\x9f\\x98\\xba\"".as_bytes());
/// ```
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_char('"')?;
for &b in &self.0 {
match b {
// Common escape codes.
b'\t' => f.write_str("\\t")?,
b'\n' => f.write_str("\\n")?,
b'\r' => f.write_str("\\r")?,
// String escape characters.
b'\"' => f.write_str("\\\"")?,
b'\\' => f.write_str("\\\\")?,
// Printable characters.
0x20..=0x7e => f.write_char(b as char)?,
_ => write!(f, "\\x{:02x}", b)?,
}
}
f.write_char('"')
}
}
impl Deref for BStr {
type Target = [u8];
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
/// Creates a new [`BStr`] from a string literal.
///
/// `b_str!` converts the supplied string literal to byte string, so non-ASCII
/// characters can be included.
///
/// # Examples
///
/// ```
/// # use kernel::b_str;
/// # use kernel::str::BStr;
/// const MY_BSTR: &BStr = b_str!("My awesome BStr!");
/// ```
#[macro_export]
macro_rules! b_str {
($str:literal) => {{
const S: &'static str = $str;
const C: &'static $crate::str::BStr = $crate::str::BStr::from_bytes(S.as_bytes());
C
}};
}
/// Possible errors when using conversion functions in [`CStr`].
#[derive(Debug, Clone, Copy)]
pub enum CStrConvertError {
/// Supplied bytes contain an interior `NUL`.
InteriorNul,
/// Supplied bytes are not terminated by `NUL`.
NotNulTerminated,
}
impl From<CStrConvertError> for Error {
#[inline]
fn from(_: CStrConvertError) -> Error {
EINVAL
}
}
/// A string that is guaranteed to have exactly one `NUL` byte, which is at the
/// end.
///
/// Used for interoperability with kernel APIs that take C strings.
#[repr(transparent)]
pub struct CStr([u8]);
impl CStr {
/// Returns the length of this string excluding `NUL`.
#[inline]
pub const fn len(&self) -> usize {
self.len_with_nul() - 1
}
/// Returns the length of this string with `NUL`.
#[inline]
pub const fn len_with_nul(&self) -> usize {
if self.0.is_empty() {
// SAFETY: This is one of the invariant of `CStr`.
// We add a `unreachable_unchecked` here to hint the optimizer that
// the value returned from this function is non-zero.
unsafe { core::hint::unreachable_unchecked() };