formaty/src/formatter/format.rs

657 lines
19 KiB
Rust

use std::fmt::{Display, Formatter, Write};
use std::io::Cursor;
use std::string::FromUtf8Error;
use byteorder::{BigEndian, ByteOrder, LittleEndian, ReadBytesExt};
use num_bigint::{BigInt, BigUint};
use serde::Deserialize;
use crate::byte_stream::{bit_mask, ByteStream, ByteStreamError};
use crate::formatter::printers::PrintType;
pub trait ByteOrderOperations: ByteOrder {
fn last_byte(buf: &mut Vec<u8>) -> Option<&mut u8>;
fn big_int(buf: &[u8], trim: usize) -> BigInt;
fn big_uint(buf: &[u8], trim: usize) -> BigUint;
fn big_u_int_to_bytes(big_int: BigUint) -> Vec<u8>;
fn big_int_to_bytes(big_int: BigInt) -> Vec<u8>;
fn pad_bytes(buf: &mut Vec<u8>, size: usize);
fn flip() -> bool;
}
impl ByteOrderOperations for BigEndian {
fn last_byte(buf: &mut Vec<u8>) -> Option<&mut u8> {
buf.first_mut()
}
fn big_int(buf: &[u8], trim: usize) -> BigInt {
let mut buf = buf.to_vec();
let byte = buf.first_mut().unwrap();
*byte = (*byte << trim) >> trim;
BigInt::from_signed_bytes_be(&buf)
}
fn big_uint(buf: &[u8], trim: usize) -> BigUint {
let mut buf = buf.to_vec();
let byte = buf.first_mut().unwrap();
*byte = (*byte << trim) >> trim;
BigUint::from_bytes_be(&buf)
}
fn big_u_int_to_bytes(big_int: BigUint) -> Vec<u8> {
big_int.to_bytes_be()
}
fn big_int_to_bytes(big_int: BigInt) -> Vec<u8> {
big_int.to_signed_bytes_be()
}
fn pad_bytes(buf: &mut Vec<u8>, size: usize) {
if size <= buf.len() {
return;
}
let pad_to = size - buf.len();
buf.splice(0..0, vec![0_u8; pad_to].iter().cloned());
}
fn flip() -> bool {
true
}
}
impl ByteOrderOperations for LittleEndian {
fn last_byte(buf: &mut Vec<u8>) -> Option<&mut u8> {
buf.last_mut()
}
fn big_int(buf: &[u8], trim: usize) -> BigInt {
BigInt::from_signed_bytes_le(buf) >> trim
}
fn big_uint(buf: &[u8], trim: usize) -> BigUint {
BigUint::from_bytes_le(buf) >> trim
}
fn big_u_int_to_bytes(big_int: BigUint) -> Vec<u8> {
big_int.to_bytes_le()
}
fn big_int_to_bytes(big_int: BigInt) -> Vec<u8> {
big_int.to_signed_bytes_le()
}
fn pad_bytes(buf: &mut Vec<u8>, size: usize) {
if size <= buf.len() {
return;
}
let pad_to = size - buf.len();
let mut pad = vec![0_u8; pad_to];
buf.append(&mut pad);
}
fn flip() -> bool {
false
}
}
#[derive(Debug, Clone)]
pub enum FormatError {
ByteSteamError(ByteStreamError),
#[allow(dead_code)]
NotSupported,
StringParseError(FromUtf8Error),
}
impl From<ByteStreamError> for FormatError {
fn from(e: ByteStreamError) -> Self {
FormatError::ByteSteamError(e)
}
}
impl From<FromUtf8Error> for FormatError {
fn from(e: FromUtf8Error) -> Self {
FormatError::StringParseError(e)
}
}
impl Display for FormatError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
FormatError::ByteSteamError(e) => writeln!(f, "Byte stream error: {}", e),
FormatError::NotSupported => write!(f, "Field type not supported"),
FormatError::StringParseError(e) => write!(f, "String parse error: {}", e),
}
}
}
#[derive(Debug, Deserialize, Clone)]
#[serde(tag = "type")]
pub enum FieldType {
/// Unsigned Int
UInt {
bit_width: usize,
endianness: Endianness,
},
/// Unsigned Int
Int {
bit_width: usize,
endianness: Endianness,
},
/// Single Precession Float
Float { endianness: Endianness },
/// Double Precession Float
Double { endianness: Endianness },
/// Null Terminated String Field
String {
max_len: usize,
endianness: Endianness,
},
/// Fixed Byte Length Field
Bytes {
max_len: usize,
endianness: Endianness,
},
}
#[derive(Debug, Deserialize, Clone, PartialOrd, PartialEq, Copy)]
pub enum Endianness {
LittleEndian,
BigEndian,
}
#[derive(Debug, Deserialize, Clone)]
pub struct Field {
/// Field Name
pub name: String,
/// Field Type
pub field_type: FieldType,
/// How to display the field
#[serde(default = "PrintType::default")]
pub print_type: PrintType,
/// Flip Bit Order
pub bit_flip: Option<bool>,
//#[serde(default = "BitOffset::default")]
//pub bit_offset: BitOffset
}
impl Field {
fn format_int<T: ByteOrderOperations>(
&self,
byte_stream: &ByteStream,
bit_ndx: usize,
bit_width: usize,
) -> Result<(String, usize), FormatError> {
let mut bytes = byte_stream.get_bytes::<T>(bit_ndx, bit_width)?;
if let Some(last_byte) = T::last_byte(&mut bytes) {
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 = T::big_int(&bytes, 0);
Ok((self.print_type.print_big_int::<T>(big_int), bit_width))
} else {
Err(ByteStreamError::OutOfRange.into())
}
}
fn format_uint<T: ByteOrderOperations>(
&self,
byte_stream: &ByteStream,
bit_ndx: usize,
bit_width: usize,
) -> Result<(String, usize), FormatError> {
let bytes = byte_stream.get_bytes::<T>(bit_ndx, bit_width)?;
let big_int = T::big_uint(&bytes, 0);
Ok((self.print_type.print_big_u_int::<T>(big_int), bit_width))
}
fn format_float<T: ByteOrderOperations>(
&self,
byte_stream: &ByteStream,
bit_ndx: usize,
) -> Result<(String, usize), FormatError> {
let bytes = byte_stream.get_bytes::<T>(bit_ndx, 32)?;
let mut cursor = Cursor::new(bytes);
let float = cursor.read_f32::<T>().unwrap();
Ok((
self.print_type.print_float::<T>(float),
std::mem::size_of::<f32>() * 8,
))
}
fn format_double<T: ByteOrderOperations>(
&self,
byte_stream: &ByteStream,
bit_ndx: usize,
) -> Result<(String, usize), FormatError> {
let bytes = byte_stream.get_bytes::<T>(bit_ndx, 64)?;
let mut cursor = Cursor::new(bytes);
let double = cursor.read_f64::<T>().unwrap();
Ok((
self.print_type.print_double::<T>(double),
std::mem::size_of::<f64>() * 8,
))
}
fn format_string<T: ByteOrderOperations>(
&self,
byte_stream: &ByteStream,
bit_ndx: usize,
max_byte_len: usize,
) -> Result<(String, usize), FormatError> {
let mut string_bytes = Vec::new();
let mut bit_count = 0;
for _ in 0..max_byte_len {
let byte = byte_stream.get_bytes::<T>(bit_ndx + bit_count, 8)?[0];
bit_count += 8;
if byte == 0 {
break;
}
string_bytes.push(byte);
}
let s = String::from_utf8(string_bytes)?;
Ok((self.print_type.print_string(&s), bit_count))
}
fn format_bytes<T: ByteOrderOperations>(
&self,
byte_stream: &ByteStream,
bit_ndx: usize,
max_byte_len: usize,
) -> Result<(String, usize), FormatError> {
let data_remaining = byte_stream.len() - bit_ndx / 8;
let width = if max_byte_len < data_remaining {
max_byte_len * 8
} else {
data_remaining * 8
};
let data = byte_stream.get_bytes::<T>(bit_ndx, width)?;
Ok((self.print_type.print_bytes(&data), width))
}
fn format_data(
&self,
byte_stream: &mut ByteStream,
bit_ndx: usize,
) -> Result<(String, usize), FormatError> {
let global_bit_flip = byte_stream.get_reverse_bits();
if let Some(bit_flip) = self.bit_flip {
byte_stream.set_reverse_bits(bit_flip);
}
let fmt = match self.field_type {
FieldType::UInt {
bit_width,
endianness,
} => match endianness {
Endianness::LittleEndian => {
self.format_uint::<LittleEndian>(byte_stream, bit_ndx, bit_width)
}
Endianness::BigEndian => {
self.format_uint::<BigEndian>(byte_stream, bit_ndx, bit_width)
}
},
FieldType::Int {
bit_width,
endianness,
} => match endianness {
Endianness::LittleEndian => {
self.format_int::<LittleEndian>(byte_stream, bit_ndx, bit_width)
}
Endianness::BigEndian => {
self.format_int::<BigEndian>(byte_stream, bit_ndx, bit_width)
}
},
FieldType::Float { endianness } => match endianness {
Endianness::LittleEndian => self.format_float::<LittleEndian>(byte_stream, bit_ndx),
Endianness::BigEndian => self.format_float::<BigEndian>(byte_stream, bit_ndx),
},
FieldType::Double { endianness } => match endianness {
Endianness::LittleEndian => {
self.format_double::<LittleEndian>(byte_stream, bit_ndx)
}
Endianness::BigEndian => self.format_double::<BigEndian>(byte_stream, bit_ndx),
},
FieldType::String {
max_len,
endianness,
} => match endianness {
Endianness::LittleEndian => {
self.format_string::<LittleEndian>(byte_stream, bit_ndx, max_len)
}
Endianness::BigEndian => {
self.format_string::<LittleEndian>(byte_stream, bit_ndx, max_len)
}
},
FieldType::Bytes {
max_len,
endianness,
} => match endianness {
Endianness::LittleEndian => {
self.format_bytes::<LittleEndian>(byte_stream, bit_ndx, max_len)
}
Endianness::BigEndian => {
self.format_bytes::<LittleEndian>(byte_stream, bit_ndx, max_len)
}
},
}?;
byte_stream.set_reverse_bits(global_bit_flip);
Ok(fmt)
}
}
#[derive(Debug, Deserialize, Clone)]
pub struct Format {
/// Format Name
pub name: String,
/// Flip bits
pub bit_flip: bool,
/// Elements of the format
pub fields: Vec<Field>,
}
impl Format {
pub fn format_data(&self, data: &[u8]) -> Result<String, FormatError> {
let mut format_str = String::new();
let mut byte_stream = ByteStream::from(data);
let mut bit_ndx: usize = 0;
byte_stream.set_reverse_bits(self.bit_flip);
for field in &self.fields {
match field.format_data(&mut byte_stream, bit_ndx) {
Ok((data_str, bit_width)) => {
bit_ndx += bit_width;
writeln!(format_str, "{}: {}", field.name, data_str).unwrap();
}
Err(e) => {
println!(
"Error formatting field (bit_ndx = {}): \"{}\": {}",
bit_ndx, field.name, e
);
return Err(e);
}
}
}
Ok(format_str)
}
}
#[cfg(test)]
mod tests {
use crate::byte_stream::ByteStream;
use crate::formatter::format::{Endianness, Field, FieldType};
#[test]
fn test_format_int_4_bits() {
let field = Field {
field_type: FieldType::Int {
bit_width: 4,
endianness: Endianness::LittleEndian,
},
name: "test".to_string(),
bit_flip: None,
print_type: Default::default(),
};
for i in 0i8..7i8 {
let mut byte_vec = Vec::new();
byte_vec.push(i as u8);
byte_vec.push((-i) as u8);
let mut byte_stream = ByteStream::from(byte_vec);
let (pos_output, width1) = field.format_data(&mut byte_stream, 4).unwrap();
let (neg_output, width2) = field.format_data(&mut byte_stream, 12).unwrap();
assert_eq!(width1, 4);
assert_eq!(width2, 4);
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,
endianness: Endianness::LittleEndian,
},
name: "test".to_string(),
bit_flip: Some(true),
print_type: Default::default(),
};
let mut byte_stream = ByteStream::from(vec![0x1B]);
let (output, width) = field.format_data(&mut byte_stream, 0).unwrap();
assert_eq!(width, 5);
assert_eq!(output, "-5")
}
#[test]
fn test_format_int_16_bits() {
let field = Field {
field_type: FieldType::Int {
bit_width: 16,
endianness: Endianness::LittleEndian,
},
name: "test".to_string(),
bit_flip: None,
print_type: Default::default(),
};
let mut byte_stream = ByteStream::from(vec![0xFC, 0xA5]);
let (output, width) = field.format_data(&mut byte_stream, 0).unwrap();
assert_eq!(width, 16);
assert_eq!(output, "-23044")
}
#[test]
fn test_format_int_16_bits_not_aligned() {
let field = Field {
field_type: FieldType::Int {
bit_width: 16,
endianness: Endianness::LittleEndian,
},
name: "test".to_string(),
bit_flip: None,
print_type: Default::default(),
};
let mut byte_stream = ByteStream::from(vec![0x0C, 0xF5, 0xA0]);
let (output, width) = field.format_data(&mut byte_stream, 4).unwrap();
assert_eq!(width, 16);
assert_eq!(output, "-23044")
}
#[test]
fn test_format_float() {
let field = Field {
field_type: FieldType::Float {
endianness: Endianness::LittleEndian,
},
name: "test".to_string(),
bit_flip: None,
print_type: Default::default(),
};
let mut byte_stream = ByteStream::from(b"\x52\x58\xd2\xc3".to_vec());
let (output, width) = field.format_data(&mut byte_stream, 0).unwrap();
assert_eq!(width, 32);
assert_eq!(output, "-420.69")
}
#[test]
fn test_format_double() {
let field = Field {
field_type: FieldType::Double {
endianness: Endianness::LittleEndian,
},
name: "test".to_string(),
bit_flip: None,
print_type: Default::default(),
};
let mut byte_stream = ByteStream::from(b"\xD7\xA3\x70\x3D\x0A\x4B\x7A\xC0".to_vec());
let (output, width) = field.format_data(&mut byte_stream, 0).unwrap();
assert_eq!(width, 64);
assert_eq!(output, "-420.69")
}
#[test]
fn test_format_float_err() {
let field = Field {
field_type: FieldType::Double {
endianness: Endianness::LittleEndian,
},
name: "test".to_string(),
bit_flip: None,
print_type: Default::default(),
};
let mut byte_stream = ByteStream::from(b"\x3D\x70\xA3\xD7".to_vec());
assert!(field.format_data(&mut byte_stream, 0).is_err())
}
#[test]
fn test_format_string() {
let test_string = "Hello World!";
let field = Field {
field_type: FieldType::String {
max_len: 16,
endianness: Endianness::LittleEndian,
},
name: "test".to_string(),
bit_flip: None,
print_type: Default::default(),
};
let mut bytes = test_string.as_bytes().to_vec();
bytes.push('\0' as u8);
let mut byte_stream = ByteStream::from(bytes);
let (output, width) = field.format_data(&mut byte_stream, 0).unwrap();
assert_eq!(width, (test_string.len() + 1) * 8); //extra byte to account for the '\0' char
assert_eq!(output, test_string)
}
#[test]
fn test_format_bytes() {
let field = Field {
field_type: FieldType::Bytes {
max_len: 2,
endianness: Endianness::LittleEndian,
},
name: "test".to_string(),
bit_flip: None,
print_type: Default::default(),
};
let mut byte_stream = ByteStream::from(vec![0xDE, 0xAD, 0xBE, 0xEF]);
let (output, width) = field.format_data(&mut byte_stream, 0).unwrap();
assert_eq!(width, 16);
assert_eq!(output, "[222, 173]")
}
#[test]
fn test_format_bytes_max_len_bigger_than_data() {
let field = Field {
field_type: FieldType::Bytes {
max_len: 64,
endianness: Endianness::LittleEndian,
},
name: "test".to_string(),
bit_flip: None,
print_type: Default::default(),
};
let mut byte_stream = ByteStream::from(vec![0xDE, 0xAD]);
let (output, width) = field.format_data(&mut byte_stream, 0).unwrap();
assert_eq!(width, 16);
assert_eq!(output, "[222, 173]")
}
#[test]
fn test_parse_str_big_endian() {
let field = Field {
field_type: FieldType::String {
max_len: 64,
endianness: Endianness::BigEndian,
},
name: "test".to_string(),
bit_flip: None,
print_type: Default::default(),
};
let src_str = b"Test\x00abcdefg";
let mut byte_stream = ByteStream::from(src_str.to_vec());
let (output, width) = field.format_data(&mut byte_stream, 0).unwrap();
assert_eq!(width, 40);
assert_eq!(output, "Test")
}
#[test]
fn test_parse_str_little_endian() {
let field = Field {
field_type: FieldType::String {
max_len: 64,
endianness: Endianness::LittleEndian,
},
name: "test".to_string(),
bit_flip: None,
print_type: Default::default(),
};
let src_str = b"Test\x00abcdefg";
let mut byte_stream = ByteStream::from(src_str.to_vec());
let (output, width) = field.format_data(&mut byte_stream, 0).unwrap();
assert_eq!(width, 40);
assert_eq!(output, "Test")
}
}