blob: 745d53abd11558d65b4c31cceff2c4d63fb4218c [file]
// Copyright 2026 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
use core::ffi::{c_char, c_int, c_void};
use core::fmt;
use core::mem::{align_of, size_of};
mod string_file;
pub use string_file::{ScopedFile, StringFile};
/// The C++ `FILE::Callback` signature: `int(void* ptr, const char* str, size_t len)`.
pub type FileCallback = unsafe extern "C" fn(*mut c_void, *const c_char, usize) -> c_int;
/// This represents the kernel `<stdio.h>` `FILE` type.
#[repr(C)]
pub struct FILE {
write: Option<FileCallback>,
ptr: *mut c_void,
}
const _: () = {
assert!(size_of::<FILE>() == 16);
assert!(align_of::<FILE>() == 8);
};
impl FILE {
/// Creates a `FILE` that sends everything written to it to `write`, passing `ptr` as the
/// callback's first argument, like the C++ `FILE(Callback* write, void* ptr)` constructor.
///
/// # Safety
///
/// For the lifetime of the returned `FILE`, `write` (if present) must be sound to call with
/// `ptr` and any `(data, len)` where `data` points to `len` readable bytes when `len > 0` (and
/// may be null when `len == 0`).
pub const unsafe fn new(write: Option<FileCallback>, ptr: *mut c_void) -> Self {
Self { write, ptr }
}
/// Writes `bytes` via the callback, like the C++ `FILE::Write`. This is what `fprintf` calls
/// to do output.
///
/// Returns the callback's result, or -1 if this `FILE` has no callback.
pub fn write(&self, bytes: &[u8]) -> c_int {
let Some(write) = self.write else {
return -1;
};
// SAFETY:
// - `self.ptr` is the context uniquely paired with `write` by the C API.
// - `bytes` points to a valid sequence of `bytes.len()` bytes that remains valid and
// immutable for the duration of the FFI call.
unsafe { write(self.ptr, bytes.as_ptr().cast::<c_char>(), bytes.len()) }
}
}
impl fmt::Write for FILE {
fn write_str(&mut self, s: &str) -> fmt::Result {
let Some(write) = self.write else {
return Err(fmt::Error);
};
if s.is_empty() {
return Ok(());
}
let c_chars = s.as_ptr().cast::<c_char>();
// SAFETY:
// - `self.ptr` is the context uniquely paired with `write` by the C API.
// - `s` is not empty, so `c_chars` points to a valid sequence of `s.len()` bytes.
// - The `s` memory region remains valid and immutable for the duration of the FFI call.
let ret = unsafe { write(self.ptr, c_chars, s.len()) };
if ret < 0 { Err(fmt::Error) } else { Ok(()) }
}
}