diff --git a/README.md b/README.md new file mode 100644 index 0000000..d2659a5 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# Trevors Utilities + +## Overview + +Trevors Utilities is a collection of code that Trevor has found useful. +All code has the following criteria: + +- Traits as appropriate, implemented for concrete types +- All code must have 100% code coverage to be part of a 'tagged release' +- All code must have 100% documentation coverage with examples of inputs and outputs + +## Code Areas + +### Number System Conversion + +A variety of methods for converting numbers between systems. Functionality includes + +- Joining u8 values into u16 values with low and high bytes +- Splitting u16 values into the high and low u8 values +- Swapping endianness of u16 values +- Converting an array of bool values into the equivalent value in u8 with bits in MSB order + +### Test Compression + +Methods for compressing and decompressing test results using deflate compression. + diff --git a/resources/data_to_text/quickbrownfox.display b/resources/data_to_text/quickbrownfox.display new file mode 100644 index 0000000..bb7c368 --- /dev/null +++ b/resources/data_to_text/quickbrownfox.display @@ -0,0 +1,3 @@ +0x74 0x68 0x65 0x20 0x71 0x75 0x69 0x63 0x6b 0x20 0x62 0x72 0x6f 0x77 0x6e 0x20 the quick brown +0x66 0x6f 0x78 0x20 0x6a 0x75 0x6d 0x70 0x73 0x20 0x6f 0x76 0x65 0x72 0x20 0x74 fox jumps over t +0x68 0x65 0x20 0x6c 0x61 0x7a 0x79 0x20 0x64 0x6f 0x67 he lazy dog diff --git a/resources/data_to_text/quickbrownfoxtest.bin b/resources/data_to_text/quickbrownfoxtest.bin new file mode 100644 index 0000000..33cf2ee --- /dev/null +++ b/resources/data_to_text/quickbrownfoxtest.bin @@ -0,0 +1 @@ +the quick brown fox jumped over the lazy dog \ No newline at end of file diff --git a/src/bin/gen_test.rs b/src/bin/gen_test.rs new file mode 100644 index 0000000..cff71fe --- /dev/null +++ b/src/bin/gen_test.rs @@ -0,0 +1,10 @@ +use trevors_utilities::data_to_text::DataToText; + +fn main() { + let text = "the quick brown fox jumps over the lazy dog"; + + let displayed_hex = DataToText::data_to_text(text.as_bytes()); + + println!("RAW: [{text}]"); + println!("FORMATTED:\n{displayed_hex}"); +} diff --git a/src/data_to_text.rs b/src/data_to_text.rs new file mode 100644 index 0000000..789bf35 --- /dev/null +++ b/src/data_to_text.rs @@ -0,0 +1,45 @@ +pub struct DataToText {} + +impl DataToText { + /// data_to_text_window + /// + /// Convert a block of u8 data to text for user display skipping specified bytes + pub fn data_to_text_window(to_convert: &[u8], offset: usize, length: usize) -> String { + DataToText::data_to_text(&to_convert[offset..(offset + length)]) + } + + /// data_to_text + /// + /// Convert a block of u8 data to text for user display + pub fn data_to_text(to_convert: &[u8]) -> String { + let mut result = String::new(); + + let mut hex_line = String::new(); + let mut ascii_line = String::new(); + + for (idx, byte) in to_convert.iter().enumerate() { + hex_line += &format!("0x{:02x} ", byte); + + let ascii_char = if byte.is_ascii_graphic() || byte.is_ascii_whitespace() { + *byte as char + } else { + '.' + }; + ascii_line.push(ascii_char); + + if idx % 16 == 15 { + // Write full line + result += &format!("{:<96} {}\n", hex_line, ascii_line); + hex_line.clear(); + ascii_line.clear(); + } + } + + // Final line if there are leftover bytes + if !hex_line.is_empty() { + result += &format!("{:<96} {}\n", hex_line, ascii_line); + } + + result + } +} diff --git a/src/lib.rs b/src/lib.rs index 41bad9b..6a2297a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,3 @@ -pub mod test_compression; pub mod number_system_conversion; +pub mod test_compression; +pub mod data_to_text; \ No newline at end of file diff --git a/src/number_system_conversion.rs b/src/number_system_conversion.rs index 7a12845..dea05c9 100644 --- a/src/number_system_conversion.rs +++ b/src/number_system_conversion.rs @@ -94,7 +94,24 @@ impl NumberSystemConversion { (high as u16) << 8 | low as u16 } - pub fn join_bytes_u16_from_u8(high: u8, low: u8) -> u16 { - low as u16 | ((high as u16) << 8) + + /// clear_high_bits + /// + /// Clear the 8 MSB of the value + /// + /// ex: 0xffff -> 0x00ff + /// 0xabcd -> 0x00cd + pub fn clear_high_bits(to_clear: u16) -> u16 { + to_clear & 0x00ff + } + + /// clear_low_bits + /// + /// Clear the 8 LSB of the value + /// + /// ex: 0xffff -> 0xff00 + /// 0xabcd -> 0xab00 + pub fn clear_low_bits(to_clear: u16) -> u16 { + to_clear & 0xff00 } } diff --git a/src/test_compression.rs b/src/test_compression.rs index 3bf059b..828cdef 100644 --- a/src/test_compression.rs +++ b/src/test_compression.rs @@ -1,10 +1,11 @@ +use flate2::Compression; +use flate2::read::DeflateDecoder; +use flate2::write::DeflateEncoder; use std::fs::{File, OpenOptions}; use std::io; use std::io::{Read, Write}; use std::path::Path; -use flate2::Compression; -use flate2::read::DeflateDecoder; -use flate2::write::DeflateEncoder; +use std::string::String; pub struct TestCompression {} @@ -17,20 +18,24 @@ impl TestCompression { encoder.finish().expect("Failed to finish compression") } - pub fn decompress_to_string(compressed_data: &[u8]) -> Result { + pub fn decompress_to_string(compressed_data: &[u8]) -> String { let mut decoder = DeflateDecoder::new(compressed_data); let mut decompressed = String::new(); - decoder.read_to_string(&mut decompressed)?; - Ok(decompressed) + decoder + .read_to_string(&mut decompressed) + .expect("Unablew to decompress data to string"); + decompressed } /// Load a pre-compressed test result to a string for comparison in an assert macro pub fn load_compressed_file_to_string(source: &Path) -> String { - let mut compressed_file = File::open(&source).expect(format!("Unable to open compressed file at [{}]", source.display()).as_str()); + let mut compressed_file = File::open(&source) + .expect(format!("Unable to open compressed file at [{}]", source.display()).as_str()); let mut compressed_data = Vec::new(); - compressed_file.read_to_end(&mut compressed_data) + compressed_file + .read_to_end(&mut compressed_data) .expect(format!("Unable to read compressed data from [{}]", source.display()).as_str()); - TestCompression::decompress_to_string(&compressed_data).unwrap() + TestCompression::decompress_to_string(&compressed_data) } /// Save a str to a specified file as compressed data @@ -40,13 +45,17 @@ impl TestCompression { .truncate(true) // optional: clear existing contents .create(true) .open(target_file) - .expect(&format!("Unable to open target file [{}] for writing", target_file.display())); + .expect(&format!( + "Unable to open target file [{}] for writing", + target_file.display() + )); let compressed_data = TestCompression::compress_string(source); - compressed_file - .write_all(&compressed_data) - .expect(&format!("Unable to write compressed data to [{}]", target_file.display())); + compressed_file.write_all(&compressed_data).expect(&format!( + "Unable to write compressed data to [{}]", + target_file.display() + )); } pub fn compress_file>(source: P, destination: P) -> io::Result<()> { @@ -68,13 +77,50 @@ impl TestCompression { pub fn compress_string_to_file(text: &str, target: &Path) { let compressed_text = TestCompression::compress_string(text); let mut writer = File::create(target).expect("Unable to create target file."); - writer.write_all(&compressed_text).expect("Unable to write compressed data"); + writer + .write_all(&compressed_text) + .expect("Unable to write compressed data"); } pub fn decompress_file_to_string(source: &Path) -> String { let mut compressed_file = File::open(source).expect("Unable to open source to compress."); let mut compressed_data = Vec::new(); - compressed_file.read_to_end(&mut compressed_data).expect("Unable to read compressed data"); - TestCompression::decompress_to_string(&compressed_data).unwrap() + compressed_file + .read_to_end(&mut compressed_data) + .expect("Unable to read compressed data"); + TestCompression::decompress_to_string(&compressed_data) } + + pub fn compress_file_to_array(file_to_compress: &Path) -> Vec { + let mut read_buffer = String::new(); + let mut file = File::open(file_to_compress).unwrap(); + file.read_to_string(&mut read_buffer) + .expect("Unable to read data to compress"); + TestCompression::compress_string(&read_buffer) + } + + pub fn decompress_file_to_array(to_decompress: &Path) -> Vec { + let mut read_buffer = Vec::new(); + let mut file = File::open(to_decompress).expect( + format!( + "Unable to open file to decompress [{}]", + to_decompress.display() + ) + .as_str(), + ); + file.read_to_end(&mut read_buffer) + .expect(format!("Unable to read from [{}]", to_decompress.display()).as_str()); + read_buffer + } + + // let file = File::open(input_path) + // .map_err(|e| std::io::Error::new(e.kind(), format!("Unable to open input path [{}]: {}", input_path.display(), e))); + // + // let mut decoder = DeflateDecoder::new(file.unwrap()); + // let mut return_vec = Vec::new(); + // decoder.read_to_end(&mut return_vec) + // .map_err(|e| std::io::Error::new(e.kind(), format!("Unable to decompress data for [{}]: {}", input_path.display(), e))); + // + // debug!("Decompressed file from path: {}", input_path.display()); + // return_vec } diff --git a/tests/data_to_text.rs b/tests/data_to_text.rs new file mode 100644 index 0000000..649827f --- /dev/null +++ b/tests/data_to_text.rs @@ -0,0 +1,27 @@ +use trevors_utilities::data_to_text::DataToText; + +#[test] +fn data_to_text() { + let data_to_display: &[u8] = &[ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, + 0x74, 0x20, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x20, + ]; + let expected_data: &str = "0x66 0x67 0x68 0x69 0x6a 0x6b 0x6c 0x6d 0x6e 0x6f 0x70 0x71 0x72 0x73 0x74 0x20 fghijklmnopqrst \n0x76 0x77 0x78 0x79 0x7a 0x7b 0x7c 0x7d 0x7e 0x7f 0x20 "; + + let actual_data = DataToText::data_to_text(data_to_display); + assert_eq!(actual_data, expected_data); +} + +#[test] +fn quickbrownfoxtest() { + let test_params = vec![ + "quickbrownfox" + ]; + // load the content from quickbrownfox.bin + + // format it for display + + // load the content from quickbrownfox.display + + // compare the formatted data to the loaded content. +} \ No newline at end of file diff --git a/tests/number_system_conversion.rs b/tests/number_system_conversion.rs index a9cb8db..4e860ed 100644 --- a/tests/number_system_conversion.rs +++ b/tests/number_system_conversion.rs @@ -7,38 +7,54 @@ fn byte_to_bool_and_bool_to_byte() { // **** NOTE THAT THIS VISUALLY IS BACKWARDS AS INDEX 0 IS FIRST AND LSB IS LAST **** let test_params: BTreeMap = BTreeMap::from([ (0xff, [true, true, true, true, true, true, true, true]), - (0x00, [false, false, false, false, false, false, false, false]), - (0xa0, [false, false, false, false, false, true, false, true]) + (0x00, [ + false, false, false, false, false, false, false, false, + ]), + (0xa0, [false, false, false, false, false, true, false, true]), ]); for (src, dst) in test_params { assert_eq!( dst, NumberSystemConversion::byte_to_bool(src), - "Unable to convert [{}] to [{:?}]", src, dst + "Unable to convert [{}] to [{:?}]", + src, + dst ); assert_eq!( src, NumberSystemConversion::bool_to_byte(dst), - "Unable to convert [{:?}] to [{}]", dst, src + "Unable to convert [{:?}] to [{}]", + dst, + src ); assert_eq!( src, - NumberSystemConversion::bool_to_byte( - NumberSystemConversion::byte_to_bool(src)) + NumberSystemConversion::bool_to_byte(NumberSystemConversion::byte_to_bool(src)) ); } } #[test] fn swap_endianness_u16() { - let test_params: Vec<(u16, u16)> = vec![(0xabcd, 0xcdab), (0x0000, 0x0000), (0xffff, 0xffff), (0x00ff, 0xff00)]; + let test_params: Vec<(u16, u16)> = vec![ + (0xabcd, 0xcdab), + (0x0000, 0x0000), + (0xffff, 0xffff), + (0x00ff, 0xff00), + ]; for (src, dst) in test_params { assert_eq!(src, NumberSystemConversion::swap_endian_u16(dst)); - assert_eq!(src, NumberSystemConversion::swap_endian_u16(NumberSystemConversion::swap_endian_u16(src))); + assert_eq!( + src, + NumberSystemConversion::swap_endian_u16(NumberSystemConversion::swap_endian_u16(src)) + ); assert_eq!(dst, NumberSystemConversion::swap_endian_u16(src)); - assert_eq!(dst, NumberSystemConversion::swap_endian_u16(NumberSystemConversion::swap_endian_u16(dst))); + assert_eq!( + dst, + NumberSystemConversion::swap_endian_u16(NumberSystemConversion::swap_endian_u16(dst)) + ); } } @@ -47,16 +63,20 @@ fn swap_endinness_u32() { let test_params: Vec<(u32, u32)> = vec![ (0xabcdef01, 0x01efcdab), (0x12345678, 0x78563412), - (0xbadbeef0, 0xf0eedbba) + (0xbadbeef0, 0xf0eedbba), ]; for (src, dst) in test_params { assert_eq!(src, NumberSystemConversion::swap_endian_u32(dst)); - assert_eq!(src, NumberSystemConversion::swap_endian_u32( - NumberSystemConversion::swap_endian_u32(src))); + assert_eq!( + src, + NumberSystemConversion::swap_endian_u32(NumberSystemConversion::swap_endian_u32(src)) + ); assert_eq!(dst, NumberSystemConversion::swap_endian_u32(src)); - assert_eq!(dst, NumberSystemConversion::swap_endian_u32( - NumberSystemConversion::swap_endian_u32(dst))); + assert_eq!( + dst, + NumberSystemConversion::swap_endian_u32(NumberSystemConversion::swap_endian_u32(dst)) + ); } } @@ -66,19 +86,15 @@ fn split_bytes_u16_join_bytes_u16() { (0x0000, (0x00, 0x00)), (0xabcd, (0xab, 0xcd)), (0xffff, (0xff, 0xff)), - (0xbeef, (0xbe, 0xef)) + (0xbeef, (0xbe, 0xef)), ]); for (joined, (high, low)) in test_params { - assert_eq!( - (high, low), - NumberSystemConversion::split_bytes_u16(joined)); + assert_eq!((high, low), NumberSystemConversion::split_bytes_u16(joined)); assert_eq!( joined, - NumberSystemConversion::join_bytes_u16_from_u8(high, low)); - assert_eq!( - joined, - NumberSystemConversion::join_bytes_u16(high as u16, low as u16)); + NumberSystemConversion::join_bytes_u16(high as u16, low as u16) + ); } } @@ -88,12 +104,47 @@ fn combine_u8_to_u16() { ((0xff, 0x00), 0x00ff), ((0x00, 0xff), 0xff00), ((0xbe, 0xef), 0xefbe), - ((0xef, 0xbe), 0xbeef) + ((0xef, 0xbe), 0xbeef), ]; - for ((low, high), base) in test_params { + for ((low, high), base) in test_params { + assert_eq!(base, NumberSystemConversion::combine_u8_to_u16(low, high)) + } +} + +#[test] +fn clear_high_bits() { + let test_params = vec![ + (0b1111_1111_0101_1010, 0b0000_0000_0101_1010), + (0b1111_1111_1111_1111, 0b0000_0000_1111_1111), + (0b0000_0000_1111_1111, 0b0000_0000_1111_1111), + (0b0101_1010_1001_0110, 0b0000_0000_1001_0110) + ]; + + for (src, dst) in test_params { + let result = NumberSystemConversion::clear_high_bits(src); + println!("Rolled {src:016b} to {result:016b} / {dst:016b}"); assert_eq!( - base, NumberSystemConversion::combine_u8_to_u16(low, high) - ) + NumberSystemConversion::clear_high_bits(src), + dst); + } + +} + +#[test] +fn clear_low_bits() { + let test_params = vec![ + (0b1111_1111_0101_1010, 0b1111_1111_0000_0000), + (0b1111_1111_1111_1111, 0b1111_1111_0000_0000), + (0b0000_0000_1111_1111, 0b0000_0000_0000_0000), + (0b0101_1010_1001_0110, 0b0101_1010_0000_0000) + ]; + + for (src, dst) in test_params { + let result = NumberSystemConversion::clear_low_bits(src); + println!("Rolled {src:08b} to {result:08b} / {dst:08b}"); + assert_eq!( + NumberSystemConversion::clear_low_bits(src), + dst); } } \ No newline at end of file diff --git a/tests/test_compression.rs b/tests/test_compression.rs index 1371396..d3d2912 100644 --- a/tests/test_compression.rs +++ b/tests/test_compression.rs @@ -1,4 +1,3 @@ -use std::fs::File; use std::io::{Read, Write}; use tempfile::NamedTempFile; use trevors_utilities::test_compression::TestCompression; @@ -12,12 +11,8 @@ fn smoke() { fn compression_round_trip() { let to_compress = "The quick brown fox jumps over the lazy dog."; - ; let decompressed_text = - TestCompression::decompress_to_string( - &TestCompression::compress_string( - to_compress - )).unwrap(); + TestCompression::decompress_to_string(&TestCompression::compress_string(to_compress)); assert_eq!(to_compress, decompressed_text); } @@ -27,21 +22,23 @@ fn file_compression_round_trip() { let string_to_compress = "The quick brown fox jumps over the lazy dog."; let compressed_string = TestCompression::compress_string(string_to_compress); let mut temp_target = NamedTempFile::new().unwrap(); - let mut temp_reader = temp_target.reopen(); + let temp_reader = temp_target.reopen(); // ...write the compressed version to a file... - temp_target.write_all(&compressed_string).expect("Unable to write compressed file for test"); + temp_target + .write_all(&compressed_string) + .expect("Unable to write compressed file for test"); // // ...decompress from file... let mut compressed_read = Vec::new(); - temp_reader.unwrap().read_to_end(&mut compressed_read).expect("Unable to read compressed data for test"); - let decompresed = TestCompression::decompress_to_string(&compressed_read).expect("Unable to decompress string for test"); + temp_reader + .unwrap() + .read_to_end(&mut compressed_read) + .expect("Unable to read compressed data for test"); + let decompresed = TestCompression::decompress_to_string(&compressed_read); // // ...verify its the same. - assert_eq!( - string_to_compress, - decompresed - ); + assert_eq!(string_to_compress, decompresed); } #[test] @@ -51,7 +48,9 @@ fn file_compression_reader() { // ...write it to the temp file... let mut temp_file = NamedTempFile::new().expect("Unable to get temp file for test"); - temp_file.write_all(&*TestCompression::compress_string(to_compress)).expect("Unable to write compressed data to temp file for test"); + temp_file + .write_all(&*TestCompression::compress_string(to_compress)) + .expect("Unable to write compressed data to temp file for test"); let temp2_path = temp_file.path(); let uncompressed_text = TestCompression::load_compressed_file_to_string(temp2_path); @@ -67,11 +66,11 @@ fn file_compression_writer() { // read back the compressed text let mut read_buffer = Vec::new(); - temp_file.read_to_end(&mut read_buffer).expect("Unable to read compressed data back."); - let decompressed_text = TestCompression::decompress_to_string(&read_buffer).expect("Unable to decompress text"); - assert_eq!( - decompressed_text, - to_compress); + temp_file + .read_to_end(&mut read_buffer) + .expect("Unable to read compressed data back."); + let decompressed_text = TestCompression::decompress_to_string(&read_buffer); + assert_eq!(decompressed_text, to_compress); } #[test] @@ -80,18 +79,22 @@ fn compress_file() { let test_text = "The quick brown fox jumps over the lazy dog."; let mut original_file = NamedTempFile::new().unwrap(); let compressed_file = NamedTempFile::new().unwrap(); - original_file.write(test_text.as_bytes()).expect("Unable to write original file"); + original_file + .write(test_text.as_bytes()) + .expect("Unable to write original file"); // run the method being tested - TestCompression::compress_file(original_file.path(), compressed_file.path()).expect("Unable to compress temp file."); + TestCompression::compress_file(original_file.path(), compressed_file.path()) + .expect("Unable to compress temp file."); // verify the data in the new file matches what we expect - let compressed_text = TestCompression::compress_string(test_text); let mut reader_file = compressed_file.reopen().unwrap(); let mut result_text = Vec::new(); - reader_file.read_to_end(&mut result_text).expect("Unable to read compressed data back to verify"); - let decompressed_text = TestCompression::decompress_to_string(&result_text).unwrap(); + reader_file + .read_to_end(&mut result_text) + .expect("Unable to read compressed data back to verify"); + let decompressed_text = TestCompression::decompress_to_string(&result_text); assert_eq!(decompressed_text, test_text); } @@ -105,15 +108,32 @@ fn compress_string_to_file() { TestCompression::compress_string_to_file(to_compress, target_file.path()); let mut file_contents = Vec::new(); - duplicate.read_to_end(&mut file_contents).expect("Unable to read compressed file."); + duplicate + .read_to_end(&mut file_contents) + .expect("Unable to read compressed file."); assert_eq!(file_contents, compressed_text); } #[test] fn decompress_file_to_string() { let to_compress = "The quick brown fox jumps over the lazy dog."; - let mut temp = NamedTempFile::new().unwrap(); + let temp = NamedTempFile::new().unwrap(); TestCompression::compress_string_to_file(to_compress, temp.path()); let result = TestCompression::decompress_file_to_string(temp.path()); assert_eq!(to_compress, result); -} \ No newline at end of file +} + +#[test] +fn compress_file_to_array() { + let to_compress = "The quick brown fox jumps over the lazy dog."; + let mut temp_file = NamedTempFile::new().expect("Unable to get temp file for test"); + temp_file.write_all(to_compress.as_bytes()).unwrap(); + + let result = TestCompression::compress_file_to_array(temp_file.path()); + + let decompressed_result = TestCompression::decompress_to_string(&result); + assert_eq!(to_compress, decompressed_result); +} + +#[test] +fn decompress_to_array() {}