///|
pub(all) enum MissingBytePolicy {
RejectMissing
FillMissing(Byte)
} derive(Eq, Debug)
///|
pub(all) struct RangeCoverage {
start_value : UInt64
end_exclusive_value : UInt64
present_bytes_value : UInt64
missing_bytes_value : UInt64
} derive(Eq, Debug)
///|
fn checked_range_end(
start : UInt64,
length : Int,
) -> Result[UInt64, FirmwareError] {
if length < 0 {
return Err(invalid_image_range("range length cannot be negative"))
}
let width = length.to_uint64()
if start > 0xFFFFFFFFFFFFFFFFUL - width {
Err(invalid_image_range("range end overflows UInt64"))
} else {
Ok(start + width)
}
}
///|
/// Read a bounded contiguous range with an explicit sparse-byte policy.
pub fn FirmwareImage::read_range(
self : FirmwareImage,
start : UInt64,
length : Int,
missing : MissingBytePolicy,
max_bytes : Int,
) -> Result[Bytes, FirmwareError] {
if max_bytes < 0 || length > max_bytes {
return Err(
invalid_image_range("range read exceeds the configured byte limit"),
)
}
match checked_range_end(start, length) {
Ok(_) => ()
Err(error) => return Err(error)
}
let result : Array[Byte] = []
for offset = 0; offset < length; offset = offset + 1 {
let address = start + offset.to_uint64()
match self.byte_at(address) {
Some(value) => result.push(value)
None =>
match missing {
FillMissing(value) => result.push(value)
RejectMissing =>
return Err(
FirmwareError::new(
IntegrityViolation,
"requested range contains an absent sparse byte at address " +
address.to_string(radix=16),
SourcePosition::line(0),
),
)
}
}
}
Ok(Bytes::makei(result.length(), index => result[index]))
}
///|
/// Measure byte coverage within a non-empty half-open address range.
pub fn FirmwareImage::coverage(
self : FirmwareImage,
start : UInt64,
end_exclusive : UInt64,
) -> Result[RangeCoverage, FirmwareError] {
if start >= end_exclusive {
return Err(invalid_image_range("coverage range must be non-empty"))
}
let mut present = 0UL
for segment in self.segments() {
let overlap_start = if segment.address() > start {
segment.address()
} else {
start
}
let overlap_end = if segment.end_exclusive() < end_exclusive {
segment.end_exclusive()
} else {
end_exclusive
}
if overlap_start < overlap_end {
present = present + overlap_end - overlap_start
}
}
let width = end_exclusive - start
Ok({
start_value: start,
end_exclusive_value: end_exclusive,
present_bytes_value: present,
missing_bytes_value: width - present,
})
}
///|
/// Write bytes using replacement semantics while preserving the original image.
pub fn FirmwareImage::write(
self : FirmwareImage,
address : UInt64,
data : Bytes,
) -> Result[FirmwareImage, FirmwareError] {
let chunk = match FirmwareChunk::create(address, data) {
Ok(value) => value
Err(error) => return Err(error)
}
let patch = match FirmwareImage::from_chunks([chunk]) {
Ok(value) => value
Err(error) => return Err(error)
}
self.merge(patch, policy=ReplaceExisting)
}
///|
/// Remove bytes in `[start, end)` and re-canonicalize remaining segments.
pub fn FirmwareImage::erase(
self : FirmwareImage,
start : UInt64,
end_exclusive : UInt64,
) -> Result[FirmwareImage, FirmwareError] {
if start >= end_exclusive {
return Err(invalid_image_range("erase range must be non-empty"))
}
let chunks : Array[FirmwareChunk] = []
for index, segment in self.segments() {
let source = segment.data()
if segment.address() < start {
let prefix_end = if segment.end_exclusive() < start {
segment.end_exclusive()
} else {
start
}
let length = (prefix_end - segment.address()).to_int()
if length > 0 {
chunks.push(
FirmwareChunk::new(
segment.address(),
Bytes::makei(length, offset => source[offset]),
index,
),
)
}
}
if segment.end_exclusive() > end_exclusive {
let suffix_start = if segment.address() > end_exclusive {
segment.address()
} else {
end_exclusive
}
let offset = (suffix_start - segment.address()).to_int()
let length = (segment.end_exclusive() - suffix_start).to_int()
if length > 0 {
chunks.push(
FirmwareChunk::new(
suffix_start,
Bytes::makei(length, item => source[offset + item]),
index,
),
)
}
}
}
let entry = match self.entry_point() {
Some(value) if value >= start && value < end_exclusive => None
value => value
}
FirmwareImage::from_chunks(chunks, entry_point=entry)
}
///|
pub fn RangeCoverage::start(self : RangeCoverage) -> UInt64 {
self.start_value
}
///|
pub fn RangeCoverage::end_exclusive(self : RangeCoverage) -> UInt64 {
self.end_exclusive_value
}
///|
pub fn RangeCoverage::present_bytes(self : RangeCoverage) -> UInt64 {
self.present_bytes_value
}
///|
pub fn RangeCoverage::missing_bytes(self : RangeCoverage) -> UInt64 {
self.missing_bytes_value
}
///|
pub fn RangeCoverage::complete(self : RangeCoverage) -> Bool {
self.missing_bytes_value == 0UL
}
///|
pub fn RangeCoverage::ratio(self : RangeCoverage) -> Double {
let width = self.end_exclusive_value - self.start_value
self.present_bytes_value.to_double() / width.to_double()
}