159 lines
4.5 KiB
Rust
159 lines
4.5 KiB
Rust
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
use core::fmt::{Arguments, Write};
|
|
|
|
use ostd::mm::{FallibleVmWrite, VmReader, VmWriter};
|
|
|
|
/// A specialized printer for formatted text output.
|
|
///
|
|
/// `VmPrinter` is designed to handle the common pattern in kernel where kernel
|
|
/// code needs to generate formatted text output (like status information, statistics,
|
|
/// or configuration data) and write it to user space with proper offset handling.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```rust,ignore
|
|
/// use aster_util::printer::VmPrinter;
|
|
/// use ostd::{mm::VmWriter, Pod};
|
|
///
|
|
/// let mut buf = [0u8; 3];
|
|
/// let mut writer = VmWriter::from(buf.as_bytes_mut()).to_fallible();
|
|
/// let mut printer = VmPrinter::new_skip(&mut writer, 3);
|
|
///
|
|
/// let res = writeln!(printer, "val: {}", 123);
|
|
/// assert!(res.is_ok());
|
|
///
|
|
/// assert_eq!(printer.bytes_written(), 3);
|
|
/// assert_eq!(&buf, b": 1");
|
|
/// ```
|
|
pub struct VmPrinter<'a, 'b> {
|
|
/// The underlying [`VmWriter`] to write the final output.
|
|
writer: &'a mut VmWriter<'b>,
|
|
/// Number of bytes to skip from the beginning of the output.
|
|
///
|
|
/// When content is written through this writer, the first `bytes_to_skip`
|
|
/// bytes will be discarded, and only subsequent bytes will be written
|
|
/// to the underlying `VmWriter`.
|
|
bytes_to_skip: usize,
|
|
/// Total number of bytes written to the underlying writer.
|
|
bytes_written: usize,
|
|
}
|
|
|
|
impl<'a, 'b> VmPrinter<'a, 'b> {
|
|
/// Creates a new `VmPrinter` that prints to `writer`.
|
|
fn new(writer: &'a mut VmWriter<'b>) -> Self {
|
|
Self {
|
|
writer,
|
|
bytes_to_skip: 0,
|
|
bytes_written: 0,
|
|
}
|
|
}
|
|
|
|
/// Creates a new `VmPrinter` that skips the first `bytes_to_skip` bytes and prints the
|
|
/// remaining bytes to `writer`.
|
|
pub fn new_skip(writer: &'a mut VmWriter<'b>, bytes_to_skip: usize) -> Self {
|
|
Self {
|
|
writer,
|
|
bytes_to_skip,
|
|
bytes_written: 0,
|
|
}
|
|
}
|
|
|
|
/// Returns the total number of bytes written to the underlying writer.
|
|
pub fn bytes_written(&self) -> usize {
|
|
self.bytes_written
|
|
}
|
|
|
|
/// Writes formatted content to the underlying writer.
|
|
pub fn write_fmt(&mut self, args: Arguments<'_>) -> Result<(), VmPrinterError> {
|
|
Write::write_fmt(self, args).map_err(|_| VmPrinterError::PageFault)
|
|
}
|
|
|
|
fn write_bytes(&mut self, bytes: &[u8]) -> core::fmt::Result {
|
|
if self.bytes_to_skip >= bytes.len() {
|
|
self.bytes_to_skip -= bytes.len();
|
|
return Ok(());
|
|
}
|
|
|
|
let bytes_to_write = &bytes[self.bytes_to_skip..];
|
|
if self.bytes_to_skip > 0 {
|
|
self.bytes_to_skip = 0;
|
|
}
|
|
|
|
let mut reader = VmReader::from(bytes_to_write);
|
|
let written_len = self
|
|
.writer
|
|
.write_fallible(&mut reader)
|
|
.map_err(|_| core::fmt::Error)?;
|
|
|
|
self.bytes_written += written_len;
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Write for VmPrinter<'_, '_> {
|
|
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
|
self.write_bytes(s.as_bytes())
|
|
}
|
|
}
|
|
|
|
impl<'a, 'b> From<&'a mut VmWriter<'b>> for VmPrinter<'a, 'b> {
|
|
fn from(writer: &'a mut VmWriter<'b>) -> Self {
|
|
Self::new(writer)
|
|
}
|
|
}
|
|
|
|
/// An error returned by [`VmPrinter::write_fmt`].
|
|
pub enum VmPrinterError {
|
|
/// Page fault occurred.
|
|
PageFault,
|
|
}
|
|
|
|
#[cfg(ktest)]
|
|
mod test {
|
|
use ostd::{Pod, mm::VmWriter, prelude::*};
|
|
|
|
use super::*;
|
|
|
|
#[ktest]
|
|
fn basic_write() {
|
|
let mut buf = [0u8; 64];
|
|
let mut writer = VmWriter::from(buf.as_bytes_mut()).to_fallible();
|
|
let mut printer = VmPrinter::from(&mut writer);
|
|
|
|
let res = writeln!(printer, "test");
|
|
assert!(res.is_ok());
|
|
|
|
assert_eq!(printer.bytes_written(), 5);
|
|
assert_eq!(&buf[..5], b"test\n");
|
|
}
|
|
|
|
#[ktest]
|
|
fn write_with_skip() {
|
|
let mut buf = [0u8; 3];
|
|
let mut writer = VmWriter::from(buf.as_bytes_mut()).to_fallible();
|
|
let mut printer = VmPrinter::new_skip(&mut writer, 3);
|
|
|
|
let res = writeln!(printer, "val: {}", 123);
|
|
assert!(res.is_ok());
|
|
|
|
assert_eq!(printer.bytes_written(), 3);
|
|
assert_eq!(&buf, b": 1");
|
|
}
|
|
|
|
#[ktest]
|
|
fn skip_all_content() {
|
|
let mut buf = [0u8; 64];
|
|
let mut writer = VmWriter::from(buf.as_bytes_mut()).to_fallible();
|
|
let mut printer = VmPrinter::new_skip(&mut writer, 100);
|
|
|
|
let res = writeln!(printer, "short message");
|
|
assert!(res.is_ok());
|
|
|
|
// Nothing should be written
|
|
assert_eq!(printer.bytes_written(), 0);
|
|
assert_eq!(buf[0], 0);
|
|
}
|
|
}
|