use std::fmt;
use std::ops::Range;
use super::Instruction;
use crate::Database;
use crate::address::Address;
use crate::function::FunctionChunks;
pub struct Instructions<'db> {
db: &'db Database,
chunks: FunctionChunks,
cursor: Option<(Address, Address)>,
}
impl<'db> Instructions<'db> {
#[inline]
pub(crate) fn new(db: &'db Database, address: Address) -> Self {
Self {
db,
chunks: FunctionChunks::new(address, db),
cursor: None,
}
}
}
impl fmt::Debug for Instructions<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Instructions")
.field("chunks", &self.chunks)
.field("cursor", &self.cursor)
.finish_non_exhaustive()
}
}
impl Iterator for Instructions<'_> {
type Item = Instruction;
fn next(&mut self) -> Option<Instruction> {
loop {
let (address, end) = match self.cursor {
Some((address, end)) if address < end => (address, end),
_ => {
let chunk = self.chunks.next()?;
self.cursor = Some((chunk.start, chunk.end));
continue;
}
};
let stepped = self.db.item_end(address);
self.cursor = Some((if stepped > address { stepped } else { end }, end));
if self.db.is_code(address)
&& let Ok(instruction) = self.db.decode(address)
{
return Some(instruction);
}
}
}
}
impl Database {
#[must_use]
pub fn instructions_in(&self, range: Range<Address>) -> InstructionsIn<'_> {
InstructionsIn {
db: self,
cursor: range.start,
end: range.end,
}
}
}
pub struct InstructionsIn<'db> {
db: &'db Database,
cursor: Address,
end: Address,
}
impl fmt::Debug for InstructionsIn<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("InstructionsIn")
.field("cursor", &self.cursor)
.field("end", &self.end)
.finish_non_exhaustive()
}
}
impl Iterator for InstructionsIn<'_> {
type Item = Instruction;
fn next(&mut self) -> Option<Instruction> {
while self.cursor < self.end {
let address = self.cursor;
let stepped = self.db.item_end(address);
self.cursor = if stepped > address { stepped } else { self.end };
if self.db.is_code(address)
&& let Ok(instruction) = self.db.decode(address)
{
return Some(instruction);
}
}
None
}
}
#[cfg(test)]
mod tests {
use assert2::assert;
use super::*;
use crate::function::FunctionChunk;
#[test]
fn instructions_debug_renders_the_chunk_cursor() {
let db = Database::new();
let start = Address::new_const(0x1000);
let end = Address::new_const(0x1010);
let iter = Instructions {
db: &db,
chunks: FunctionChunks::from_chunks(vec![FunctionChunk { start, end }]),
cursor: Some((start, end)),
};
let rendered = format!("{iter:?}");
assert!(rendered.starts_with("Instructions"));
assert!(rendered.contains("cursor"));
}
#[test]
fn instructions_in_debug_renders_the_walk_bounds() {
let db = Database::new();
let iter = InstructionsIn {
db: &db,
cursor: Address::new_const(0x1000),
end: Address::new_const(0x1010),
};
let rendered = format!("{iter:?}");
assert!(rendered.starts_with("InstructionsIn"));
assert!(rendered.contains("cursor"));
assert!(rendered.contains("end"));
}
}