///|
/// Half-open [start, end) interval. End may equal 2^32; start must be smaller.
pub struct AddressRange {
start : Int64
end : Int64
} derive(Eq, Debug)
///|
/// Construct a checked interval. Empty intervals are legal.
pub fn AddressRange::new(
start : Int64,
end : Int64,
) -> AddressRange raise FirmwareError {
if start < 0L || end < start || end > 0x100000000L {
raise FirmwareError(
diagnostic(
InvalidRange,
"expected 0 <= start <= end <= 2^32",
address=start,
end_address=end,
),
)
}
{ start, end, }
}
///|
/// Convert an inclusive user-facing range to the library's half-open model.
pub fn AddressRange::inclusive(
start : Int64,
last : Int64,
) -> AddressRange raise FirmwareError {
if last < 0L || last > 0xFFFFFFFFL || last < start {
raise FirmwareError(
diagnostic(
InvalidRange,
"invalid inclusive address range",
address=start,
end_address=last,
),
)
}
AddressRange::new(start, last + 1L)
}
///|
/// Number of addresses in the interval, without narrowing to machine Int.
pub fn AddressRange::length(self : AddressRange) -> Int64 {
self.end - self.start
}
///|
/// Empty intervals contain no addresses.
pub fn AddressRange::is_empty(self : AddressRange) -> Bool {
self.start == self.end
}
///|
/// Test membership using an exclusive upper bound.
pub fn AddressRange::contains(self : AddressRange, address : Int64) -> Bool {
address >= self.start && address < self.end
}
///|
/// A nonempty intersection, or None for disjoint/touching intervals.
pub fn AddressRange::intersection(
self : AddressRange,
other : AddressRange,
) -> AddressRange? {
let start = self.start.max(other.start)
let end = self.end.min(other.end)
if start < end {
Some({ start, end, })
} else {
None
}
}
///|
/// Split around an intersection, retaining the two possible outer intervals.
pub fn AddressRange::subtract(
self : AddressRange,
other : AddressRange,
) -> Array[AddressRange] {
match self.intersection(other) {
None => if self.is_empty() { [] } else { [self] }
Some(overlap) => {
let result = []
if self.start < overlap.start {
result.push({ start: self.start, end: overlap.start, })
}
if overlap.end < self.end {
result.push({ start: overlap.end, end: self.end, })
}
result
}
}
}
///|
/// Checked relocation of a range, useful for bootloader image rebasing.
pub fn AddressRange::shift(
self : AddressRange,
delta : Int64,
) -> AddressRange raise FirmwareError {
// Validate delta before adding, including the most negative Int64 value.
if delta < -self.start || delta > 0x100000000L - self.end {
raise FirmwareError(
diagnostic(AddressOverflow, "relocation exceeds 32-bit address space"),
)
}
AddressRange::new(self.start + delta, self.end + delta)
}
///|
/// Display a nonempty interval with inclusive endpoints for firmware tools.
pub fn AddressRange::render(self : AddressRange) -> String {
if self.is_empty() {
"empty at " + hex_address(self.start)
} else {
hex_address(self.start) + ".." + hex_address(self.end - 1L)
}
}
///|
/// Check a start plus byte length without overflow in either integer width.
pub fn data_range(
address : Int64,
length : Int,
) -> AddressRange raise FirmwareError {
if address < 0L ||
address > 0xFFFFFFFFL ||
length < 0 ||
length.to_int64() > 0x100000000L - address {
raise FirmwareError(
diagnostic(AddressOverflow, "data exceeds 32-bit address space", address~),
)
}
AddressRange::new(address, address + length.to_int64())
}