Initial Commit

+ Basic implementation working with just unsigned ints
+ Can fetch data from arbitrary bit positions in a byte string
+ TOML config implementation for describing data
+ Simple CLI
This commit is contained in:
2021-09-11 12:21:34 -06:00
commit b85ba31f91
8 changed files with 591 additions and 0 deletions
+136
View File
@@ -0,0 +1,136 @@
#[derive(Clone, Debug)]
pub enum ByteStreamError {
OutOfRange,
}
const fn bit_mask(mask: u8) -> u8 {
match mask {
0 => 0x00,
1 => 0x01,
2 => 0x03,
3 => 0x07,
4 => 0x0f,
5 => 0x1f,
6 => 0x3f,
7 => 0x7f,
_ => 0xff,
}
}
#[derive(Default, Clone, Debug)]
pub struct ByteStream {
data: Vec<u8>,
}
impl ByteStream {
pub fn get_bytes(&self, bit_ndx: usize, bit_count: usize) -> Result<Vec<u8>, ByteStreamError> {
let byte_ndx = bit_ndx / 8;
let bits_before = (bit_ndx % 8) as u8;
let bits_in_last_byte = ((bit_ndx + bit_count - bits_before as usize) % 8) as u8;
let byte_count = if bit_count >= 8 { bit_count / 8 } else { 1 };
let bytes_needed = if bits_before != 0 {
byte_count + 1
} else {
byte_count
};
if bytes_needed > self.data.len() || bytes_needed + byte_ndx > self.data.len() {
return Err(ByteStreamError::OutOfRange);
}
let mut byte_stream = self.data[byte_ndx..byte_ndx + bytes_needed].to_vec();
if bits_before != 0 {
let mut carry: u8 = 0;
for _ in 0..bits_before {
for k in (0..bytes_needed).rev() {
let next: u8 = (byte_stream[k] & 0x01) << 7;
byte_stream[k] = (byte_stream[k] >> 1) | carry;
carry = next;
}
}
}
if bytes_needed > byte_count {
byte_stream.pop();
}
if bits_in_last_byte != 0 {
*byte_stream.last_mut().unwrap() &= bit_mask(bits_in_last_byte);
}
Ok(byte_stream)
}
}
impl From<&[u8]> for ByteStream {
fn from(slice: &[u8]) -> Self {
ByteStream::from(slice.to_vec())
}
}
impl From<Vec<u8>> for ByteStream {
fn from(vec: Vec<u8>) -> Self {
ByteStream { data: vec }
}
}
#[cfg(test)]
mod tests {
use super::ByteStream;
#[test]
fn test_get_bytes_no_shift() {
let bytes: Vec<u8> = vec![0xff, 0x00, 0x55];
let bit_stream = ByteStream::from(bytes.clone());
let new_bytes = bit_stream.get_bytes(0, bytes.len() * 8).unwrap();
assert_eq!(bytes, new_bytes);
}
#[test]
fn test_get_bytes_with_shift_in_byte() {
let bytes: Vec<u8> = vec![0xff, 0x00, 0x55];
let bit_stream = ByteStream::from(bytes.clone());
let new_bytes = bit_stream.get_bytes(4, 4).unwrap();
assert_eq!(vec![0x0f], new_bytes);
}
#[test]
fn test_get_bytes_with_shift_across_bytes() {
let bytes: Vec<u8> = vec![0xff, 0x55];
let bit_stream = ByteStream::from(bytes.clone());
let new_bytes = bit_stream.get_bytes(4, 8).unwrap();
assert_eq!(vec![0x5f], new_bytes);
}
#[test]
fn test_get_bytes_with_shift_across_bytes_odd() {
let bytes: Vec<u8> = vec![0xff, 0x55];
let bit_stream = ByteStream::from(bytes.clone());
let new_bytes = bit_stream.get_bytes(7, 2).unwrap();
assert_eq!(vec![0x03], new_bytes);
}
#[test]
fn test_get_bytes_one_byte() {
let bytes: Vec<u8> = vec![0xff, 0x55];
let bit_stream = ByteStream::from(bytes.clone());
let new_bytes = bit_stream.get_bytes(8, 8).unwrap();
assert_eq!(vec![0x55], new_bytes);
}
#[test]
fn test_get_bytes_3_bits() {
let bytes: Vec<u8> = vec![0x55];
let bit_stream = ByteStream::from(bytes.clone());
let new_bytes = bit_stream.get_bytes(0, 3).unwrap();
assert_eq!(vec![0x05], new_bytes);
}
}
+66
View File
@@ -0,0 +1,66 @@
use crate::byte_stream::ByteStream;
use serde::Deserialize;
use std::fmt::Write;
#[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_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)
}
_ => ("".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
}
}
+24
View File
@@ -0,0 +1,24 @@
pub mod format;
use crate::formatter::format::Format;
use serde::Deserialize;
use std::fs::File;
use std::io::Read;
use std::path::Path;
#[derive(Debug, Deserialize, Clone)]
pub struct FormatConfig {
pub formats: Vec<Format>,
}
impl FormatConfig {
pub fn new(config_path: &Path) -> Result<Self, std::io::Error> {
let mut config = File::open(config_path)?;
let mut contents = String::new();
config.read_to_string(&mut contents)?;
Ok(toml::from_str(&contents).unwrap())
}
}
+37
View File
@@ -0,0 +1,37 @@
use crate::parser::parse_bytes_from_input_arg;
use formatter::FormatConfig;
use std::path::PathBuf;
use structopt::StructOpt;
mod byte_stream;
mod formatter;
mod parser;
#[derive(Debug, StructOpt)]
#[structopt(name = "Formaty", about = "Arbitrary Binary Data Formatting")]
pub struct Args {
#[structopt(parse(from_os_str))]
config: PathBuf,
format: String,
data: Vec<String>,
}
fn main() {
let args: Args = Args::from_args();
let config = FormatConfig::new(&args.config).unwrap();
let format = match config.formats.iter().find(|f| f.name == args.format) {
None => {
println!("Format not found in config file");
return;
}
Some(format) => format,
};
let data = parse_bytes_from_input_arg(args.data).unwrap();
println!("{}", format.format_data(&data));
}
+52
View File
@@ -0,0 +1,52 @@
use std::num::ParseIntError;
#[derive(Debug, Clone)]
pub enum ByteArrayParseErr {
EmptySrcArray,
ParseIntError(ParseIntError),
}
impl From<ParseIntError> for ByteArrayParseErr {
fn from(e: ParseIntError) -> Self {
ByteArrayParseErr::ParseIntError(e)
}
}
fn bytes_from_str_array(src: Vec<String>) -> Result<Vec<u8>, ByteArrayParseErr> {
src.iter()
.map(|element| {
if element.starts_with("0x") || element.starts_with("0X") {
u8::from_str_radix(&element[2..], 16)
} else if element.starts_with("0b") || element.starts_with("0B") {
u8::from_str_radix(&element[2..], 1)
} else if element.starts_with('h') || element.starts_with('h') {
u8::from_str_radix(&element[1..], 16)
} else if let Some(value) = element.strip_prefix('0') {
u8::from_str_radix(value, 8)
} else {
str::parse(element)
}
})
.map(|e| e.map_err( ByteArrayParseErr::from ))
.collect()
}
pub fn parse_bytes_from_input_arg(src: Vec<String>) -> Result<Vec<u8>, ByteArrayParseErr> {
if src.is_empty() {
return Err(ByteArrayParseErr::EmptySrcArray);
}
let str_arr = if src.len() == 1 {
src[0]
.replace(",", " ")
.replace("[", "")
.replace("]", "")
.split_whitespace()
.map(|s| s.to_string())
.collect()
} else {
src
};
bytes_from_str_array(str_arr)
}