formaty/src/formatter/format.rs

143 lines
4.1 KiB
Rust

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<Field>,
}
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")
}
}