From 9e5b0aafbd50f0f5f1c79569a229d29ba9938369 Mon Sep 17 00:00:00 2001 From: Joey Hines Date: Sat, 9 Oct 2021 08:42:17 -0600 Subject: [PATCH] Int formatting working + Using num-bigint to support arbitrarily sized ints + Added formatting tests --- Cargo.lock | 37 ++++++++++++++++ Cargo.toml | 3 +- src/byte_stream/mod.rs | 2 +- src/formatter/format.rs | 98 ++++++++++++++++++++++++++++++++++++----- 4 files changed, 127 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 19bbcaf..478aa0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,6 +22,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + [[package]] name = "bitflags" version = "1.3.2" @@ -54,6 +60,7 @@ name = "formaty" version = "0.1.0" dependencies = [ "byteorder", + "num-bigint", "serde", "structopt", "toml", @@ -89,6 +96,36 @@ version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" +[[package]] +name = "num-bigint" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74e768dff5fb39a41b3bcd30bb25cf989706c90d028d1ad71971987aa309d535" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + [[package]] name = "proc-macro-error" version = "1.0.4" diff --git a/Cargo.toml b/Cargo.toml index 261e8a7..2b8de72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,5 @@ edition = "2018" structopt = "0.3.23" serde = { version = "1.0", features = ["derive"] } toml = "0.5.8" -byteorder = "1.4.3" \ No newline at end of file +byteorder = "1.4.3" +num-bigint = "0.4" \ No newline at end of file diff --git a/src/byte_stream/mod.rs b/src/byte_stream/mod.rs index 7694dfa..b6423f2 100644 --- a/src/byte_stream/mod.rs +++ b/src/byte_stream/mod.rs @@ -3,7 +3,7 @@ pub enum ByteStreamError { OutOfRange, } -const fn bit_mask(mask: u8) -> u8 { +pub const fn bit_mask(mask: u8) -> u8 { match mask { 0 => 0x00, 1 => 0x01, diff --git a/src/formatter/format.rs b/src/formatter/format.rs index d90227a..53dc5d7 100644 --- a/src/formatter/format.rs +++ b/src/formatter/format.rs @@ -1,6 +1,7 @@ -use crate::byte_stream::ByteStream; +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")] @@ -24,18 +25,38 @@ 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(); + + 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 } => { - let bytes = byte_stream.get_bytes(bit_ndx, bit_width).unwrap(); - let mut string = String::with_capacity(bytes.len() * 2); - - for byte in bytes.iter().rev() { - string.push_str(&format!("{:x}", byte)) - } - - (string, bit_width) - } + 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), } } @@ -64,3 +85,58 @@ impl Format { 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") + } +}