215 lines
6.6 KiB
Rust
215 lines
6.6 KiB
Rust
use crate::byte_stream::{ByteStream, bit_mask, ByteStreamError};
|
|
use serde::Deserialize;
|
|
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")]
|
|
pub enum FieldType {
|
|
/// Unsigned Int
|
|
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
|
|
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) -> 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;
|
|
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);
|
|
|
|
Ok((big_int.to_string(), bit_width))
|
|
}
|
|
else {
|
|
Err(ByteStreamError::OutOfRange.into())
|
|
}
|
|
}
|
|
|
|
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);
|
|
Ok((big_int.to_string(), bit_width))
|
|
}
|
|
|
|
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),
|
|
FieldType::Float => Self::format_float(byte_stream, bit_ndx),
|
|
FieldType::Double => Self::format_double(byte_stream, bit_ndx),
|
|
_ => Err(FormatError::NotSupported),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Clone)]
|
|
pub struct Format {
|
|
/// Format Name
|
|
pub name: String,
|
|
/// 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 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!(format_str, "{}: {}", field.name, data_str).unwrap();
|
|
}
|
|
|
|
Ok(format_str)
|
|
}
|
|
}
|
|
|
|
#[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).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());
|
|
}
|
|
}
|
|
|
|
#[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).unwrap();
|
|
|
|
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).unwrap();
|
|
|
|
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).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())
|
|
}
|
|
}
|