libc/stdio.rs
1// Copyright 2026 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7use core::ffi::{VaList, c_char, c_int, c_void};
8use core::fmt;
9use core::mem::{align_of, size_of};
10
11mod string_file;
12
13pub use string_file::{ScopedFile, StringFile};
14
15/// The C++ `FILE::Callback` signature: `int(void* ptr, const char* str, size_t len)`.
16pub type FileCallback = unsafe extern "C" fn(*mut c_void, *const c_char, usize) -> c_int;
17
18/// This represents the kernel `<stdio.h>` `FILE` type.
19#[repr(C)]
20pub struct FILE {
21 write: Option<FileCallback>,
22 ptr: *mut c_void,
23}
24
25const _: () = {
26 assert!(size_of::<FILE>() == 16);
27 assert!(align_of::<FILE>() == 8);
28};
29
30impl FILE {
31 /// Creates a `FILE` that sends everything written to it to `write`, passing `ptr` as the
32 /// callback's first argument, like the C++ `FILE(Callback* write, void* ptr)` constructor.
33 ///
34 /// # Safety
35 ///
36 /// For the lifetime of the returned `FILE`, `write` (if present) must be sound to call with
37 /// `ptr` and any `(data, len)` where `data` points to `len` readable bytes when `len > 0` (and
38 /// may be null when `len == 0`).
39 pub const unsafe fn new(write: Option<FileCallback>, ptr: *mut c_void) -> Self {
40 Self { write, ptr }
41 }
42
43 /// Writes `bytes` via the callback, like the C++ `FILE::Write`. This is what `fprintf` calls
44 /// to do output.
45 ///
46 /// Returns the callback's result, or -1 if this `FILE` has no callback.
47 pub fn write(&self, bytes: &[u8]) -> c_int {
48 let Some(write) = self.write else {
49 return -1;
50 };
51 // SAFETY:
52 // - `self.ptr` is the context uniquely paired with `write` by the C API.
53 // - `bytes` points to a valid sequence of `bytes.len()` bytes that remains valid and
54 // immutable for the duration of the FFI call.
55 unsafe { write(self.ptr, bytes.as_ptr().cast::<c_char>(), bytes.len()) }
56 }
57}
58
59impl fmt::Write for FILE {
60 fn write_str(&mut self, s: &str) -> fmt::Result {
61 let Some(write) = self.write else {
62 return Err(fmt::Error);
63 };
64 if s.is_empty() {
65 return Ok(());
66 }
67 let c_chars = s.as_ptr().cast::<c_char>();
68 // SAFETY:
69 // - `self.ptr` is the context uniquely paired with `write` by the C API.
70 // - `s` is not empty, so `c_chars` points to a valid sequence of `s.len()` bytes.
71 // - The `s` memory region remains valid and immutable for the duration of the FFI call.
72 let ret = unsafe { write(self.ptr, c_chars, s.len()) };
73 if ret < 0 { Err(fmt::Error) } else { Ok(()) }
74 }
75}
76
77unsafe extern "C" {
78 /// Writes formatted output to `f`, matching `<stdio.h>` `fprintf`.
79 ///
80 /// # Safety
81 ///
82 /// `f` must point to a valid `FILE`. `fmt` must be non-null and point to a valid
83 /// NUL-terminated C format string whose format specifiers match the variadic arguments.
84 pub fn fprintf(f: *mut FILE, fmt: *const c_char, ...) -> c_int;
85
86 /// Writes `VaList` formatted output to `f`, matching `<stdio.h>` `vfprintf`.
87 ///
88 /// # Safety
89 ///
90 /// `f` must point to a valid `FILE`. `fmt` must be non-null and point to a valid
91 /// NUL-terminated C format string whose format specifiers match the arguments in `ap`.
92 pub fn vfprintf(f: *mut FILE, fmt: *const c_char, ap: VaList<'_>) -> c_int;
93}