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#[cfg(test)]
8extern crate std;
9
10#[allow(unused_extern_crates)]
11extern crate self as kprint;
12
13pub mod backend;
14
15#[doc(hidden)]
16pub use kprint_macro::{__kformat_internal, __kprint_internal, __kprintln_internal};
17
18/// Prints formatted text directly to standard kernel console output (via C `printf`).
19#[macro_export]
20macro_rules! kprint {
21    ($($tt:tt)*) => {
22        $crate::__kprint_internal!($crate, $($tt)*)
23    };
24}
25
26/// Prints formatted text followed by a newline to standard kernel console output (via C `printf`).
27#[macro_export]
28macro_rules! kprintln {
29    ($($tt:tt)*) => {
30        $crate::__kprintln_internal!($crate, $($tt)*)
31    };
32}
33
34/// Formats text into a provided buffer (via C `snprintf`) and returns a byte slice (`&[u8]`).
35#[macro_export]
36macro_rules! kformat {
37    ($($tt:tt)*) => {
38        $crate::__kformat_internal!($crate, $($tt)*)
39    };
40}
41
42#[cfg(test)]
43mod tests {
44    #[allow(unused_imports)]
45    use super::*;
46
47    /// Asserts that `kformat!` produces both `$expected` and the exact same
48    /// output as standard Rust `std::format!`.
49    macro_rules! assert_kformat_eq {
50        ($buf:expr, $expected:expr, $($arg:tt)*) => {{
51            let actual = kformat!($buf, $($arg)*);
52            let std_out = std::format!($($arg)*);
53            assert_eq!(actual, $expected);
54            assert_eq!(
55                core::str::from_utf8(actual).unwrap(),
56                std_out.as_str(),
57            );
58        }};
59    }
60
61    #[test]
62    fn test_signed_integers() {
63        let mut buf = [0u8; 256];
64        assert_kformat_eq!(&mut buf, b"val: -12345", "val: {}", -12345);
65        assert_kformat_eq!(&mut buf, b"val: +42", "val: {:+}", 42);
66        assert_eq!(kformat!(&mut buf, "val: {:+d}", 42), std::format!("val: {:+}", 42).as_bytes());
67        assert_eq!(kformat!(&mut buf, "val: {: d}", 42), b"val:  42");
68        assert_kformat_eq!(&mut buf, b"val: '42      '", "val: '{:<8}'", 42);
69        assert_eq!(
70            kformat!(&mut buf, "val: '{:<8d}'", 42),
71            std::format!("val: '{:<8}'", 42).as_bytes()
72        );
73        assert_kformat_eq!(&mut buf, b"val: '      42'", "val: '{:>8}'", 42);
74        assert_eq!(
75            kformat!(&mut buf, "val: '{:>8d}'", 42),
76            std::format!("val: '{:>8}'", 42).as_bytes()
77        );
78        assert_kformat_eq!(&mut buf, b"val: '      42'", "val: '{:8}'", 42);
79        assert_kformat_eq!(&mut buf, b"val: '      42'", "val: '{:-8}'", 42);
80        assert_kformat_eq!(&mut buf, b"val: 00042", "val: {:05}", 42);
81        assert_eq!(
82            kformat!(&mut buf, "val: {:05d}", 42),
83            std::format!("val: {:05}", 42).as_bytes()
84        );
85        assert_kformat_eq!(&mut buf, b"val: -0000042", "val: {:08}", -42);
86        assert_kformat_eq!(&mut buf, b"val: +0000042", "val: {:+08}", 42);
87        assert_kformat_eq!(&mut buf, b"val: 00000042", "val: {:<08}", 42);
88        assert_kformat_eq!(&mut buf, b"val: 00000042", "val: {:0>8}", 42);
89        assert_kformat_eq!(&mut buf, b"val: 42", "val: {:.5}", 42);
90
91        let val_i8: i8 = -8;
92        let val_i16: i16 = -16;
93        let val_i32: i32 = -32;
94        let val_i64: i64 = -64;
95        let val_isize: isize = -100;
96        assert_kformat_eq!(
97            &mut buf,
98            b"-8 -16 -32 -64 -100",
99            "{} {} {} {} {}",
100            &val_i8,
101            &val_i16,
102            val_i32,
103            val_i64,
104            val_isize
105        );
106        assert_kformat_eq!(&mut buf, b"min i64: -9223372036854775808", "min i64: {}", i64::MIN);
107    }
108
109    #[test]
110    fn test_unsigned_and_hex() {
111        let mut buf = [0u8; 256];
112        assert_kformat_eq!(&mut buf, b"val: 12345", "val: {}", 12345u32);
113        assert_eq!(
114            kformat!(&mut buf, "val: {:u}", 12345u32),
115            std::format!("val: {}", 12345u32).as_bytes()
116        );
117        assert_kformat_eq!(&mut buf, b"val: abcd", "val: {:x}", 0xabcd);
118        assert_kformat_eq!(&mut buf, b"val: ABCD", "val: {:X}", 0xabcd);
119        assert_kformat_eq!(&mut buf, b"val: 0x0", "val: {:#x}", 0);
120        assert_kformat_eq!(&mut buf, b"val: 0x2a", "val: {:#x}", 0x2a);
121        assert_kformat_eq!(&mut buf, b"val: 0x2A", "val: {:#X}", 0x2a);
122        assert_kformat_eq!(&mut buf, b"val: 0x00001A", "val: {:#08X}", 0x1A);
123        assert_kformat_eq!(&mut buf, b"val: 0x000000", "val: {:#08x}", 0);
124        assert_kformat_eq!(&mut buf, b"val:                0x1", "val: {:#18x}", 1);
125        assert_kformat_eq!(&mut buf, b"val:                0x0", "val: {:#18x}", 0);
126        assert_kformat_eq!(&mut buf, b"val:     0x2a", "val: {:>#8x}", 0x2a);
127        assert_kformat_eq!(&mut buf, b"val:     0x1A", "val: {:#8X}", 0x1a);
128        assert_kformat_eq!(&mut buf, b"val: 0x12345", "val: {:#6x}", 0x12345);
129        assert_kformat_eq!(&mut buf, b"val: 0xffffffffffffffff", "val: {:#18x}", u64::MAX);
130        assert_kformat_eq!(&mut buf, b"val: '0x2a      '", "val: '{:<#10x}'", 0x2a);
131        assert_kformat_eq!(&mut buf, b"val: '0x2A      '", "val: '{:<#10X}'", 0x2a);
132        assert_kformat_eq!(&mut buf, b"val: '0x0       '", "val: '{:<#10x}'", 0);
133        assert_kformat_eq!(&mut buf, b"val: 2a", "val: {:.5x}", 0x2a);
134        assert_kformat_eq!(&mut buf, b"val: 100", "val: {:o}", 64);
135
136        // Signed integers formatted as hex use same-width unsigned representation
137        assert_kformat_eq!(&mut buf, b"f4", "{:x}", -12i8);
138        assert_kformat_eq!(&mut buf, b"0xf4", "{:#x}", -12i8);
139        assert_kformat_eq!(&mut buf, b"fb2e", "{:x}", -1234i16);
140        assert_kformat_eq!(&mut buf, b"ff439eb2", "{:x}", -12345678i32);
141        assert_kformat_eq!(&mut buf, b"ffffffffffffffff", "{:x}", -1i64);
142
143        let val_u8: u8 = 8;
144        let val_u16: u16 = 16;
145        let val_u32: u32 = 32;
146        let val_u64: u64 = 64;
147        let val_usize: usize = 128;
148        assert_eq!(
149            kformat!(
150                &mut buf,
151                "{:u} {:u} {:u} {:u} {:u}",
152                &val_u8,
153                val_u16,
154                val_u32,
155                val_u64,
156                val_usize
157            ),
158            std::format!("{} {} {} {} {}", &val_u8, val_u16, val_u32, val_u64, val_usize)
159                .as_bytes()
160        );
161        assert_eq!(
162            kformat!(&mut buf, "max u64: {:u}", u64::MAX),
163            std::format!("max u64: {}", u64::MAX).as_bytes()
164        );
165    }
166
167    #[test]
168    fn test_strings_and_cstrings() {
169        let mut buf = [0u8; 256];
170        assert_kformat_eq!(&mut buf, b"hello world", "hello {}", "world");
171        let s = "fuchsia";
172        assert_eq!(kformat!(&mut buf, "os: {:s}", s), std::format!("os: {}", s).as_bytes());
173        let sub = "pigweed kernel";
174        assert_eq!(
175            kformat!(&mut buf, "sub: {:s}", &sub[0..7]),
176            std::format!("sub: {}", &sub[0..7]).as_bytes()
177        );
178        assert_kformat_eq!(&mut buf, b"prec: 12345", "prec: {:.5}", "123456789");
179        assert_eq!(
180            kformat!(&mut buf, "prec: {:.5s}", "123456789"),
181            std::format!("prec: {:.5}", "123456789").as_bytes()
182        );
183
184        // String alignment, width, and precision
185        assert_kformat_eq!(&mut buf, b"'abc     '", "'{:8}'", "abc");
186        assert_kformat_eq!(&mut buf, b"'abc     '", "'{:<8}'", "abc");
187        assert_kformat_eq!(&mut buf, b"'     abc'", "'{:>8}'", "abc");
188        assert_kformat_eq!(&mut buf, b"'abc     '", "'{:08}'", "abc");
189        assert_kformat_eq!(&mut buf, b"'abc     '", "'{:8.3}'", "abcdef");
190        assert_kformat_eq!(&mut buf, b"'     abc'", "'{:>8.3}'", "abcdef");
191
192        let var_s = "abc";
193        assert_eq!(kformat!(&mut buf, "'{:8s}'", var_s), std::format!("'{:8}'", var_s).as_bytes());
194        assert_eq!(
195            kformat!(&mut buf, "'{:<8s}'", var_s),
196            std::format!("'{:<8}'", var_s).as_bytes()
197        );
198        assert_eq!(
199            kformat!(&mut buf, "'{:>8s}'", var_s),
200            std::format!("'{:>8}'", var_s).as_bytes()
201        );
202        assert_eq!(
203            kformat!(&mut buf, "'{:08s}'", var_s),
204            std::format!("'{:08}'", var_s).as_bytes()
205        );
206
207        let byte_slice: &[u8] = b"bytes";
208        assert_eq!(kformat!(&mut buf, "raw: {:s}", byte_slice), b"raw: bytes");
209
210        let fixed_array: [u8; 4] = [b'a', b'b', b'c', b'd'];
211        assert_eq!(kformat!(&mut buf, "arr: {:s}", fixed_array), b"arr: abcd");
212
213        let c_str = c"zircon-cstr";
214        assert_eq!(kformat!(&mut buf, "cstr: {:s}", c_str), b"cstr: zircon-cstr");
215
216        let raw_c_ptr = c"raw-c-ptr".as_ptr();
217        assert_eq!(kformat!(&mut buf, "raw_ptr: {:cs}", raw_c_ptr), b"raw_ptr: raw-c-ptr");
218        assert_eq!(kformat!(&mut buf, "aligned: {:<12cs}!", raw_c_ptr), b"aligned: raw-c-ptr   !");
219        assert_eq!(kformat!(&mut buf, "aligned: {:12cs}!", raw_c_ptr), b"aligned: raw-c-ptr   !");
220        assert_eq!(kformat!(&mut buf, "aligned: {:>12cs}!", raw_c_ptr), b"aligned:    raw-c-ptr!");
221    }
222
223    #[test]
224    fn test_pointers() {
225        let mut buf = [0u8; 256];
226        let val: i32 = 42;
227        let ptr = &val as *const i32;
228        assert_eq!(kformat!(&mut buf, "ptr: {:p}", ptr), std::format!("ptr: {:p}", ptr).as_bytes());
229        assert_eq!(
230            kformat!(&mut buf, "ptr: {:#p}", ptr),
231            std::format!("ptr: {:#p}", ptr).as_bytes()
232        );
233
234        let usize_addr: usize = 0x12345678;
235        let addr_ptr = usize_addr as *const core::ffi::c_void;
236        assert_kformat_eq!(&mut buf, b"addr: 0x12345678", "addr: {:p}", addr_ptr);
237        assert_eq!(kformat!(&mut buf, "addr: {:p}", usize_addr), b"addr: 0x12345678");
238        assert_kformat_eq!(&mut buf, b"addr: 0x0000000012345678", "addr: {:#p}", addr_ptr);
239        assert_kformat_eq!(&mut buf, b"addr:     0x12345678", "addr: {:14p}", addr_ptr);
240        assert_kformat_eq!(&mut buf, b"addr: 0x12345678    ", "addr: {:<14p}", addr_ptr);
241        assert_kformat_eq!(&mut buf, b"addr: 0x000012345678", "addr: {:014p}", addr_ptr);
242
243        let null_ptr: *const core::ffi::c_void = core::ptr::null();
244        assert_kformat_eq!(&mut buf, b"null: 0x0", "null: {:p}", null_ptr);
245        assert_kformat_eq!(&mut buf, b"null: 0x0000000000000000", "null: {:#p}", null_ptr);
246    }
247
248    #[test]
249    fn test_single_evaluation() {
250        let mut buf = [0u8; 256];
251        let mut eval_count = 0;
252        let mut side_effect_fn = || {
253            eval_count += 1;
254            "single_eval"
255        };
256        let res = kformat!(&mut buf, "result: {:s}", side_effect_fn());
257        assert_eq!(res, std::format!("result: {}", "single_eval").as_bytes());
258        assert_eq!(eval_count, 1);
259
260        let mut bool_eval_count = 0;
261        let mut bool_side_effect_fn = || {
262            bool_eval_count += 1;
263            true
264        };
265        let res_b = kformat!(&mut buf, "flag: {:b}", bool_side_effect_fn());
266        assert_eq!(res_b, std::format!("flag: {}", true).as_bytes());
267        assert_eq!(bool_eval_count, 1);
268
269        let mut multi_eval_count = 0;
270        let mut multi_side_effect_fn = || {
271            multi_eval_count += 1;
272            100
273        };
274        let res_multi = kformat!(&mut buf, "{0} + {0} = 200", multi_side_effect_fn());
275        assert_eq!(res_multi, std::format!("{0} + {0} = 200", 100).as_bytes());
276        assert_eq!(multi_eval_count, 1);
277
278        let mut hex_eval_count = 0;
279        let mut hex_side_effect_fn = || {
280            hex_eval_count += 1;
281            0x2au32
282        };
283        let res_hex = kformat!(&mut buf, "{:#10x}", hex_side_effect_fn());
284        assert_eq!(res_hex, std::format!("{:#10x}", 0x2au32).as_bytes());
285        assert_eq!(hex_eval_count, 1);
286    }
287
288    #[test]
289    fn test_concat_format_string() {
290        let mut buf = [0u8; 256];
291        let val = 42;
292        assert_kformat_eq!(
293            &mut buf,
294            b"prefix_status: 42",
295            concat!("prefix_", "status: ", "{}"),
296            val
297        );
298        assert_kformat_eq!(
299            &mut buf,
300            b"test_1_true_c=99",
301            concat!("test_", 1, "_", true, "_", 'c', "={}"),
302            99
303        );
304    }
305
306    #[test]
307    fn test_captured_and_named_variables() {
308        let mut buf = [0u8; 256];
309        let user = "alice";
310        let score = 100;
311        assert_eq!(
312            kformat!(&mut buf, "player {user:s} has score {score}"),
313            std::format!("player {user} has score {score}").as_bytes()
314        );
315        assert_eq!(
316            kformat!(&mut buf, "{greeting:s}, {name:s}!", greeting = "hello", name = "world"),
317            std::format!("{greeting}, {name}!", greeting = "hello", name = "world").as_bytes()
318        );
319    }
320
321    #[test]
322    fn test_positional_arguments() {
323        let mut buf = [0u8; 256];
324        assert_kformat_eq!(&mut buf, b"a + b = a", "{0} + {1} = {0}", "a", "b");
325        assert_kformat_eq!(&mut buf, b"20 10 20", "{1} {0} {1}", 10, 20);
326    }
327
328    #[test]
329    fn test_escapes_and_percent() {
330        let mut buf = [0u8; 256];
331        assert_kformat_eq!(&mut buf, b"{hello}", "{{hello}}");
332        assert_kformat_eq!(&mut buf, b"100% completed", "100% completed");
333        assert_kformat_eq!(&mut buf, b"75% done", "{}% done", 75);
334        assert_kformat_eq!(&mut buf, b"{50%}", "{{{}%}}", 50);
335    }
336
337    #[test]
338    fn test_chars() {
339        let mut buf = [0u8; 256];
340        assert_kformat_eq!(&mut buf, b"char: Z", "char: {}", 'Z');
341        let c = '?';
342        assert_eq!(kformat!(&mut buf, "char: {:c}", c), std::format!("char: {}", c).as_bytes());
343        assert_eq!(
344            kformat!(&mut buf, "ref char: {:c}", &c),
345            std::format!("ref char: {}", &c).as_bytes()
346        );
347
348        // Alignment, width, and precision on chars
349        assert_kformat_eq!(&mut buf, b"'a     '", "'{:6}'", 'a');
350        assert_kformat_eq!(&mut buf, b"'a     '", "'{:<6}'", 'a');
351        assert_kformat_eq!(&mut buf, b"'     a'", "'{:>6}'", 'a');
352        assert_kformat_eq!(&mut buf, b"'a     '", "'{:06}'", 'a');
353        assert_kformat_eq!(&mut buf, b"''", "'{:.0}'", 'a');
354        assert_kformat_eq!(&mut buf, b"'a'", "'{:.5}'", 'a');
355
356        assert_eq!(kformat!(&mut buf, "'{:6c}'", c), std::format!("'{:6}'", c).as_bytes());
357        assert_eq!(kformat!(&mut buf, "'{:>6c}'", c), std::format!("'{:>6}'", c).as_bytes());
358        assert_eq!(kformat!(&mut buf, "'{:.0c}'", c), std::format!("'{:.0}'", c).as_bytes());
359
360        // u8 and byte literals: {} formats byte literal as integer (88), {:c} formats as ASCII character
361        let byte: u8 = b'A';
362        assert_kformat_eq!(&mut buf, b"byte lit auto: 88", "byte lit auto: {}", b'X');
363        assert_eq!(kformat!(&mut buf, "byte: {:c}", byte), b"byte: A");
364        assert_eq!(kformat!(&mut buf, "ref byte: {:c}", &byte), b"ref byte: A");
365        assert_eq!(kformat!(&mut buf, "byte lit: {:c}", b'K'), b"byte lit: K");
366
367        // Non-ASCII and multi-byte Unicode characters
368        assert_kformat_eq!(&mut buf, "crab: 🦀".as_bytes(), "crab: {}", '🦀');
369        assert_eq!(kformat!(&mut buf, "accent: {:c}", 'é'), "accent: é".as_bytes());
370    }
371
372    #[test]
373    fn test_booleans() {
374        let mut buf = [0u8; 256];
375        assert_kformat_eq!(&mut buf, b"flag: true", "flag: {}", true);
376        assert_kformat_eq!(&mut buf, b"flag: false", "flag: {}", false);
377        assert_kformat_eq!(&mut buf, b"'true    '", "'{:8}'", true);
378        assert_kformat_eq!(&mut buf, b"'    true'", "'{:>8}'", true);
379        assert_kformat_eq!(&mut buf, b"'true    '", "'{:08}'", true);
380        assert_kformat_eq!(&mut buf, b"'tru'", "'{:.3}'", true);
381
382        let f = false;
383        assert_eq!(kformat!(&mut buf, "flag: {:b}", f), std::format!("flag: {}", f).as_bytes());
384        assert_eq!(kformat!(&mut buf, "'{:8b}'", f), std::format!("'{:8}'", f).as_bytes());
385        assert_eq!(kformat!(&mut buf, "'{:>8b}'", f), std::format!("'{:>8}'", f).as_bytes());
386        assert_eq!(kformat!(&mut buf, "'{:.3b}'", f), std::format!("'{:.3}'", f).as_bytes());
387    }
388
389    #[test]
390    fn test_floats() {
391        let mut buf = [0u8; 256];
392        assert_eq!(
393            kformat!(&mut buf, "pi: {:.2f}", core::f64::consts::PI),
394            std::format!("pi: {:.2}", core::f64::consts::PI).as_bytes()
395        );
396        assert_kformat_eq!(&mut buf, b"float: 1.2346", "float: {:.4}", 1.23456f32);
397        assert_eq!(
398            kformat!(&mut buf, "float: {:.4f}", 1.23456f32),
399            std::format!("float: {:.4}", 1.23456f32).as_bytes()
400        );
401        assert_kformat_eq!(&mut buf, b"'      1.25'", "'{:10.2}'", 1.25f64);
402        assert_kformat_eq!(&mut buf, b"'1.25      '", "'{:<10.2}'", 1.25f64);
403        assert_kformat_eq!(&mut buf, b"'-000001.25'", "'{:010.2}'", -1.25f64);
404        assert_kformat_eq!(&mut buf, b"'+000001.25'", "'{:+010.2}'", 1.25f64);
405        let f = 1000.0;
406        let res_e = kformat!(&mut buf, "sci: {:.1e}", f);
407        assert_eq!(res_e, b"sci: 1.0e+03");
408    }
409
410    #[test]
411    fn test_buffer_truncation() {
412        let mut small_buf = [0u8; 8];
413        let res = kformat!(&mut small_buf, "1234567890");
414        assert_eq!(res, b"1234567");
415        assert_eq!(res.len(), 7);
416    }
417
418    #[test]
419    fn test_macro_wrapper_hygiene() {
420        macro_rules! wrapped_kformat {
421            ($buf:expr, $($tt:tt)*) => {
422                kformat!($buf, $($tt)*)
423            };
424        }
425        let mut buf = [0u8; 64];
426        let x = 123;
427        assert_eq!(wrapped_kformat!(&mut buf, "val: {}", x), b"val: 123");
428        assert_eq!(wrapped_kformat!(&mut buf, "val: {}", x), std::format!("val: {}", x).as_bytes());
429    }
430
431    #[test]
432    fn test_print_macros() {
433        kprint!("test printf {}", 123);
434        kprint!("100%");
435        kprintln!(" line2");
436        kprintln!("empty without args");
437        kprintln!("{:#x}", 0);
438        kprintln!("crab: {}", '🦀');
439    }
440}