Skip to content

Commit d626b6f

Browse files
committed
rust: add basic owned C strings
For now they're only constructible from formatted arguments. They will be used in a subsequent patch to allow registration of miscdevs with names that are constructed at runtime. Signed-off-by: Wedson Almeida Filho <[email protected]>
1 parent b9b1330 commit d626b6f

File tree

2 files changed

+77
-3
lines changed

2 files changed

+77
-3
lines changed

rust/kernel/prelude.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub use macros::module;
2020
pub use super::build_assert;
2121

2222
pub use super::{
23-
dbg, dev_alert, dev_crit, dev_dbg, dev_emerg, dev_err, dev_info, dev_notice, dev_warn,
23+
dbg, dev_alert, dev_crit, dev_dbg, dev_emerg, dev_err, dev_info, dev_notice, dev_warn, fmt,
2424
pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr_notice, pr_warn,
2525
};
2626

rust/kernel/str.rs

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

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

8-
use crate::bindings;
9-
use crate::c_types;
9+
use crate::{bindings, c_types, Error};
1010

1111
/// Byte string without UTF-8 validity guarantee.
1212
///
@@ -500,3 +500,77 @@ impl fmt::Write for Formatter {
500500
}
501501
}
502502
}
503+
504+
/// An owned string that is guaranteed to have exactly one `NUL` byte, which is at the end.
505+
///
506+
/// Used for interoperability with kernel APIs that take C strings.
507+
///
508+
/// # Examples
509+
///
510+
/// ```
511+
/// # use kernel::prelude::*;
512+
/// use kernel::str::CString;
513+
///
514+
/// let s = CString::try_from_fmt(fmt!("{}{}{}", "abc", 10, 20)).unwrap();
515+
/// assert_eq!(s.as_bytes_with_nul(), "abc1020\0".as_bytes());
516+
///
517+
/// let tmp = "testing";
518+
/// let s = CString::try_from_fmt(fmt!("{tmp}{}", 123)).unwrap();
519+
/// assert_eq!(s.as_bytes_with_nul(), "testing123\0".as_bytes());
520+
/// ```
521+
pub struct CString {
522+
buf: Vec<u8>,
523+
}
524+
525+
impl CString {
526+
/// Creates an instance of [`CString`] from the given formatted arguments.
527+
pub fn try_from_fmt(args: fmt::Arguments<'_>) -> Result<Self, Error> {
528+
// Calculate the size needed (formatted string plus `NUL` terminator). Since we're
529+
// initialising the initial buffer to `null`, we know the insert position will hold the
530+
// required length after we've written everything.
531+
// SAFETY: The buffer is empty, so we guarantee its validity vacuously.
532+
let mut f =
533+
unsafe { RawFormatter::from_ptrs(core::ptr::null_mut(), core::ptr::null_mut()) };
534+
f.write_fmt(args)?;
535+
f.write_str("\0")?;
536+
let size = f.pos() as _;
537+
538+
// Allocate a vector with the required number of bytes, and write to it.
539+
let mut buf = Vec::try_with_capacity(size)?;
540+
// SAFETY: The buffer stored in `buf` is at least of size `size` and is valid for writes.
541+
let mut f = unsafe { Formatter::from_buffer(buf.as_mut_ptr(), size) };
542+
f.write_fmt(args)?;
543+
f.write_str("\0")?;
544+
545+
// SAFETY: `size` is exactly the capacity of `buf`, and all elements have been initialised
546+
// by the formatter above.
547+
unsafe { buf.set_len(size) };
548+
549+
// Check that there are no `NUL` bytes before the end.
550+
for c in buf.iter().take(size - 1) {
551+
if *c == 0 {
552+
return Err(Error::EINVAL);
553+
}
554+
}
555+
556+
Ok(Self { buf })
557+
}
558+
559+
/// Returns a C pointer to the string.
560+
#[inline]
561+
pub fn as_char_ptr(&self) -> *const c_types::c_char {
562+
self.buf.as_ptr() as _
563+
}
564+
565+
/// Converts the string to a byte slice containing the trailing 0 byte.
566+
#[inline]
567+
pub fn as_bytes_with_nul(&self) -> &[u8] {
568+
self.buf.as_slice()
569+
}
570+
}
571+
572+
/// A convenience alias for [`core::format_args`].
573+
#[macro_export]
574+
macro_rules! fmt {
575+
($($f:tt)*) => ( core::format_args!($($f)*) )
576+
}

0 commit comments

Comments
 (0)