|
2 | 2 |
|
3 | 3 | //! String representations.
|
4 | 4 |
|
| 5 | +use alloc::vec::Vec; |
5 | 6 | use core::fmt::{self, Write};
|
6 | 7 | use core::ops::{self, Deref, Index};
|
7 | 8 |
|
8 |
| -use crate::bindings; |
9 |
| -use crate::c_types; |
| 9 | +use crate::{bindings, c_types, Error}; |
10 | 10 |
|
11 | 11 | /// Byte string without UTF-8 validity guarantee.
|
12 | 12 | ///
|
@@ -500,3 +500,77 @@ impl fmt::Write for Formatter {
|
500 | 500 | }
|
501 | 501 | }
|
502 | 502 | }
|
| 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