|
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 | ///
|
@@ -391,6 +391,16 @@ pub(crate) struct RawFormatter {
|
391 | 391 | }
|
392 | 392 |
|
393 | 393 | 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 | + |
394 | 404 | /// Creates a new instance of [`RawFormatter`] with the given buffer pointers.
|
395 | 405 | ///
|
396 | 406 | /// # Safety
|
@@ -500,3 +510,73 @@ impl fmt::Write for Formatter {
|
500 | 510 | }
|
501 | 511 | }
|
502 | 512 | }
|
| 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 | +/// # Examples |
| 519 | +/// |
| 520 | +/// ``` |
| 521 | +/// # use kernel::prelude::*; |
| 522 | +/// use kernel::str::CString; |
| 523 | +/// |
| 524 | +/// let s = CString::try_from_fmt(fmt!("{}{}{}", "abc", 10, 20)).unwrap(); |
| 525 | +/// assert_eq!(s.as_bytes_with_nul(), "abc1020\0".as_bytes()); |
| 526 | +/// |
| 527 | +/// let tmp = "testing"; |
| 528 | +/// let s = CString::try_from_fmt(fmt!("{tmp}{}", 123)).unwrap(); |
| 529 | +/// assert_eq!(s.as_bytes_with_nul(), "testing123\0".as_bytes()); |
| 530 | +/// ``` |
| 531 | +pub struct CString { |
| 532 | + buf: Vec<u8>, |
| 533 | +} |
| 534 | + |
| 535 | +impl CString { |
| 536 | + /// Creates an instance of [`CString`] from the given formatted arguments. |
| 537 | + pub fn try_from_fmt(args: fmt::Arguments<'_>) -> Result<Self, Error> { |
| 538 | + // Calculate the size needed (formatted string plus `NUL` terminator). |
| 539 | + let mut f = RawFormatter::new(); |
| 540 | + f.write_fmt(args)?; |
| 541 | + f.write_str("\0")?; |
| 542 | + let size = f.bytes_written(); |
| 543 | + |
| 544 | + // Allocate a vector with the required number of bytes, and write to it. |
| 545 | + let mut buf = Vec::try_with_capacity(size)?; |
| 546 | + // SAFETY: The buffer stored in `buf` is at least of size `size` and is valid for writes. |
| 547 | + let mut f = unsafe { Formatter::from_buffer(buf.as_mut_ptr(), size) }; |
| 548 | + f.write_fmt(args)?; |
| 549 | + f.write_str("\0")?; |
| 550 | + |
| 551 | + // SAFETY: The number of bytes that can be written to `f` is bounded by `size`, which is |
| 552 | + // `buf`'s capacity. The contents of the buffer have been initialised by writes to `f`. |
| 553 | + unsafe { buf.set_len(f.bytes_written()) }; |
| 554 | + |
| 555 | + // Check that there are no `NUL` bytes before the end. |
| 556 | + for c in buf.iter().take(f.bytes_written() - 1) { |
| 557 | + if *c == 0 { |
| 558 | + return Err(Error::EINVAL); |
| 559 | + } |
| 560 | + } |
| 561 | + |
| 562 | + Ok(Self { buf }) |
| 563 | + } |
| 564 | + |
| 565 | + /// Returns a C pointer to the string. |
| 566 | + #[inline] |
| 567 | + pub fn as_char_ptr(&self) -> *const c_types::c_char { |
| 568 | + self.buf.as_ptr() as _ |
| 569 | + } |
| 570 | + |
| 571 | + /// Converts the string to a byte slice containing the trailing 0 byte. |
| 572 | + #[inline] |
| 573 | + pub fn as_bytes_with_nul(&self) -> &[u8] { |
| 574 | + self.buf.as_slice() |
| 575 | + } |
| 576 | +} |
| 577 | + |
| 578 | +/// A convenience alias for [`core::format_args`]. |
| 579 | +#[macro_export] |
| 580 | +macro_rules! fmt { |
| 581 | + ($($f:tt)*) => ( core::format_args!($($f)*) ) |
| 582 | +} |
0 commit comments