use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BitField {
pub name: String,
pub bits: usize,
pub field_type: FieldType,
}
impl BitField {
pub fn new(name: impl Into<String>, bits: usize, field_type: FieldType) -> Self {
Self {
name: name.into(),
bits,
field_type,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FieldType {
U8,
U16,
U32,
U64,
Nested(String),
}
impl FieldType {
pub fn default_bit_size(&self) -> Option<usize> {
match self {
FieldType::U8 => Some(8),
FieldType::U16 => Some(16),
FieldType::U32 => Some(32),
FieldType::U64 => Some(64),
FieldType::Nested(_) => None, }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BitfieldStruct {
pub name: String,
pub fields: Vec<BitField>,
}
impl BitfieldStruct {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
fields: Vec::new(),
}
}
pub fn add_field(&mut self, name: impl Into<String>, bits: usize, field_type: FieldType) {
self.fields.push(BitField::new(name, bits, field_type));
}
pub fn bit_size(&self, structs: &HashMap<String, BitfieldStruct>) -> usize {
self.fields
.iter()
.map(|field| match &field.field_type {
FieldType::Nested(struct_name) => {
if let Some(nested) = structs.get(struct_name) {
nested.bit_size(structs)
} else {
0 }
}
_ => field.bits,
})
.sum()
}
pub fn byte_size(&self, structs: &HashMap<String, BitfieldStruct>) -> usize {
self.bit_size(structs).div_ceil(8)
}
pub fn get_field(&self, name: &str) -> Option<&BitField> {
self.fields.iter().find(|field| field.name == name)
}
pub fn field_bit_offset(
&self,
field_name: &str,
structs: &HashMap<String, BitfieldStruct>,
) -> Option<usize> {
let mut offset = 0;
for field in &self.fields {
if field.name == field_name {
return Some(offset);
}
offset += match &field.field_type {
FieldType::Nested(struct_name) => {
if let Some(nested) = structs.get(struct_name) {
nested.bit_size(structs)
} else {
0
}
}
_ => field.bits,
};
}
None
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct BitfieldValue {
pub struct_name: String,
pub values: HashMap<String, FieldValue>,
}
impl BitfieldValue {
pub fn new(struct_name: impl Into<String>) -> Self {
Self {
struct_name: struct_name.into(),
values: HashMap::new(),
}
}
pub fn set_u8(&mut self, field_name: impl Into<String>, value: u8) {
self.values.insert(field_name.into(), FieldValue::U8(value));
}
pub fn set_u16(&mut self, field_name: impl Into<String>, value: u16) {
self.values
.insert(field_name.into(), FieldValue::U16(value));
}
pub fn set_u32(&mut self, field_name: impl Into<String>, value: u32) {
self.values
.insert(field_name.into(), FieldValue::U32(value));
}
pub fn set_u64(&mut self, field_name: impl Into<String>, value: u64) {
self.values
.insert(field_name.into(), FieldValue::U64(value));
}
pub fn set_nested(&mut self, field_name: impl Into<String>, value: BitfieldValue) {
self.values
.insert(field_name.into(), FieldValue::Nested(Box::new(value)));
}
pub fn get_u8(&self, field_name: &str) -> Option<u8> {
match self.values.get(field_name)? {
FieldValue::U8(val) => Some(*val),
_ => None,
}
}
pub fn get_u16(&self, field_name: &str) -> Option<u16> {
match self.values.get(field_name)? {
FieldValue::U16(val) => Some(*val),
_ => None,
}
}
pub fn get_u32(&self, field_name: &str) -> Option<u32> {
match self.values.get(field_name)? {
FieldValue::U32(val) => Some(*val),
_ => None,
}
}
pub fn get_u64(&self, field_name: &str) -> Option<u64> {
match self.values.get(field_name)? {
FieldValue::U64(val) => Some(*val),
_ => None,
}
}
pub fn get_nested(&self, field_name: &str) -> Option<&BitfieldValue> {
match self.values.get(field_name)? {
FieldValue::Nested(val) => Some(val),
_ => None,
}
}
pub fn get_nested_mut(&mut self, field_name: &str) -> Option<&mut BitfieldValue> {
match self.values.get_mut(field_name)? {
FieldValue::Nested(val) => Some(val),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum FieldValue {
U8(u8),
U16(u16),
U32(u32),
U64(u64),
Nested(Box<BitfieldValue>),
}
impl FieldValue {
pub fn to_u64(&self) -> u64 {
match self {
FieldValue::U8(val) => *val as u64,
FieldValue::U16(val) => *val as u64,
FieldValue::U32(val) => *val as u64,
FieldValue::U64(val) => *val,
FieldValue::Nested(_) => 0, }
}
pub fn field_type(&self) -> FieldType {
match self {
FieldValue::U8(_) => FieldType::U8,
FieldValue::U16(_) => FieldType::U16,
FieldValue::U32(_) => FieldType::U32,
FieldValue::U64(_) => FieldType::U64,
FieldValue::Nested(val) => FieldType::Nested(val.struct_name.clone()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bitfield_struct_creation() {
let mut bf_struct = BitfieldStruct::new("TestStruct");
bf_struct.add_field("field1", 4, FieldType::U8);
bf_struct.add_field("field2", 8, FieldType::U16);
assert_eq!(bf_struct.name, "TestStruct");
assert_eq!(bf_struct.fields.len(), 2);
assert_eq!(bf_struct.fields[0].name, "field1");
assert_eq!(bf_struct.fields[0].bits, 4);
}
#[test]
fn test_bitfield_value_operations() {
let mut value = BitfieldValue::new("TestStruct");
value.set_u8("field1", 42);
value.set_u16("field2", 1024);
assert_eq!(value.get_u8("field1"), Some(42));
assert_eq!(value.get_u16("field2"), Some(1024));
assert_eq!(value.get_u8("nonexistent"), None);
}
#[test]
fn test_nested_structures() {
let mut nested = BitfieldValue::new("NestedStruct");
nested.set_u8("inner_field", 100);
let mut parent = BitfieldValue::new("ParentStruct");
parent.set_nested("nested", nested);
let retrieved = parent.get_nested("nested").unwrap();
assert_eq!(retrieved.get_u8("inner_field"), Some(100));
}
#[test]
fn test_bit_size_calculation() {
let mut structs = HashMap::new();
let mut bf_struct = BitfieldStruct::new("TestStruct");
bf_struct.add_field("field1", 4, FieldType::U8);
bf_struct.add_field("field2", 12, FieldType::U16);
structs.insert("TestStruct".to_string(), bf_struct.clone());
assert_eq!(bf_struct.bit_size(&structs), 16);
assert_eq!(bf_struct.byte_size(&structs), 2);
}
#[test]
fn test_field_offset_calculation() {
let mut structs = HashMap::new();
let mut bf_struct = BitfieldStruct::new("TestStruct");
bf_struct.add_field("field1", 4, FieldType::U8);
bf_struct.add_field("field2", 8, FieldType::U16);
bf_struct.add_field("field3", 4, FieldType::U8);
structs.insert("TestStruct".to_string(), bf_struct.clone());
assert_eq!(bf_struct.field_bit_offset("field1", &structs), Some(0));
assert_eq!(bf_struct.field_bit_offset("field2", &structs), Some(4));
assert_eq!(bf_struct.field_bit_offset("field3", &structs), Some(12));
assert_eq!(bf_struct.field_bit_offset("nonexistent", &structs), None);
}
}