use std::collections::{HashMap, HashSet};
use device_driver_common::{
span::{Span, SpanExt, Spanned},
specifiers::{ByteOrder, ResetValue},
};
use crate::{
model::{FieldSet, LendingIterator, Manifest, Object, Register, Unique, UniqueId},
passes::{Assumption, Pass},
search_object,
};
use device_driver_diagnostics::{
Diagnostics, DynError,
errors::{ResetValueArrayWrongSize, ResetValueIntTooBig},
};
pub struct ResetValuesConverted;
impl Pass for ResetValuesConverted {
const ASSUMPTIONS_MADE: &[Assumption] = &[
Assumption::FieldsetRefsValid,
Assumption::ByteOrderSpecified,
Assumption::NamesUnique,
];
const ASSUMPTIONS_RELEASED: &[Assumption] = &[];
fn run_pass(
manifest: &mut Manifest,
diagnostics: &mut Diagnostics,
) -> Result<HashSet<UniqueId>, DynError> {
let mut new_reset_values = HashMap::new();
for object in manifest.iter_objects() {
if let Object::Register(reg) = object {
let target_field_set = get_target_field_set(reg, manifest);
if let Some(reset_value) = reg.reset_value.as_ref() {
let new_reset_value = convert_reset_value(
reset_value.clone(),
target_field_set.size_bytes.value,
target_field_set.byte_order.unwrap(),
diagnostics,
reg.name.span,
);
assert_eq!(
new_reset_values.insert(reg.id(), new_reset_value),
None,
"All names must be unique"
);
}
}
}
let mut iter = manifest.iter_objects_with_config_mut();
while let Some((object, _)) = iter.next() {
if let Object::Register(register) = object
&& let Some(new_reset_value) = new_reset_values.remove(®ister.id())
{
register.reset_value = new_reset_value;
}
}
assert!(new_reset_values.is_empty());
Ok(Default::default())
}
}
fn get_target_field_set<'m>(reg: &Register, manifest: &'m Manifest) -> &'m FieldSet {
search_object(manifest, ®.field_set_ref)
.expect("All fieldset refs should already be checked and valid here")
.as_field_set()
.expect("All fieldset refs should already be checked and valid here")
}
fn convert_reset_value(
reset_value: Spanned<ResetValue>,
size_bytes: u32,
target_byte_order: ByteOrder,
diagnostics: &mut Diagnostics,
register_context: Span,
) -> Option<Spanned<ResetValue>> {
match reset_value.value {
ResetValue::Integer(int) => match target_byte_order {
ByteOrder::LE => {
let int_bytes = int.to_le_bytes();
let (val, rest) = int_bytes
.split_at_checked(size_bytes as usize)
.unwrap_or((&int_bytes, &[]));
if rest.iter().any(|b| *b != 0) {
diagnostics.add(ResetValueIntTooBig {
reset_value: reset_value.span,
reset_value_size_bytes: size_bytes
+ (rest.len() - rest.iter().rev().take_while(|b| **b == 0).count())
as u32,
register_size_bytes: size_bytes,
register_context,
});
return None;
}
let mut val = val.to_vec();
if val.len() < size_bytes as usize {
val.resize(size_bytes as usize, 0);
}
Some(ResetValue::Array(val).with_span(reset_value.span))
}
ByteOrder::BE => {
let int_bytes = int.to_be_bytes();
let (rest, val) =
int_bytes.split_at(int_bytes.len().saturating_sub(size_bytes as usize));
if rest.iter().any(|b| *b != 0) {
diagnostics.add(ResetValueIntTooBig {
reset_value: reset_value.span,
reset_value_size_bytes: size_bytes
+ (rest.len() - rest.iter().take_while(|b| **b == 0).count()) as u32,
register_size_bytes: size_bytes,
register_context,
});
return None;
}
let mut val = val.to_vec();
if val.len() < size_bytes as usize {
let mut new_val = vec![0; size_bytes as usize];
new_val[size_bytes as usize - val.len()..].copy_from_slice(&val);
val = new_val;
}
Some(ResetValue::Array(val).with_span(reset_value.span))
}
},
ResetValue::Array(array) => {
if array.len() != size_bytes as usize {
diagnostics.add(ResetValueArrayWrongSize {
reset_value: reset_value.span,
reset_value_size_bytes: array.len() as u32,
register_size_bytes: size_bytes,
register_context,
});
return None;
}
Some(ResetValue::Array(array).with_span(reset_value.span))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn integer_pass() {
let mut diagnostics = Diagnostics::new();
assert_eq!(
convert_reset_value(
ResetValue::Integer(0x12_34).with_dummy_span(),
2,
ByteOrder::LE,
&mut diagnostics,
Span::empty()
),
Some(ResetValue::Array(vec![0x34, 0x12]).with_dummy_span())
);
assert!(diagnostics.is_empty());
let mut diagnostics = Diagnostics::new();
assert_eq!(
convert_reset_value(
ResetValue::Integer(0x12_34).with_dummy_span(),
2,
ByteOrder::BE,
&mut diagnostics,
Span::empty()
),
Some(ResetValue::Array(vec![0x12, 0x34]).with_dummy_span())
);
assert!(diagnostics.is_empty());
}
#[test]
fn integer_fail() {
let mut diagnostics = Diagnostics::new();
assert_eq!(
convert_reset_value(
ResetValue::Integer(0x12_34_56).with_dummy_span(),
1,
ByteOrder::LE,
&mut diagnostics,
Span::empty()
),
None
);
assert!(diagnostics.has_error());
println!("{diagnostics:?}");
let mut diagnostics = Diagnostics::new();
assert_eq!(
convert_reset_value(
ResetValue::Integer(0x12_34_56).with_dummy_span(),
1,
ByteOrder::BE,
&mut diagnostics,
Span::empty()
),
None
);
assert!(diagnostics.has_error());
println!("{diagnostics:?}");
}
#[test]
fn array_pass() {
let mut diagnostics = Diagnostics::new();
assert_eq!(
convert_reset_value(
ResetValue::Array(vec![0x12, 0x34]).with_dummy_span(),
2,
ByteOrder::LE,
&mut diagnostics,
Span::empty()
),
Some(ResetValue::Array(vec![0x12, 0x34]).with_dummy_span())
);
assert!(diagnostics.is_empty());
}
#[test]
fn array_fail() {
let mut diagnostics = Diagnostics::new();
assert_eq!(
convert_reset_value(
ResetValue::Array(vec![0x12, 0x34, 0x56]).with_dummy_span(),
2,
ByteOrder::LE,
&mut diagnostics,
Span::empty()
),
None
);
assert!(diagnostics.has_error());
println!("{diagnostics:?}");
let mut diagnostics = Diagnostics::new();
assert_eq!(
convert_reset_value(
ResetValue::Array(vec![0x12]).with_dummy_span(),
2,
ByteOrder::LE,
&mut diagnostics,
Span::empty()
),
None
);
assert!(diagnostics.has_error());
println!("{diagnostics:?}");
}
}