///|
fn image_as_chunks(image : FirmwareImage) -> Array[FirmwareChunk] {
let chunks : Array[FirmwareChunk] = []
for index, segment in image.segments() {
chunks.push(FirmwareChunk::new(segment.address(), segment.data(), index))
}
chunks
}
///|
fn invalid_image_range(message : String) -> FirmwareError {
FirmwareError::new(ImageInvalidRange, message, SourcePosition::line(0))
}
///|
/// Merge two images while applying an explicit byte-overlap policy.
pub fn FirmwareImage::merge(
self : FirmwareImage,
other : FirmwareImage,
policy? : OverlapPolicy = RejectOverlap,
) -> Result[FirmwareImage, FirmwareError] {
let chunks = image_as_chunks(self)
for chunk in image_as_chunks(other) {
chunks.push(chunk)
}
let entry = match (self.entry_point(), other.entry_point()) {
(Some(left), Some(right)) if left != right =>
return Err(
FirmwareError::new(
IntegrityViolation,
"cannot merge images with different entry points",
SourcePosition::line(0),
),
)
(Some(value), _) | (_, Some(value)) => Some(value)
_ => None
}
FirmwareImage::from_chunks(chunks, policy~, entry_point=entry)
}
///|
/// Return bytes intersecting the half-open address range `[start, end)`.
pub fn FirmwareImage::slice(
self : FirmwareImage,
start : UInt64,
end_exclusive : UInt64,
) -> Result[FirmwareImage, FirmwareError] {
if start >= end_exclusive {
return Err(invalid_image_range("image slice start must be below its end"))
}
let chunks : Array[FirmwareChunk] = []
for index, 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 {
let source = segment.data()
let offset = (overlap_start - segment.address()).to_int()
let length = (overlap_end - overlap_start).to_int()
let data = Bytes::makei(length, item => source[offset + item])
chunks.push(FirmwareChunk::new(overlap_start, data, index))
}
}
let entry = match self.entry_point() {
Some(value) if value >= start && value < end_exclusive => Some(value)
_ => None
}
FirmwareImage::from_chunks(chunks, entry_point=entry)
}
///|
fn relocate_address(
value : UInt64,
old_base : UInt64,
new_base : UInt64,
) -> Result[UInt64, FirmwareError] {
if new_base >= old_base {
let delta = new_base - old_base
if value > 0xFFFFFFFFFFFFFFFFUL - delta {
Err(invalid_image_range("image relocation overflows UInt64"))
} else {
Ok(value + delta)
}
} else {
let delta = old_base - new_base
if value < delta {
Err(invalid_image_range("image relocation underflows address zero"))
} else {
Ok(value - delta)
}
}
}
///|
/// Move the lowest image address to `new_base` while preserving distances.
pub fn FirmwareImage::relocate(
self : FirmwareImage,
new_base : UInt64,
) -> Result[FirmwareImage, FirmwareError] {
let old_base = match self.lowest_address() {
Some(value) => value
None => return Err(invalid_image_range("cannot relocate an empty image"))
}
let chunks : Array[FirmwareChunk] = []
for index, segment in self.segments() {
let address = match
relocate_address(segment.address(), old_base, new_base) {
Ok(value) => value
Err(error) => return Err(error)
}
let chunk = match
FirmwareChunk::create(address, segment.data(), line_index=index) {
Ok(value) => value
Err(error) => return Err(error)
}
chunks.push(chunk)
}
let entry = match self.entry_point() {
Some(value) =>
match relocate_address(value, old_base, new_base) {
Ok(relocated) => Some(relocated)
Err(error) => return Err(error)
}
None => None
}
FirmwareImage::from_chunks(chunks, entry_point=entry)
}
///|
/// Fill only gaps between existing segments, subject to an allocation bound.
pub fn FirmwareImage::fill_gaps(
self : FirmwareImage,
value : Byte,
max_fill_bytes : Int,
) -> Result[FirmwareImage, FirmwareError] {
if max_fill_bytes < 0 {
return Err(invalid_image_range("maximum fill bytes cannot be negative"))
}
let gaps = self.gaps()
let mut total = 0UL
for gap in gaps {
total = total + gap.length()
}
if total > max_fill_bytes.to_uint64() || total > 0x7FFFFFFFUL {
return Err(
invalid_image_range("gap fill exceeds the configured byte limit"),
)
}
let chunks = image_as_chunks(self)
for index, gap in gaps {
let data = Bytes::makei(gap.length().to_int(), _ => value)
chunks.push(FirmwareChunk::new(gap.start(), data, index))
}
FirmwareImage::from_chunks(
chunks,
policy=AllowIdentical,
entry_point=self.entry_point(),
)
}