use crate::byte_stream::{ByteStream, bit_mask}; use serde::Deserialize; use std::fmt::Write; use num_bigint::{BigUint, BigInt}; #[derive(Debug, Deserialize, Clone)] #[serde(tag = "type")] pub enum FieldType { /// Unsigned Int UInt { bit_width: usize }, /// Unsigned Int Int { bit_width: usize }, /// Null Terminated String Field String { max_len: usize }, /// Fixed Byte Length Field Bytes { max_len: usize }, } #[derive(Debug, Deserialize, Clone)] pub struct Field { /// Field Name pub name: String, /// Field Type pub field_type: FieldType, } 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(); if let Some(last_byte) = bytes.last_mut() { let last_bit = ((bit_width - 1) % 8) as u8; let sign_bit = (*last_byte >> last_bit) & 0x1 == 1; if sign_bit { // Sign extend *last_byte |= !bit_mask(last_bit + 1) } let big_int = BigInt::from_signed_bytes_be(&bytes); (big_int.to_string(), bit_width) } else { ("".to_string(), bit_width) } } 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(); let big_int = BigUint::from_bytes_be(&bytes); (big_int.to_string(), bit_width) } fn format_data(&self, byte_stream: &ByteStream, bit_ndx: usize) -> (String, usize) { 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), } } } #[derive(Debug, Deserialize, Clone)] pub struct Format { /// Format Name pub name: String, /// Elements of the format pub fields: Vec, } impl Format { pub fn format_data(&self, data: &[u8]) -> String { let mut s = 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); bit_ndx += bit_width; writeln!(s, "{}: {}", field.name, data_str).unwrap(); } s } } #[cfg(test)] mod tests { use crate::formatter::format::{FieldType, Field}; use crate::byte_stream::ByteStream; #[test] fn test_format_int_4_bits() { let field = Field {field_type: FieldType::Int {bit_width: 4,}, name: "test".to_string()}; for i in 0i8..7i8 { let mut byte_vec = Vec::new(); byte_vec.push(i as u8); 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); assert_eq!(pos_output, i.to_string()); assert_eq!(neg_output, (-i).to_string()); } } #[test] 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); assert_eq!(output, "-5") } #[test] fn test_format_int_16_bits() { let field = Field {field_type: FieldType::Int {bit_width: 16,}, name: "test".to_string()}; let byte_steam = ByteStream::from(vec![0xA5, 0xFC]); let (output, _) = field.format_data(&byte_steam, 0); assert_eq!(output, "-23044") } #[test] fn test_format_int_16_bits_not_aligned() { let field = Field {field_type: FieldType::Int {bit_width: 16,}, name: "test".to_string()}; let byte_steam = ByteStream::from(vec![0x50, 0xCA, 0x0F]); let (output, _) = field.format_data(&byte_steam, 4); assert_eq!(output, "-23044") } }