|
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,83 @@ 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 | +/// # Invariants |
| 519 | +/// |
| 520 | +/// The string is always `NUL`-terminated and contains no other `NUL` bytes. |
| 521 | +/// |
| 522 | +/// # Examples |
| 523 | +/// |
| 524 | +/// ``` |
| 525 | +/// # use kernel::prelude::*; |
| 526 | +/// use kernel::str::CString; |
| 527 | +/// |
| 528 | +/// let s = CString::try_from_fmt(fmt!("{}{}{}", "abc", 10, 20)).unwrap(); |
| 529 | +/// assert_eq!(s.as_bytes_with_nul(), "abc1020\0".as_bytes()); |
| 530 | +/// |
| 531 | +/// let tmp = "testing"; |
| 532 | +/// let s = CString::try_from_fmt(fmt!("{tmp}{}", 123)).unwrap(); |
| 533 | +/// assert_eq!(s.as_bytes_with_nul(), "testing123\0".as_bytes()); |
| 534 | +/// |
| 535 | +/// // This fails because it has an embedded `NUL` byte. |
| 536 | +/// let s = CString::try_from_fmt(fmt!("a\0b{}", 123)); |
| 537 | +/// assert_eq!(s.is_ok(), false); |
| 538 | +/// ``` |
| 539 | +pub struct CString { |
| 540 | + buf: Vec<u8>, |
| 541 | +} |
| 542 | + |
| 543 | +impl CString { |
| 544 | + /// Creates an instance of [`CString`] from the given formatted arguments. |
| 545 | + pub fn try_from_fmt(args: fmt::Arguments<'_>) -> Result<Self, Error> { |
| 546 | + // Calculate the size needed (formatted string plus `NUL` terminator). |
| 547 | + let mut f = RawFormatter::new(); |
| 548 | + f.write_fmt(args)?; |
| 549 | + f.write_str("\0")?; |
| 550 | + let size = f.bytes_written(); |
| 551 | + |
| 552 | + // Allocate a vector with the required number of bytes, and write to it. |
| 553 | + let mut buf = Vec::try_with_capacity(size)?; |
| 554 | + // SAFETY: The buffer stored in `buf` is at least of size `size` and is valid for writes. |
| 555 | + let mut f = unsafe { Formatter::from_buffer(buf.as_mut_ptr(), size) }; |
| 556 | + f.write_fmt(args)?; |
| 557 | + f.write_str("\0")?; |
| 558 | + |
| 559 | + // SAFETY: The number of bytes that can be written to `f` is bounded by `size`, which is |
| 560 | + // `buf`'s capacity. The contents of the buffer have been initialised by writes to `f`. |
| 561 | + unsafe { buf.set_len(f.bytes_written()) }; |
| 562 | + |
| 563 | + // Check that there are no `NUL` bytes before the end. |
| 564 | + // SAFETY: The buffer is valid for read because `f.bytes_written()` is bounded by `size` |
| 565 | + // (which the minimum buffer size) and is non-zero (we wrote at least the `NUL` terminator) |
| 566 | + // so `f.bytes_written() - 1` doesn't underflow. |
| 567 | + let ptr = unsafe { bindings::memchr(buf.as_ptr().cast(), 0, (f.bytes_written() - 1) as _) }; |
| 568 | + if !ptr.is_null() { |
| 569 | + return Err(Error::EINVAL); |
| 570 | + } |
| 571 | + |
| 572 | + // INVARIANT: We wrote the `NUL` terminator and checked above that no other `NUL` bytes |
| 573 | + // exist in the buffer. |
| 574 | + Ok(Self { buf }) |
| 575 | + } |
| 576 | +} |
| 577 | + |
| 578 | +impl Deref for CString { |
| 579 | + type Target = CStr; |
| 580 | + |
| 581 | + fn deref(&self) -> &Self::Target { |
| 582 | + // SAFETY: The type invariants guarantee that the string is `NUL`-terminated and that no |
| 583 | + // other `NUL` bytes exist. |
| 584 | + unsafe { CStr::from_bytes_with_nul_unchecked(self.buf.as_slice()) } |
| 585 | + } |
| 586 | +} |
| 587 | + |
| 588 | +/// A convenience alias for [`core::format_args`]. |
| 589 | +#[macro_export] |
| 590 | +macro_rules! fmt { |
| 591 | + ($($f:tt)*) => ( core::format_args!($($f)*) ) |
| 592 | +} |
0 commit comments