Skip to content
This repository was archived by the owner on Nov 8, 2023. It is now read-only.

Commit 65e1e49

Browse files
wedsonafojeda
authored andcommitted
rust: str: add CString type
Add the `CString` type, which is an owned string that is guaranteed to have exactly one `NUL` byte at the end, i.e. the owned equivalent to `CStr` introduced earlier. It is used for interoperability with kernel APIs that take C strings. In order to do so, implement the `RawFormatter::new()` constructor and the `RawFormatter::bytes_written()` method as well. Signed-off-by: Wedson Almeida Filho <[email protected]> [Reworded, adapted for upstream and applied latest changes] Signed-off-by: Miguel Ojeda <[email protected]>
1 parent fffed67 commit 65e1e49

File tree

1 file changed

+89
-2
lines changed

1 file changed

+89
-2
lines changed

rust/kernel/str.rs

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
//! String representations.
44
5+
use alloc::vec::Vec;
56
use core::fmt::{self, Write};
67
use core::ops::{self, Deref, Index};
78

@@ -384,13 +385,22 @@ mod tests {
384385
/// is less than `end`.
385386
pub(crate) struct RawFormatter {
386387
// Use `usize` to use `saturating_*` functions.
387-
#[allow(dead_code)]
388388
beg: usize,
389389
pos: usize,
390390
end: usize,
391391
}
392392

393393
impl RawFormatter {
394+
/// Creates a new instance of [`RawFormatter`] with an empty buffer.
395+
fn new() -> Self {
396+
// INVARIANT: The buffer is empty, so the region that needs to be writable is empty.
397+
Self {
398+
beg: 0,
399+
pos: 0,
400+
end: 0,
401+
}
402+
}
403+
394404
/// Creates a new instance of [`RawFormatter`] with the given buffer pointers.
395405
///
396406
/// # Safety
@@ -429,6 +439,11 @@ impl RawFormatter {
429439
pub(crate) fn pos(&self) -> *mut u8 {
430440
self.pos as _
431441
}
442+
443+
/// Return the number of bytes written to the formatter.
444+
pub(crate) fn bytes_written(&self) -> usize {
445+
self.pos - self.beg
446+
}
432447
}
433448

434449
impl fmt::Write for RawFormatter {
@@ -469,7 +484,6 @@ impl Formatter {
469484
///
470485
/// The memory region starting at `buf` and extending for `len` bytes must be valid for writes
471486
/// for the lifetime of the returned [`Formatter`].
472-
#[allow(dead_code)]
473487
pub(crate) unsafe fn from_buffer(buf: *mut u8, len: usize) -> Self {
474488
// SAFETY: The safety requirements of this function satisfy those of the callee.
475489
Self(unsafe { RawFormatter::from_buffer(buf, len) })
@@ -496,3 +510,76 @@ impl fmt::Write for Formatter {
496510
}
497511
}
498512
}
513+
514+
/// An owned string that is guaranteed to have exactly one `NUL` byte, which is at the end.
515+
///
516+
/// Used for interoperability with kernel APIs that take C strings.
517+
///
518+
/// # Invariants
519+
///
520+
/// The string is always `NUL`-terminated and contains no other `NUL` bytes.
521+
///
522+
/// # Examples
523+
///
524+
/// ```
525+
/// use kernel::str::CString;
526+
///
527+
/// let s = CString::try_from_fmt(fmt!("{}{}{}", "abc", 10, 20)).unwrap();
528+
/// assert_eq!(s.as_bytes_with_nul(), "abc1020\0".as_bytes());
529+
///
530+
/// let tmp = "testing";
531+
/// let s = CString::try_from_fmt(fmt!("{tmp}{}", 123)).unwrap();
532+
/// assert_eq!(s.as_bytes_with_nul(), "testing123\0".as_bytes());
533+
///
534+
/// // This fails because it has an embedded `NUL` byte.
535+
/// let s = CString::try_from_fmt(fmt!("a\0b{}", 123));
536+
/// assert_eq!(s.is_ok(), false);
537+
/// ```
538+
pub struct CString {
539+
buf: Vec<u8>,
540+
}
541+
542+
impl CString {
543+
/// Creates an instance of [`CString`] from the given formatted arguments.
544+
pub fn try_from_fmt(args: fmt::Arguments<'_>) -> Result<Self, Error> {
545+
// Calculate the size needed (formatted string plus `NUL` terminator).
546+
let mut f = RawFormatter::new();
547+
f.write_fmt(args)?;
548+
f.write_str("\0")?;
549+
let size = f.bytes_written();
550+
551+
// Allocate a vector with the required number of bytes, and write to it.
552+
let mut buf = Vec::try_with_capacity(size)?;
553+
// SAFETY: The buffer stored in `buf` is at least of size `size` and is valid for writes.
554+
let mut f = unsafe { Formatter::from_buffer(buf.as_mut_ptr(), size) };
555+
f.write_fmt(args)?;
556+
f.write_str("\0")?;
557+
558+
// SAFETY: The number of bytes that can be written to `f` is bounded by `size`, which is
559+
// `buf`'s capacity. The contents of the buffer have been initialised by writes to `f`.
560+
unsafe { buf.set_len(f.bytes_written()) };
561+
562+
// Check that there are no `NUL` bytes before the end.
563+
// SAFETY: The buffer is valid for read because `f.bytes_written()` is bounded by `size`
564+
// (which the minimum buffer size) and is non-zero (we wrote at least the `NUL` terminator)
565+
// so `f.bytes_written() - 1` doesn't underflow.
566+
let ptr = unsafe { bindings::memchr(buf.as_ptr().cast(), 0, (f.bytes_written() - 1) as _) };
567+
if !ptr.is_null() {
568+
return Err(EINVAL);
569+
}
570+
571+
// INVARIANT: We wrote the `NUL` terminator and checked above that no other `NUL` bytes
572+
// exist in the buffer.
573+
Ok(Self { buf })
574+
}
575+
}
576+
577+
impl Deref for CString {
578+
type Target = CStr;
579+
580+
fn deref(&self) -> &Self::Target {
581+
// SAFETY: The type invariants guarantee that the string is `NUL`-terminated and that no
582+
// other `NUL` bytes exist.
583+
unsafe { CStr::from_bytes_with_nul_unchecked(self.buf.as_slice()) }
584+
}
585+
}

0 commit comments

Comments
 (0)