From 652682cb19b034f03ea42e7e6d319ceef633dc26 Mon Sep 17 00:00:00 2001 From: Joey Hines Date: Sat, 9 Oct 2021 09:30:02 -0600 Subject: [PATCH] Added floating point formatting + Supports single and double precession formatting + Added Error handling from the formatting functions --- src/byte_stream/mod.rs | 10 ++++ src/formatter/format.rs | 114 ++++++++++++++++++++++++++++++++-------- src/main.rs | 5 +- 3 files changed, 107 insertions(+), 22 deletions(-) diff --git a/src/byte_stream/mod.rs b/src/byte_stream/mod.rs index b6423f2..c84d7bf 100644 --- a/src/byte_stream/mod.rs +++ b/src/byte_stream/mod.rs @@ -1,8 +1,18 @@ +use std::fmt::{Display, Formatter}; + #[derive(Clone, Debug)] pub enum ByteStreamError { OutOfRange, } +impl Display for ByteStreamError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + ByteStreamError::OutOfRange => write!(f, "Request values out of range") + } + } +} + pub const fn bit_mask(mask: u8) -> u8 { match mask { 0 => 0x00, diff --git a/src/formatter/format.rs b/src/formatter/format.rs index 53dc5d7..0f4fdfb 100644 --- a/src/formatter/format.rs +++ b/src/formatter/format.rs @@ -1,7 +1,30 @@ -use crate::byte_stream::{ByteStream, bit_mask}; +use crate::byte_stream::{ByteStream, bit_mask, ByteStreamError}; use serde::Deserialize; -use std::fmt::Write; +use std::fmt::{Write, Display, Formatter}; use num_bigint::{BigUint, BigInt}; +use std::io::Cursor; +use byteorder::{BigEndian, ReadBytesExt}; + +#[derive(Debug, Clone)] +pub enum FormatError { + ByteSteamError(ByteStreamError), + NotSupported +} + +impl From for FormatError { + fn from(e: ByteStreamError) -> Self { + FormatError::ByteSteamError(e) + } +} + +impl Display for FormatError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + FormatError::ByteSteamError(e) => writeln!(f, "Byte steam error: {}", e), + FormatError::NotSupported => write!(f, "Field type not supported") + } + } +} #[derive(Debug, Deserialize, Clone)] #[serde(tag = "type")] @@ -10,6 +33,10 @@ pub enum FieldType { UInt { bit_width: usize }, /// Unsigned Int Int { bit_width: usize }, + /// Single Precession Float + Float, + /// Double Precession Float + Double, /// Null Terminated String Field String { max_len: usize }, /// Fixed Byte Length Field @@ -25,8 +52,8 @@ pub struct Field { } impl Field { - fn format_int(byte_stream: &ByteStream, bit_ndx: usize, bit_width: usize) -> (String, usize) { - let mut bytes = byte_stream.get_bytes(bit_ndx, bit_width).unwrap(); + fn format_int(byte_stream: &ByteStream, bit_ndx: usize, bit_width: usize) -> Result<(String, usize), FormatError> { + let mut bytes = byte_stream.get_bytes(bit_ndx, bit_width)?; if let Some(last_byte) = bytes.last_mut() { let last_bit = ((bit_width - 1) % 8) as u8; @@ -39,25 +66,41 @@ impl Field { let big_int = BigInt::from_signed_bytes_be(&bytes); - (big_int.to_string(), bit_width) + Ok((big_int.to_string(), bit_width)) } else { - ("".to_string(), bit_width) + Err(ByteStreamError::OutOfRange.into()) } } - fn format_uint(byte_stream: &ByteStream, bit_ndx: usize, bit_width: usize) -> (String, usize) { - let bytes = byte_stream.get_bytes(bit_ndx, bit_width).unwrap(); + fn format_uint(byte_stream: &ByteStream, bit_ndx: usize, bit_width: usize) -> Result<(String, usize), FormatError> { + let bytes = byte_stream.get_bytes(bit_ndx, bit_width)?; let big_int = BigUint::from_bytes_be(&bytes); - (big_int.to_string(), bit_width) + Ok((big_int.to_string(), bit_width)) } - fn format_data(&self, byte_stream: &ByteStream, bit_ndx: usize) -> (String, usize) { + fn format_float(byte_stream: &ByteStream, bit_ndx: usize) -> Result<(String, usize), FormatError> { + let bytes = byte_stream.get_bytes(bit_ndx, 32)?; + let mut cursor = Cursor::new(bytes); + + Ok((cursor.read_f32::().unwrap().to_string(), 4)) + } + + fn format_double(byte_stream: &ByteStream, bit_ndx: usize) -> Result<(String, usize), FormatError> { + let bytes = byte_stream.get_bytes(bit_ndx, 64)?; + let mut cursor = Cursor::new(bytes); + + Ok((cursor.read_f64::().unwrap().to_string(), 4)) + } + + fn format_data(&self, byte_stream: &ByteStream, bit_ndx: usize) -> Result<(String, usize), FormatError> { match self.field_type { FieldType::UInt { bit_width } => Self::format_uint(byte_stream, bit_ndx, bit_width), FieldType::Int { bit_width } => Self::format_int(byte_stream, bit_ndx, bit_width), - _ => ("".to_string(), 0), + FieldType::Float => Self::format_float(byte_stream, bit_ndx), + FieldType::Double => Self::format_double(byte_stream, bit_ndx), + _ => Err(FormatError::NotSupported), } } } @@ -71,18 +114,18 @@ pub struct Format { } impl Format { - pub fn format_data(&self, data: &[u8]) -> String { - let mut s = String::new(); + pub fn format_data(&self, data: &[u8]) -> Result { + let mut format_str = String::new(); let byte_stream = ByteStream::from(data); let mut bit_ndx: usize = 0; for field in &self.fields { - let (data_str, bit_width) = field.format_data(&byte_stream, bit_ndx); + let (data_str, bit_width) = field.format_data(&byte_stream, bit_ndx)?; bit_ndx += bit_width; - writeln!(s, "{}: {}", field.name, data_str).unwrap(); + writeln!(format_str, "{}: {}", field.name, data_str).unwrap(); } - s + Ok(format_str) } } @@ -101,8 +144,8 @@ mod tests { byte_vec.push((-i) as u8); let byte_steam = ByteStream::from(byte_vec); - let (pos_output, _) = field.format_data(&byte_steam, 0); - let (neg_output, _) = field.format_data(&byte_steam, 8); + let (pos_output, _) = field.format_data(&byte_steam, 0).unwrap(); + let (neg_output, _) = field.format_data(&byte_steam, 8).unwrap(); assert_eq!(pos_output, i.to_string()); assert_eq!(neg_output, (-i).to_string()); @@ -113,7 +156,7 @@ mod tests { fn test_format_int_5_bits() { let field = Field {field_type: FieldType::Int {bit_width: 5,}, name: "test".to_string()}; let byte_steam = ByteStream::from(vec![0x1B]); - let (output, _) = field.format_data(&byte_steam, 0); + let (output, _) = field.format_data(&byte_steam, 0).unwrap(); assert_eq!(output, "-5") } @@ -124,7 +167,7 @@ mod tests { let byte_steam = ByteStream::from(vec![0xA5, 0xFC]); - let (output, _) = field.format_data(&byte_steam, 0); + let (output, _) = field.format_data(&byte_steam, 0).unwrap(); assert_eq!(output, "-23044") } @@ -135,8 +178,37 @@ mod tests { let byte_steam = ByteStream::from(vec![0x50, 0xCA, 0x0F]); - let (output, _) = field.format_data(&byte_steam, 4); + let (output, _) = field.format_data(&byte_steam, 4).unwrap(); assert_eq!(output, "-23044") } + + #[test] + fn test_format_float() { + let field = Field {field_type: FieldType::Float, name: "test".to_string()}; + + let byte_steam = ByteStream::from(b"\xc3\xd2\x58\x52".to_vec()); + let (output, _) = field.format_data(&byte_steam, 0).unwrap(); + + assert_eq!(output, "-420.69") + } + + #[test] + fn test_format_double() { + let field = Field {field_type: FieldType::Double, name: "test".to_string()}; + + let byte_steam = ByteStream::from(b"\xC0\x7A\x4B\x0A\x3D\x70\xA3\xD7".to_vec()); + let (output, _) = field.format_data(&byte_steam, 0).unwrap(); + + assert_eq!(output, "-420.69") + } + + #[test] + fn test_format_float_err() { + let field = Field {field_type: FieldType::Double, name: "test".to_string()}; + + let byte_steam = ByteStream::from(b"\x3D\x70\xA3\xD7".to_vec()); + + assert!(field.format_data(&byte_steam, 0).is_err()) + } } diff --git a/src/main.rs b/src/main.rs index f27d029..b079cb6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,5 +33,8 @@ fn main() { let data = parse_bytes_from_input_arg(args.data).unwrap(); - println!("{}", format.format_data(&data)); + match format.format_data(&data) { + Ok(data_str) => println!("{}", data_str), + Err(e) => println!("Unable to format data: {}", e) + } }