#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SpaceId(u32);
impl SpaceId {
pub const ROOT: SpaceId = SpaceId(0);
pub(crate) const fn from_index(index: u32) -> Self {
Self(index)
}
pub fn index(self) -> u32 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ByteRange {
pub start: u64,
pub end: u64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SpaceDerivation {
Root,
StoredSlice {
parent: SpaceId,
range: ByteRange,
},
Expanded {
parent: SpaceId,
source_range: ByteRange,
},
Concatenated {
parents: Vec<SpaceId>,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SpaceDescriptor {
pub id: SpaceId,
pub label: String,
pub derivation: SpaceDerivation,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AddressStep {
pub label: String,
pub kind: AddressStepKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AddressStepKind {
Root,
StoredMember,
ExpandedMember,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResolvedAddress {
pub steps: Vec<AddressStep>,
pub offset: u64,
}
impl ResolvedAddress {
pub fn path(&self) -> String {
let mut out = String::new();
for (index, step) in self.steps.iter().enumerate() {
if index > 0 {
out.push('/');
}
out.push_str(&step.label);
}
out.push('@');
out.push_str(&self.offset.to_string());
out
}
pub fn inspect_commands(&self, file: &str) -> Vec<String> {
let leaf = self.steps.last();
match leaf.map(|step| step.kind) {
None | Some(AddressStepKind::Root) => {
vec![format!(
"cadmpeg inspect hex {file} --offset {} --len 64",
self.offset
)]
}
Some(AddressStepKind::StoredMember | AddressStepKind::ExpandedMember) => {
let member = leaf.expect("leaf present").label.clone();
let extracted = format!("{file}.member");
vec![
format!("cadmpeg inspect extract {file} {member} -o {extracted}"),
format!(
"cadmpeg inspect hex {extracted} --offset {} --len 64",
self.offset
),
]
}
}
}
}
pub fn resolve_address(
descriptors: &[SpaceDescriptor],
location: super::error::SourceLocation,
) -> ResolvedAddress {
let mut steps = Vec::new();
let mut current = location.space;
let mut guard = 0u32;
while guard < descriptors.len() as u32 + 1 {
guard = guard.saturating_add(1);
let Some(descriptor) = descriptors.iter().find(|entry| entry.id == current) else {
break;
};
let kind = match descriptor.derivation {
SpaceDerivation::Root => AddressStepKind::Root,
SpaceDerivation::StoredSlice { .. } => AddressStepKind::StoredMember,
SpaceDerivation::Expanded { .. } | SpaceDerivation::Concatenated { .. } => {
AddressStepKind::ExpandedMember
}
};
steps.push(AddressStep {
label: descriptor.label.clone(),
kind,
});
match descriptor.derivation {
SpaceDerivation::Root => break,
SpaceDerivation::StoredSlice { parent, .. }
| SpaceDerivation::Expanded { parent, .. } => {
current = parent;
}
SpaceDerivation::Concatenated { ref parents } => {
if let Some(parent) = parents.first().copied() {
current = parent;
} else {
break;
}
}
}
}
steps.reverse();
if steps.is_empty() {
steps.push(AddressStep {
label: "root".into(),
kind: AddressStepKind::Root,
});
}
ResolvedAddress {
steps,
offset: location.offset,
}
}