This repository is my submission to Professor Porquet’s screening test for Lab C (Educational OS in Rust).
About
The original hexdump functionality that was mentioned in the doc was simply to
print the contents of a file in hex, with an optional -n
flag to specify how
many bytes to print.
This functionality was achieved in the second commit of this repository, and can be seen by viewing that commit and its code or by clicking here.
When it comes to parsing arguments, I originally used Clap
. However, while it
drastically simplifies the code and streamlines the process of parsing CLI
arguments, it was brought to my attention that kernel-level code should avoid
using non-std
modules. Hence, I reverted the program back to using a manual
argument parser.
Improvements / Features
After completing the core hexdump
functionality along with the -n
option, I
took it on as a challenge to implement more features as I saw fit.
Here is an updated --help
menu, which displays all the features I added.
$ hexdump_rust.exe --help
hexdump: A tool used to print/format the bytes of an input file.
Usage: hexdump [OPTIONS] <FILE>
-n <NUM> Total number of bytes to read.
-w --width <NUM> Number of bytes to print per line. (Default: 16)
-c --chunk-size <NUM> Number of bytes to print per space-separated chunk. (Default: 2)
-s --start-offset <NUM> Starting offset to read file from. (Default: 0)
-t --translate Enables in-line ASCII translation.
-o --no-offset Disables offset column.
-h --help Prints this message.
I decided not to copy the real hexdump
’s features exactly and instead took the
creative liberty to add ones that made sense and showcased Rust’s power the
best. Many of the original hexdump
’s features are still present, but have just
been generalized through different options/flags.
All added features mentioned above are working as intended and have tests to ensure their functionality.
When it comes to error handling, we can use a nifty trick of having main()
returning a Result
type. This way, Rust will automatically print the Err
type in the case of any failure:
$ hexdump_rust.exe not-a-file
Error: Os { code: 2, kind: NotFound, message: "The system cannot find the file specified." }
error: process didn't exit successfully: `hexdump_rust.exe not-a-file` (exit code: 1)
$ hexdump_rust.exe example.bin -s 257
Error: LengthError { message: "Starting offset (257) was larger than the file length (256)" }
error: process didn't exit successfully: `hexdump_rust.exe example.bin -s 257` (exit code: 1)
An added benefit of this approach is to allow us to utilize the ?
operator on
any Result
type present within the code. This drastically cleans up the logic
and makes writing new code much easier.
Examples
Here are examples of the non-obvious features on the same file used in the document:
-w, --width <NUM>
$ hexdump_rust.exe hexdump -n 256 -w 8
00000000 7f45 4c46 0201 0100
00000008 0000 0000 0000 0000
00000010 0200 f300 0100 0000
00000018 b606 0100 0000 0000
00000020 4000 0000 0000 0000
00000028 785c 0000 0000 0000
00000030 0100 0000 4000 3800
00000038 0400 4000 1100 1000
00000040 0300 0070 0400 0000
00000048 2330 0000 0000 0000
00000050 0000 0000 0000 0000
00000058 0000 0000 0000 0000
00000060 4a00 0000 0000 0000
00000068 0000 0000 0000 0000
00000070 0100 0000 0000 0000
00000078 0100 0000 0500 0000
00000080 0010 0000 0000 0000
00000088 0000 0100 0000 0000
00000090 0000 0100 0000 0000
00000098 9f10 0000 0000 0000
000000a0 9f10 0000 0000 0000
000000a8 0010 0000 0000 0000
000000b0 0100 0000 0600 0000
000000b8 0030 0000 0000 0000
000000c0 0020 0100 0000 0000
000000c8 0020 0100 0000 0000
000000d0 1100 0000 0000 0000
000000d8 1802 0000 0000 0000
000000e0 0010 0000 0000 0000
000000e8 51e5 7464 0600 0000
000000f0 0000 0000 0000 0000
000000f8 0000 0000 0000 0000
-c, --chunk-size <NUM>
$ hexdump_rust.exe hexdump -n 256 -c 1
00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
00000010 02 00 f3 00 01 00 00 00 b6 06 01 00 00 00 00 00
00000020 40 00 00 00 00 00 00 00 78 5c 00 00 00 00 00 00
00000030 01 00 00 00 40 00 38 00 04 00 40 00 11 00 10 00
00000040 03 00 00 70 04 00 00 00 23 30 00 00 00 00 00 00
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000060 4a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000070 01 00 00 00 00 00 00 00 01 00 00 00 05 00 00 00
00000080 00 10 00 00 00 00 00 00 00 00 01 00 00 00 00 00
00000090 00 00 01 00 00 00 00 00 9f 10 00 00 00 00 00 00
000000a0 9f 10 00 00 00 00 00 00 00 10 00 00 00 00 00 00
000000b0 01 00 00 00 06 00 00 00 00 30 00 00 00 00 00 00
000000c0 00 20 01 00 00 00 00 00 00 20 01 00 00 00 00 00
000000d0 11 00 00 00 00 00 00 00 18 02 00 00 00 00 00 00
000000e0 00 10 00 00 00 00 00 00 51 e5 74 64 06 00 00 00
000000f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-t, --translate
$ hexdump_rust.exe hexdump -n 256 -t
00000000 7f45 4c46 0201 0100 0000 0000 0000 0000 |.ELF............|
00000010 0200 f300 0100 0000 b606 0100 0000 0000 |................|
00000020 4000 0000 0000 0000 785c 0000 0000 0000 |@.......x\......|
00000030 0100 0000 4000 3800 0400 4000 1100 1000 |[email protected]...@.....|
00000040 0300 0070 0400 0000 2330 0000 0000 0000 |...p....#0......|
00000050 0000 0000 0000 0000 0000 0000 0000 0000 |................|
00000060 4a00 0000 0000 0000 0000 0000 0000 0000 |J...............|
00000070 0100 0000 0000 0000 0100 0000 0500 0000 |................|
00000080 0010 0000 0000 0000 0000 0100 0000 0000 |................|
00000090 0000 0100 0000 0000 9f10 0000 0000 0000 |................|
000000a0 9f10 0000 0000 0000 0010 0000 0000 0000 |................|
000000b0 0100 0000 0600 0000 0030 0000 0000 0000 |.........0......|
000000c0 0020 0100 0000 0000 0020 0100 0000 0000 |. ....... ......|
000000d0 1100 0000 0000 0000 1802 0000 0000 0000 |................|
000000e0 0010 0000 0000 0000 51e5 7464 0600 0000 |........Q.td....|
000000f0 0000 0000 0000 0000 0000 0000 0000 0000 |................|
(It’s important to note that on some architectures, the hexdump
command
reverses each “chunk” due to some processors using the little-endian convention
for 16-bit words. My implementation of the program does not do this, and instead
prints all bytes in order)
Testing
This project can be tested via Cargo’s built-in testing tool.
All tests are located within tests.rs
and linted with the #[test]
macro. To
run all tests, simply run cargo test
and a detailed summary of the results
will be printed to the screen. All tests use .bin
or .txt
files located
within the root directory, so please ensure you pull these before running the
tests.
Final Thoughts
I loved working on this project, as it tested my knowledge of Rust as well as put me into the mindset of writing “kernel level” code. Writing my own Error types and ensuring that the program should never fail unexpectedly was a fun challenge to take on.
I may continue working on this project even after Professor Porquet’s lab applications close, so if you have any suggestions on features I should add or coding conventions, please feel free to let me know!