Added floating point formatting

+ Supports single and double precession formatting
+ Added Error handling from the formatting functions
pull/1/head
Joey Hines 2021-10-09 09:30:02 -06:00
parent 9e5b0aafbd
commit 652682cb19
No known key found for this signature in database
GPG Key ID: 80F567B5C968F91B
3 changed files with 107 additions and 22 deletions

View File

@ -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,

View File

@ -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<ByteStreamError> 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::<BigEndian>().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::<BigEndian>().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<String, FormatError> {
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())
}
}

View File

@ -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)
}
}