keos/util.rs
1//! Debugging Utilities.
2
3use alloc::string::String;
4use core::fmt::Write;
5
6use crate::{KernelError, fs::RegularFile};
7
8/// Dumps the bytes in `buf` to the console as hex bytes, arranged 16 per line.
9///
10/// Each line is buffered into a String and printed in a single operation to
11/// avoid console race conditions in multi-threaded environments.
12///
13/// # Arguments
14///
15/// * `ofs` - The starting offset for the first byte in `buf`.
16/// * `buf` - The slice of bytes to dump.
17/// * `ascii` - A flag to enable or disable the ASCII character view.
18///
19/// # Usage
20///
21/// ```
22/// let my_data: &[u8] = &[0xDE, 0xAD, 0xBE, 0xEF];
23/// // This is a safe function and can be called directly.
24/// hex_dump_slice(0x100, my_data, true);
25/// ```
26pub fn hex_dump_slice(ofs: usize, buf: &[u8], ascii: bool) {
27 const PER_LINE: usize = 16; // Maximum bytes per line.
28
29 // Create mutable local variables from the immutable arguments to track state.
30 let mut current_ofs = ofs;
31 let mut current_buf = buf;
32
33 // Loop until all bytes in the buffer have been processed.
34 while !current_buf.is_empty() {
35 // Create a mutable string to buffer the output for the current line.
36 let mut line_buffer = String::new();
37
38 // --- Calculate this line's layout ---
39
40 // `start_col` is the column (0-15) where the first byte of this iteration will
41 // be printed.
42 let start_col = current_ofs % PER_LINE;
43
44 // Determine how many bytes from the buffer we will print on this line.
45 let bytes_on_this_line = (PER_LINE - start_col).min(current_buf.len());
46
47 // `end_col` is the column where our printing will stop.
48 let end_col = start_col + bytes_on_this_line;
49
50 // --- Build the line string ---
51
52 // The offset printed at the start of the line is always rounded down.
53 // The .unwrap() is safe because writing to a String should not fail.
54 write!(&mut line_buffer, "{:016x} ", current_ofs - start_col).unwrap();
55
56 // --- Append the hex representation ---
57
58 // 1. Append leading spaces for alignment.
59 for _ in 0..start_col {
60 write!(&mut line_buffer, " ").unwrap();
61 }
62
63 // 2. Append the hex value for each byte.
64 for (i, c) in current_buf.iter().enumerate().take(bytes_on_this_line) {
65 let current_col = start_col + i;
66 write!(&mut line_buffer, "{c:02x}").unwrap();
67 if current_col == PER_LINE / 2 - 1 {
68 write!(&mut line_buffer, "-").unwrap();
69 } else {
70 write!(&mut line_buffer, " ").unwrap();
71 }
72 }
73
74 // --- Append the ASCII representation (if requested) ---
75 if ascii {
76 // 3. Append trailing spaces to align the ASCII section.
77 for _ in end_col..PER_LINE {
78 write!(&mut line_buffer, " ").unwrap();
79 }
80
81 write!(&mut line_buffer, "|").unwrap();
82
83 // 1. Append leading spaces for alignment.
84 for _ in 0..start_col {
85 write!(&mut line_buffer, " ").unwrap();
86 }
87
88 // 2. Append the character for each byte.
89 for c in current_buf {
90 if (0x20..0x7e).contains(c) {
91 write!(&mut line_buffer, "{}", *c as char).unwrap();
92 } else {
93 write!(&mut line_buffer, ".").unwrap();
94 }
95 }
96
97 // 3. Append trailing spaces to align the final bar.
98 for _ in end_col..PER_LINE {
99 write!(&mut line_buffer, " ").unwrap();
100 }
101 write!(&mut line_buffer, "|").unwrap();
102 }
103
104 // Print the fully constructed line buffer at once.
105 println!("{}", line_buffer);
106
107 // --- Update state for the next iteration ---
108
109 // Advance the buffer slice past the bytes we just printed.
110 current_buf = ¤t_buf[bytes_on_this_line..];
111 // Increment the master offset.
112 current_ofs += bytes_on_this_line;
113 }
114}
115
116/// Dumps the memory occupied by a value of type `T` to the console.
117///
118/// This function takes a raw pointer to the data. The size of the data to dump
119/// is determined by `core::mem::size_of::<T>()`. This function handles
120/// potentially unaligned pointers by first performing an unaligned read.
121///
122/// # Safety
123///
124/// The caller must ensure that `ptr` is valid for reads of `size_of::<T>()`
125/// bytes for the duration of this function call. The pointer does **not** need
126/// to be aligned.
127///
128/// # Arguments
129///
130/// * `ofs` - The starting offset for the first byte.
131/// * `ptr` - A raw pointer to the data to be dumped.
132/// * `ascii` - A flag to enable or disable the ASCII character view.
133///
134/// # Usage
135///
136/// ```
137/// let my_data: u32 = 0x12345678;
138/// // This function is unsafe and must be called within an unsafe block.
139/// unsafe {
140/// hex_dump(0, &my_data, true);
141/// }
142/// ```
143pub unsafe fn hex_dump<T>(ofs: usize, ptr: *const T, ascii: bool) {
144 unsafe {
145 // To safely handle potentially unaligned pointers, we first perform an
146 // unaligned read into a local variable on the stack. This `value` is
147 // guaranteed to have the correct alignment for type T.
148 let value: T = core::ptr::read_unaligned(ptr);
149
150 // Now we can safely get a pointer to the local, aligned variable.
151 let aligned_ptr: *const T = &value;
152
153 // Determine the size of the data based on its type.
154 let size = core::mem::size_of::<T>();
155
156 // Create a byte slice from the aligned pointer and size. This is safe
157 // because `aligned_ptr` points to a valid, local variable.
158 let slice = core::slice::from_raw_parts(aligned_ptr as *const u8, size);
159
160 // Call the safe, slice-based implementation.
161 hex_dump_slice(ofs, slice, ascii)
162 }
163}
164
165/// Copy a RegularFile's content into another RegularFile.
166pub fn copy_file(src: &RegularFile, dest: &RegularFile) -> Result<(), KernelError> {
167 let mut buf: [u8; 4096] = [0u8; 4096];
168 let size = src.size();
169
170 for i in 0..=(size / 4096) {
171 let position = i * 4096;
172 let size_to_copy = if (i + 1) * 4096 > size {
173 size % 4096
174 } else {
175 4096
176 };
177
178 src.read(position, &mut buf[..size_to_copy])?;
179 dest.write(position, &buf[..size_to_copy])?;
180 }
181 dest.writeback()?;
182
183 Ok(())
184}