///|
pub(all) enum ByteDifferenceKind {
ByteAdded
ByteRemoved
ByteChanged
} derive(Eq, Debug)
///|
pub struct ByteDifference {
address_value : UInt64
kind_value : ByteDifferenceKind
left_value : Byte?
right_value : Byte?
} derive(Eq, Debug)
///|
pub struct ImageComparison {
difference_values : Array[ByteDifference]
added_count_value : Int
removed_count_value : Int
changed_count_value : Int
} derive(Eq, Debug)
///|
fn image_cells(image : FirmwareImage) -> (Array[UInt64], Array[Byte]) {
let addresses : Array[UInt64] = []
let values : Array[Byte] = []
for segment in image.segments() {
let data = segment.data()
for offset = 0; offset < data.length(); offset = offset + 1 {
addresses.push(segment.address() + offset.to_uint64())
values.push(data[offset])
}
}
(addresses, values)
}
///|
fn comparison_limit_error() -> FirmwareError {
FirmwareError::new(
IntegrityViolation,
"image comparison exceeds the configured difference evidence limit",
SourcePosition::line(0),
)
}
///|
/// Compare sparse images without treating absent addresses as zero bytes.
pub fn compare_images(
left : FirmwareImage,
right : FirmwareImage,
max_differences? : Int = 10000,
) -> Result[ImageComparison, FirmwareError] {
if max_differences < 0 {
return Err(comparison_limit_error())
}
let (left_addresses, left_values) = image_cells(left)
let (right_addresses, right_values) = image_cells(right)
let differences : Array[ByteDifference] = []
let mut left_index = 0
let mut right_index = 0
let mut added = 0
let mut removed = 0
let mut changed = 0
while left_index < left_addresses.length() ||
right_index < right_addresses.length() {
if left_index >= left_addresses.length() {
differences.push({
address_value: right_addresses[right_index],
kind_value: ByteAdded,
left_value: None,
right_value: Some(right_values[right_index]),
})
added = added + 1
right_index = right_index + 1
} else if right_index >= right_addresses.length() {
differences.push({
address_value: left_addresses[left_index],
kind_value: ByteRemoved,
left_value: Some(left_values[left_index]),
right_value: None,
})
removed = removed + 1
left_index = left_index + 1
} else if left_addresses[left_index] < right_addresses[right_index] {
differences.push({
address_value: left_addresses[left_index],
kind_value: ByteRemoved,
left_value: Some(left_values[left_index]),
right_value: None,
})
removed = removed + 1
left_index = left_index + 1
} else if right_addresses[right_index] < left_addresses[left_index] {
differences.push({
address_value: right_addresses[right_index],
kind_value: ByteAdded,
left_value: None,
right_value: Some(right_values[right_index]),
})
added = added + 1
right_index = right_index + 1
} else {
if left_values[left_index] != right_values[right_index] {
differences.push({
address_value: left_addresses[left_index],
kind_value: ByteChanged,
left_value: Some(left_values[left_index]),
right_value: Some(right_values[right_index]),
})
changed = changed + 1
}
left_index = left_index + 1
right_index = right_index + 1
}
if differences.length() > max_differences {
return Err(comparison_limit_error())
}
}
Ok({
difference_values: differences,
added_count_value: added,
removed_count_value: removed,
changed_count_value: changed,
})
}
///|
pub fn ByteDifference::address(self : ByteDifference) -> UInt64 {
self.address_value
}
///|
pub fn ByteDifference::kind(self : ByteDifference) -> ByteDifferenceKind {
self.kind_value
}
///|
pub fn ByteDifference::left(self : ByteDifference) -> Byte? {
self.left_value
}
///|
pub fn ByteDifference::right(self : ByteDifference) -> Byte? {
self.right_value
}
///|
pub fn ImageComparison::differences(
self : ImageComparison,
) -> Array[ByteDifference] {
self.difference_values.copy()
}
///|
pub fn ImageComparison::added_count(self : ImageComparison) -> Int {
self.added_count_value
}
///|
pub fn ImageComparison::removed_count(self : ImageComparison) -> Int {
self.removed_count_value
}
///|
pub fn ImageComparison::changed_count(self : ImageComparison) -> Int {
self.changed_count_value
}
///|
pub fn ImageComparison::identical(self : ImageComparison) -> Bool {
self.difference_values.length() == 0
}