1use kernel::prelude::*;
8
9pub use core::fmt::{
10 Arguments,
11 Debug,
12 Error,
13 Formatter,
14 Result,
15 Write, };
17
18#[doc(hidden)]
24pub struct Adapter<T>(pub T);
25
26macro_rules! impl_fmt_adapter_forward {
27 ($($trait:ident),* $(,)?) => {
28 $(
29 impl<T: $trait> $trait for Adapter<T> {
30 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
31 let Self(t) = self;
32 $trait::fmt(t, f)
33 }
34 }
35 )*
36 };
37}
38
39use core::fmt::{
40 Binary,
41 LowerExp,
42 LowerHex,
43 Octal,
44 UpperExp,
45 UpperHex, };
47use core::ptr::NonNull;
48impl_fmt_adapter_forward!(Debug, LowerHex, UpperHex, Octal, Binary, LowerExp, UpperExp);
49
50pub trait Pointer {
58 fn fmt(&self, f: &mut Formatter<'_>) -> Result;
60}
61
62pub struct HashedPtr<T: ?Sized>(pub *const T);
68
69impl<T: ?Sized> Pointer for HashedPtr<T> {
70 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
71 use crate::str::CStrExt as _;
72
73 let mut buf = [0u8; 32];
74
75 let default_width = (2 * size_of::<usize>() + 2) as c_int;
78 let width = match (f.sign_aware_zero_pad(), f.width()) {
79 (true, Some(w)) if w > 0 => w.min(buf.len() - 1) as c_int,
80 _ => default_width,
81 };
82
83 let len = unsafe {
88 crate::bindings::scnprintf(
89 buf.as_mut_ptr().cast(),
90 buf.len(),
91 c"%#0*p".as_char_ptr(),
92 width,
93 self.0.cast::<c_void>(),
94 )
95 };
96
97 let s = unsafe { core::str::from_utf8_unchecked(&buf[..len as usize]) };
99
100 if f.sign_aware_zero_pad() {
101 f.write_str(s)
103 } else {
104 f.pad(s)
105 }
106 }
107}
108
109impl<T: ?Sized> Pointer for *const T {
112 #[inline]
113 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
114 Pointer::fmt(&HashedPtr(*self), f)
115 }
116}
117
118impl<T: ?Sized> Pointer for *mut T {
119 #[inline]
120 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
121 Pointer::fmt(&HashedPtr(*self), f)
122 }
123}
124
125impl<T: ?Sized> Pointer for &T {
126 #[inline]
127 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
128 Pointer::fmt(&HashedPtr(*self), f)
129 }
130}
131
132impl<T: ?Sized> Pointer for &mut T {
133 #[inline]
134 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
135 Pointer::fmt(&HashedPtr(core::ptr::from_ref(*self)), f)
136 }
137}
138
139impl<T: ?Sized> Pointer for NonNull<T> {
140 #[inline]
141 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
142 Pointer::fmt(&HashedPtr(self.as_ptr()), f)
143 }
144}
145
146impl<T: Pointer> core::fmt::Pointer for Adapter<&T> {
148 #[inline]
149 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
150 Pointer::fmt(self.0, f)
151 }
152}
153
154pub trait Display {
162 fn fmt(&self, f: &mut Formatter<'_>) -> Result;
164}
165
166impl<T: ?Sized + Display> Display for &T {
167 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
168 Display::fmt(*self, f)
169 }
170}
171
172impl<T: ?Sized + Display> core::fmt::Display for Adapter<&T> {
173 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
174 let Self(t) = self;
175 Display::fmt(t, f)
176 }
177}
178
179macro_rules! impl_display_forward {
180 ($(
181 $( { $($generics:tt)* } )? $ty:ty $( { where $($where:tt)* } )?
182 ),* $(,)?) => {
183 $(
184 impl$($($generics)*)? Display for $ty $(where $($where)*)? {
185 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
186 core::fmt::Display::fmt(self, f)
187 }
188 }
189 )*
190 };
191}
192
193impl_display_forward!(
194 bool,
195 char,
196 core::panic::PanicInfo<'_>,
197 Arguments<'_>,
198 i128,
199 i16,
200 i32,
201 i64,
202 i8,
203 isize,
204 str,
205 u128,
206 u16,
207 u32,
208 u64,
209 u8,
210 usize,
211 {<T: ?Sized>} crate::sync::Arc<T> {where crate::sync::Arc<T>: core::fmt::Display},
212 {<T: ?Sized>} crate::sync::UniqueArc<T> {where crate::sync::UniqueArc<T>: core::fmt::Display},
213);
214
215#[macros::kunit_tests(rust_kernel_fmt)]
216mod tests {
217 use crate::{
218 bindings,
219 prelude::fmt,
220 str::CString, };
222
223 #[cfg(CONFIG_64BIT)]
224 mod expected {
225 pub(super) const PTR_VALUE: usize = 0xffffffffdeadbeef;
226 pub(super) const PTR_VAL_NO_CRNG: &str = "(____ptrval____)";
227 pub(super) const HASHED_PREFIX: &str = "0x00000000";
228 pub(super) const RAW_POINTER: &str = "0xffffffffdeadbeef";
229 pub(super) const PADDED_RIGHT: &str = " 0xffffffffdeadbeef";
230 pub(super) const ZERO_PADDED: &str = "0x000000ffffffffdeadbeef";
231 pub(super) const HASHED_PADDED_RIGHT_PREFIX: &str = " ";
232 pub(super) const HASHED_ZERO_PADDED_PREFIX: &str = "0x00000000000000";
233 pub(super) const CLAMPED: &str = "0x0000000000000ffffffffdeadbeef";
234 }
235
236 #[cfg(not(CONFIG_64BIT))]
237 mod expected {
238 pub(super) const PTR_VALUE: usize = 0xdeadbeef;
239 pub(super) const PTR_VAL_NO_CRNG: &str = "(ptrval)";
240 pub(super) const HASHED_PREFIX: &str = "0x";
241 pub(super) const RAW_POINTER: &str = "0xdeadbeef";
242 pub(super) const PADDED_RIGHT: &str = " 0xdeadbeef";
243 pub(super) const ZERO_PADDED: &str = "0x00000000000000deadbeef";
244 pub(super) const HASHED_PADDED_RIGHT_PREFIX: &str = " ";
245 pub(super) const HASHED_ZERO_PADDED_PREFIX: &str = "0x00000000000000";
246 pub(super) const CLAMPED: &str = "0x0000000000000000000000deadbeef";
247 }
248
249 #[test]
250 fn test_ptr_formatting() -> core::result::Result<(), crate::error::Error> {
251 let ptr: *const u8 = core::ptr::without_provenance(expected::PTR_VALUE);
252
253 let no_hash = unsafe { bindings::no_hash_pointers };
257
258 if no_hash {
259 let cstr = CString::try_from_fmt(fmt!("{:p}", ptr))?;
260 assert_eq!(cstr.to_str()?, expected::RAW_POINTER);
261
262 let cstr = CString::try_from_fmt(fmt!("{:>24p}", ptr))?;
263 assert_eq!(cstr.to_str()?, expected::PADDED_RIGHT);
264
265 let cstr = CString::try_from_fmt(fmt!("{:024p}", ptr))?;
266 assert_eq!(cstr.to_str()?, expected::ZERO_PADDED);
267
268 let cstr = CString::try_from_fmt(fmt!("{:0100p}", ptr))?;
269 assert_eq!(cstr.to_str()?, expected::CLAMPED);
270 } else {
271 let cstr = CString::try_from_fmt(fmt!("{:p}", ptr))?;
272 let formatted = cstr.to_str()?;
273 if formatted == expected::PTR_VAL_NO_CRNG {
275 return Ok(());
276 }
277 assert!(formatted.starts_with(expected::HASHED_PREFIX));
278 assert_ne!(formatted, expected::RAW_POINTER);
279
280 let cstr = CString::try_from_fmt(fmt!("{:>24p}", ptr))?;
281 assert!(cstr
282 .to_str()?
283 .starts_with(expected::HASHED_PADDED_RIGHT_PREFIX));
284
285 let cstr = CString::try_from_fmt(fmt!("{:024p}", ptr))?;
286 assert!(cstr
287 .to_str()?
288 .starts_with(expected::HASHED_ZERO_PADDED_PREFIX));
289
290 let cstr = CString::try_from_fmt(fmt!("{:0100p}", ptr))?;
291 let output = cstr.to_str()?;
292 assert!(output.starts_with("0x"));
293 assert!(!output[2..].chars().all(|c| c == '0'));
294 }
295
296 Ok(())
297 }
298}