Skip to main content

kprint/
lib.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#![no_std]
6
7#[allow(unused_extern_crates)]
8extern crate self as kprint;
9
10pub mod backend;
11
12#[doc(hidden)]
13pub use kprint_macro::{__kformat_internal, __kprint_internal, __kprintln_internal};
14
15/// Prints formatted text directly to standard kernel console output (via C `printf`).
16#[macro_export]
17macro_rules! kprint {
18    ($($tt:tt)*) => {
19        $crate::__kprint_internal!($crate, $($tt)*)
20    };
21}
22
23/// Prints formatted text followed by a newline to standard kernel console output (via C `printf`).
24#[macro_export]
25macro_rules! kprintln {
26    ($($tt:tt)*) => {
27        $crate::__kprintln_internal!($crate, $($tt)*)
28    };
29}
30
31/// Formats text into a provided buffer (via C `snprintf`) and returns a byte slice (`&[u8]`).
32#[macro_export]
33macro_rules! kformat {
34    ($($tt:tt)*) => {
35        $crate::__kformat_internal!($crate, $($tt)*)
36    };
37}
38
39#[cfg(test)]
40mod tests {
41    #[allow(unused_imports)]
42    use super::*;
43
44    #[test]
45    fn test_signed_integers() {
46        let mut buf = [0u8; 256];
47        assert_eq!(kformat!(&mut buf, "val: {}", -12345), b"val: -12345");
48        assert_eq!(kformat!(&mut buf, "val: {:+d}", 42), b"val: +42");
49        assert_eq!(kformat!(&mut buf, "val: {: d}", 42), b"val:  42");
50        assert_eq!(kformat!(&mut buf, "val: '{:<8d}'", 42), b"val: '42      '");
51        assert_eq!(kformat!(&mut buf, "val: '{:>8d}'", 42), b"val: '      42'");
52        assert_eq!(kformat!(&mut buf, "val: {:05d}", 42), b"val: 00042");
53
54        let val_i8: i8 = -8;
55        let val_i16: i16 = -16;
56        let val_i32: i32 = -32;
57        let val_i64: i64 = -64;
58        let val_isize: isize = -100;
59        assert_eq!(
60            kformat!(&mut buf, "{} {} {} {} {}", &val_i8, &val_i16, val_i32, val_i64, val_isize),
61            b"-8 -16 -32 -64 -100"
62        );
63        assert_eq!(kformat!(&mut buf, "min i64: {}", i64::MIN), b"min i64: -9223372036854775808");
64    }
65
66    #[test]
67    fn test_unsigned_and_hex() {
68        let mut buf = [0u8; 256];
69        assert_eq!(kformat!(&mut buf, "val: {:u}", 12345u32), b"val: 12345");
70        assert_eq!(kformat!(&mut buf, "val: {:x}", 0xabcd), b"val: abcd");
71        assert_eq!(kformat!(&mut buf, "val: {:X}", 0xabcd), b"val: ABCD");
72        assert_eq!(kformat!(&mut buf, "val: {:#x}", 0), b"val: 0x0");
73        assert_eq!(kformat!(&mut buf, "val: {:#x}", 0x2a), b"val: 0x2a");
74        assert_eq!(kformat!(&mut buf, "val: {:#X}", 0x2a), b"val: 0X2A");
75        assert_eq!(kformat!(&mut buf, "val: {:#08X}", 0x1A), b"val: 0X00001A");
76        assert_eq!(kformat!(&mut buf, "val: {:o}", 64), b"val: 100");
77
78        let val_u8: u8 = 8;
79        let val_u16: u16 = 16;
80        let val_u32: u32 = 32;
81        let val_u64: u64 = 64;
82        let val_usize: usize = 128;
83        assert_eq!(
84            kformat!(
85                &mut buf,
86                "{:u} {:u} {:u} {:u} {:u}",
87                &val_u8,
88                val_u16,
89                val_u32,
90                val_u64,
91                val_usize
92            ),
93            b"8 16 32 64 128"
94        );
95        assert_eq!(kformat!(&mut buf, "max u64: {:u}", u64::MAX), b"max u64: 18446744073709551615");
96    }
97
98    #[test]
99    fn test_strings_and_cstrings() {
100        let mut buf = [0u8; 256];
101        assert_eq!(kformat!(&mut buf, "hello {}", "world"), b"hello world");
102        let s = "fuchsia";
103        assert_eq!(kformat!(&mut buf, "os: {:s}", s), b"os: fuchsia");
104        let sub = "pigweed kernel";
105        assert_eq!(kformat!(&mut buf, "sub: {:s}", &sub[0..7]), b"sub: pigweed");
106        assert_eq!(kformat!(&mut buf, "prec: {:.5s}", "123456789"), b"prec: 12345");
107
108        let byte_slice: &[u8] = b"bytes";
109        assert_eq!(kformat!(&mut buf, "raw: {:s}", byte_slice), b"raw: bytes");
110
111        let fixed_array: [u8; 4] = [b'a', b'b', b'c', b'd'];
112        assert_eq!(kformat!(&mut buf, "arr: {:s}", fixed_array), b"arr: abcd");
113
114        let c_str = c"zircon-cstr";
115        assert_eq!(kformat!(&mut buf, "cstr: {:s}", c_str), b"cstr: zircon-cstr");
116
117        let raw_c_ptr = c"raw-c-ptr".as_ptr();
118        assert_eq!(kformat!(&mut buf, "raw_ptr: {:cs}", raw_c_ptr), b"raw_ptr: raw-c-ptr");
119        assert_eq!(kformat!(&mut buf, "aligned: {:<12cs}!", raw_c_ptr), b"aligned: raw-c-ptr   !");
120    }
121
122    #[test]
123    fn test_pointers() {
124        let mut buf = [0u8; 256];
125        let val: i32 = 42;
126        let ptr = &val as *const i32;
127        let res = kformat!(&mut buf, "ptr: {:p}", ptr);
128        assert!(res.starts_with(b"ptr: 0x") || res.starts_with(b"ptr: (nil)"));
129
130        let usize_addr: usize = 0x12345678;
131        let res2 = kformat!(&mut buf, "addr: {:p}", usize_addr);
132        let res2_str = core::str::from_utf8(res2).unwrap();
133        assert!(res2_str.contains("12345678"));
134
135        let null_ptr: *const core::ffi::c_void = core::ptr::null();
136        let res_null = kformat!(&mut buf, "null: {:p}", null_ptr);
137        let res_null_str = core::str::from_utf8(res_null).unwrap();
138        assert!(
139            res_null_str.contains("0x0")
140                || res_null_str.contains("(nil)")
141                || res_null_str.contains("00000000")
142                || res_null_str.contains("0")
143        );
144    }
145
146    #[test]
147    fn test_single_evaluation() {
148        let mut buf = [0u8; 256];
149        let mut eval_count = 0;
150        let mut side_effect_fn = || {
151            eval_count += 1;
152            "single_eval"
153        };
154        let res = kformat!(&mut buf, "result: {:s}", side_effect_fn());
155        assert_eq!(res, b"result: single_eval");
156        assert_eq!(eval_count, 1);
157
158        let mut bool_eval_count = 0;
159        let mut bool_side_effect_fn = || {
160            bool_eval_count += 1;
161            true
162        };
163        let res_b = kformat!(&mut buf, "flag: {:b}", bool_side_effect_fn());
164        assert_eq!(res_b, b"flag: true");
165        assert_eq!(bool_eval_count, 1);
166
167        let mut multi_eval_count = 0;
168        let mut multi_side_effect_fn = || {
169            multi_eval_count += 1;
170            100
171        };
172        let res_multi = kformat!(&mut buf, "{0} + {0} = 200", multi_side_effect_fn());
173        assert_eq!(res_multi, b"100 + 100 = 200");
174        assert_eq!(multi_eval_count, 1);
175    }
176
177    #[test]
178    fn test_concat_format_string() {
179        let mut buf = [0u8; 256];
180        let val = 42;
181        assert_eq!(
182            kformat!(&mut buf, concat!("prefix_", "status: ", "{}"), val),
183            b"prefix_status: 42"
184        );
185        assert_eq!(
186            kformat!(&mut buf, concat!("test_", 1, "_", true, "_", 'c', "={}"), 99),
187            b"test_1_true_c=99"
188        );
189    }
190
191    #[test]
192    fn test_captured_and_named_variables() {
193        let mut buf = [0u8; 256];
194        let user = "alice";
195        let score = 100;
196        assert_eq!(
197            kformat!(&mut buf, "player {user:s} has score {score}"),
198            b"player alice has score 100"
199        );
200        assert_eq!(
201            kformat!(&mut buf, "{greeting:s}, {name:s}!", greeting = "hello", name = "world"),
202            b"hello, world!"
203        );
204    }
205
206    #[test]
207    fn test_positional_arguments() {
208        let mut buf = [0u8; 256];
209        assert_eq!(kformat!(&mut buf, "{0} + {1} = {0}", "a", "b"), b"a + b = a");
210        assert_eq!(kformat!(&mut buf, "{1} {0} {1}", 10, 20), b"20 10 20");
211    }
212
213    #[test]
214    fn test_escapes_and_percent() {
215        let mut buf = [0u8; 256];
216        assert_eq!(kformat!(&mut buf, "{{hello}}"), b"{hello}");
217        assert_eq!(kformat!(&mut buf, "100% completed"), b"100% completed");
218        assert_eq!(kformat!(&mut buf, "{}% done", 75), b"75% done");
219        assert_eq!(kformat!(&mut buf, "{{{}%}}", 50), b"{50%}");
220    }
221
222    #[test]
223    fn test_chars() {
224        let mut buf = [0u8; 256];
225        assert_eq!(kformat!(&mut buf, "char: {}", 'Z'), b"char: Z");
226        let c = '?';
227        assert_eq!(kformat!(&mut buf, "char: {:c}", c), b"char: ?");
228        assert_eq!(kformat!(&mut buf, "ref char: {:c}", &c), b"ref char: ?");
229
230        // u8 and byte literals with {:c}
231        let byte: u8 = b'A';
232        assert_eq!(kformat!(&mut buf, "byte: {:c}", byte), b"byte: A");
233        assert_eq!(kformat!(&mut buf, "ref byte: {:c}", &byte), b"ref byte: A");
234        assert_eq!(kformat!(&mut buf, "byte lit: {:c}", b'K'), b"byte lit: K");
235        assert_eq!(kformat!(&mut buf, "byte lit auto: {}", b'X'), b"byte lit auto: X");
236
237        // Non-ASCII and multi-byte Unicode characters
238        assert_eq!(kformat!(&mut buf, "crab: {}", '🦀'), "crab: 🦀".as_bytes());
239        assert_eq!(kformat!(&mut buf, "accent: {:c}", 'é'), "accent: é".as_bytes());
240    }
241
242    #[test]
243    fn test_booleans() {
244        let mut buf = [0u8; 256];
245        assert_eq!(kformat!(&mut buf, "flag: {}", true), b"flag: true");
246        assert_eq!(kformat!(&mut buf, "flag: {:b}", false), b"flag: false");
247    }
248
249    #[test]
250    fn test_floats() {
251        let mut buf = [0u8; 256];
252        assert_eq!(kformat!(&mut buf, "pi: {:.2f}", core::f64::consts::PI), b"pi: 3.14");
253        assert_eq!(kformat!(&mut buf, "float: {:.4f}", 1.23456f32), b"float: 1.2346");
254        let f = 1000.0;
255        let res_e = kformat!(&mut buf, "sci: {:.1e}", f);
256        assert_eq!(res_e, b"sci: 1.0e+03");
257    }
258
259    #[test]
260    fn test_buffer_truncation() {
261        let mut small_buf = [0u8; 8];
262        let res = kformat!(&mut small_buf, "1234567890");
263        assert_eq!(res, b"1234567");
264        assert_eq!(res.len(), 7);
265    }
266
267    #[test]
268    fn test_macro_wrapper_hygiene() {
269        macro_rules! wrapped_kformat {
270            ($buf:expr, $($tt:tt)*) => {
271                kformat!($buf, $($tt)*)
272            };
273        }
274        let mut buf = [0u8; 64];
275        let x = 123;
276        assert_eq!(wrapped_kformat!(&mut buf, "val: {}", x), b"val: 123");
277    }
278
279    #[test]
280    fn test_print_macros() {
281        kprint!("test printf {}", 123);
282        kprint!("100%");
283        kprintln!(" line2");
284        kprintln!("empty without args");
285        kprintln!("{:#x}", 0);
286        kprintln!("crab: {}", '🦀');
287    }
288}