1 // SPDX-License-Identifier: GPL-2.0 2 // Copyright (C) 2025 Google LLC. 3 4 //! Traits for rendering or updating values exported to DebugFS. 5 6 use crate::{ 7 alloc::Allocator, 8 fmt, 9 fs::file, 10 prelude::*, 11 sync::{ 12 atomic::{ 13 Atomic, 14 AtomicBasicOps, 15 AtomicType, 16 Relaxed, // 17 }, 18 Arc, 19 Mutex, // 20 }, 21 uaccess::{ 22 UserSliceReader, 23 UserSliceWriter, // 24 }, 25 }; 26 27 use core::{ 28 ops::{ 29 Deref, 30 DerefMut, // 31 }, 32 str::FromStr, 33 }; 34 35 use zerocopy::Immutable; 36 37 /// A trait for types that can be written into a string. 38 /// 39 /// This works very similarly to `Debug`, and is automatically implemented if `Debug` is 40 /// implemented for a type. It is also implemented for any writable type inside a `Mutex`. 41 /// 42 /// The derived implementation of `Debug` [may 43 /// change](https://doc.rust-lang.org/std/fmt/trait.Debug.html#stability) 44 /// between Rust versions, so if stability is key for your use case, please implement `Writer` 45 /// explicitly instead. 46 pub trait Writer { 47 /// Formats the value using the given formatter. 48 fn write(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result; 49 } 50 51 impl<T: Writer> Writer for Mutex<T> { 52 fn write(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 53 self.lock().write(f) 54 } 55 } 56 57 impl<T: fmt::Debug> Writer for T { 58 fn write(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 59 writeln!(f, "{self:?}") 60 } 61 } 62 63 /// Trait for types that can be written out as binary. 64 pub trait BinaryWriter { 65 /// Writes the binary form of `self` into `writer`. 66 /// 67 /// `offset` is the requested offset into the binary representation of `self`. 68 /// 69 /// On success, returns the number of bytes written in to `writer`. 70 fn write_to_slice( 71 &self, 72 writer: &mut UserSliceWriter, 73 offset: &mut file::Offset, 74 ) -> Result<usize>; 75 } 76 77 // Base implementation for any `T: Immutable + IntoBytes`. 78 impl<T: Immutable + IntoBytes> BinaryWriter for T { 79 fn write_to_slice( 80 &self, 81 writer: &mut UserSliceWriter, 82 offset: &mut file::Offset, 83 ) -> Result<usize> { 84 writer.write_slice_file(self.as_bytes(), offset) 85 } 86 } 87 88 // Delegate for `Mutex<T>`: Support a `T` with an outer mutex. 89 impl<T: BinaryWriter> BinaryWriter for Mutex<T> { 90 fn write_to_slice( 91 &self, 92 writer: &mut UserSliceWriter, 93 offset: &mut file::Offset, 94 ) -> Result<usize> { 95 let guard = self.lock(); 96 97 guard.write_to_slice(writer, offset) 98 } 99 } 100 101 // Delegate for `Box<T, A>`: Support a `Box<T, A>` with no lock or an inner lock. 102 impl<T, A> BinaryWriter for Box<T, A> 103 where 104 T: BinaryWriter, 105 A: Allocator, 106 { 107 fn write_to_slice( 108 &self, 109 writer: &mut UserSliceWriter, 110 offset: &mut file::Offset, 111 ) -> Result<usize> { 112 self.deref().write_to_slice(writer, offset) 113 } 114 } 115 116 // Delegate for `Pin<Box<T, A>>`: Support a `Pin<Box<T, A>>` with no lock or an inner lock. 117 impl<T, A> BinaryWriter for Pin<Box<T, A>> 118 where 119 T: BinaryWriter, 120 A: Allocator, 121 { 122 fn write_to_slice( 123 &self, 124 writer: &mut UserSliceWriter, 125 offset: &mut file::Offset, 126 ) -> Result<usize> { 127 self.deref().write_to_slice(writer, offset) 128 } 129 } 130 131 // Delegate for `Arc<T>`: Support a `Arc<T>` with no lock or an inner lock. 132 impl<T> BinaryWriter for Arc<T> 133 where 134 T: BinaryWriter, 135 { 136 fn write_to_slice( 137 &self, 138 writer: &mut UserSliceWriter, 139 offset: &mut file::Offset, 140 ) -> Result<usize> { 141 self.deref().write_to_slice(writer, offset) 142 } 143 } 144 145 // Delegate for `Vec<T, A>`. 146 impl<T, A> BinaryWriter for Vec<T, A> 147 where 148 T: Immutable + IntoBytes, 149 A: Allocator, 150 { 151 fn write_to_slice( 152 &self, 153 writer: &mut UserSliceWriter, 154 offset: &mut file::Offset, 155 ) -> Result<usize> { 156 writer.write_slice_file(self.as_bytes(), offset) 157 } 158 } 159 160 /// A trait for types that can be updated from a user slice. 161 /// 162 /// This works similarly to `FromStr`, but operates on a `UserSliceReader` rather than a &str. 163 /// 164 /// It is automatically implemented for all atomic integers, or any type that implements `FromStr` 165 /// wrapped in a `Mutex`. 166 pub trait Reader { 167 /// Updates the value from the given user slice. 168 fn read_from_slice(&self, reader: &mut UserSliceReader) -> Result; 169 } 170 171 impl<T: FromStr + Unpin> Reader for Mutex<T> { 172 fn read_from_slice(&self, reader: &mut UserSliceReader) -> Result { 173 let mut buf = [0u8; 128]; 174 if reader.len() > buf.len() { 175 return Err(EINVAL); 176 } 177 let n = reader.len(); 178 reader.read_slice(&mut buf[..n])?; 179 180 let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?; 181 let val = s.trim().parse::<T>().map_err(|_| EINVAL)?; 182 *self.lock() = val; 183 Ok(()) 184 } 185 } 186 187 impl<T: AtomicType + FromStr> Reader for Atomic<T> 188 where 189 T::Repr: AtomicBasicOps, 190 { 191 fn read_from_slice(&self, reader: &mut UserSliceReader) -> Result { 192 let mut buf = [0u8; 21]; // Enough for a 64-bit number. 193 if reader.len() > buf.len() { 194 return Err(EINVAL); 195 } 196 let n = reader.len(); 197 reader.read_slice(&mut buf[..n])?; 198 199 let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?; 200 let val = s.trim().parse::<T>().map_err(|_| EINVAL)?; 201 self.store(val, Relaxed); 202 Ok(()) 203 } 204 } 205 206 /// Trait for types that can be constructed from a binary representation. 207 /// 208 /// See also [`BinaryReader`] for interior mutability. 209 pub trait BinaryReaderMut { 210 /// Reads the binary form of `self` from `reader`. 211 /// 212 /// Same as [`BinaryReader::read_from_slice`], but takes a mutable reference. 213 /// 214 /// `offset` is the requested offset into the binary representation of `self`. 215 /// 216 /// On success, returns the number of bytes read from `reader`. 217 fn read_from_slice_mut( 218 &mut self, 219 reader: &mut UserSliceReader, 220 offset: &mut file::Offset, 221 ) -> Result<usize>; 222 } 223 224 // Base implementation for any `T: FromBytes + IntoBytes`. 225 impl<T: FromBytes + IntoBytes> BinaryReaderMut for T { 226 fn read_from_slice_mut( 227 &mut self, 228 reader: &mut UserSliceReader, 229 offset: &mut file::Offset, 230 ) -> Result<usize> { 231 reader.read_slice_file(self.as_mut_bytes(), offset) 232 } 233 } 234 235 // Delegate for `Box<T, A>`: Support a `Box<T, A>` with an outer lock. 236 impl<T: ?Sized + BinaryReaderMut, A: Allocator> BinaryReaderMut for Box<T, A> { 237 fn read_from_slice_mut( 238 &mut self, 239 reader: &mut UserSliceReader, 240 offset: &mut file::Offset, 241 ) -> Result<usize> { 242 self.deref_mut().read_from_slice_mut(reader, offset) 243 } 244 } 245 246 // Delegate for `Vec<T, A>`: Support a `Vec<T, A>` with an outer lock. 247 impl<T, A> BinaryReaderMut for Vec<T, A> 248 where 249 T: FromBytes + IntoBytes, 250 A: Allocator, 251 { 252 fn read_from_slice_mut( 253 &mut self, 254 reader: &mut UserSliceReader, 255 offset: &mut file::Offset, 256 ) -> Result<usize> { 257 reader.read_slice_file(self.as_mut_bytes(), offset) 258 } 259 } 260 261 /// Trait for types that can be constructed from a binary representation. 262 /// 263 /// See also [`BinaryReaderMut`] for the mutable version. 264 pub trait BinaryReader { 265 /// Reads the binary form of `self` from `reader`. 266 /// 267 /// `offset` is the requested offset into the binary representation of `self`. 268 /// 269 /// On success, returns the number of bytes read from `reader`. 270 fn read_from_slice( 271 &self, 272 reader: &mut UserSliceReader, 273 offset: &mut file::Offset, 274 ) -> Result<usize>; 275 } 276 277 // Delegate for `Mutex<T>`: Support a `T` with an outer `Mutex`. 278 impl<T: BinaryReaderMut + Unpin> BinaryReader for Mutex<T> { 279 fn read_from_slice( 280 &self, 281 reader: &mut UserSliceReader, 282 offset: &mut file::Offset, 283 ) -> Result<usize> { 284 let mut this = self.lock(); 285 286 this.read_from_slice_mut(reader, offset) 287 } 288 } 289 290 // Delegate for `Box<T, A>`: Support a `Box<T, A>` with an inner lock. 291 impl<T: ?Sized + BinaryReader, A: Allocator> BinaryReader for Box<T, A> { 292 fn read_from_slice( 293 &self, 294 reader: &mut UserSliceReader, 295 offset: &mut file::Offset, 296 ) -> Result<usize> { 297 self.deref().read_from_slice(reader, offset) 298 } 299 } 300 301 // Delegate for `Pin<Box<T, A>>`: Support a `Pin<Box<T, A>>` with an inner lock. 302 impl<T: ?Sized + BinaryReader, A: Allocator> BinaryReader for Pin<Box<T, A>> { 303 fn read_from_slice( 304 &self, 305 reader: &mut UserSliceReader, 306 offset: &mut file::Offset, 307 ) -> Result<usize> { 308 self.deref().read_from_slice(reader, offset) 309 } 310 } 311 312 // Delegate for `Arc<T>`: Support an `Arc<T>` with an inner lock. 313 impl<T: ?Sized + BinaryReader> BinaryReader for Arc<T> { 314 fn read_from_slice( 315 &self, 316 reader: &mut UserSliceReader, 317 offset: &mut file::Offset, 318 ) -> Result<usize> { 319 self.deref().read_from_slice(reader, offset) 320 } 321 } 322