Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions cmd/readmem/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,23 +206,32 @@ fn readmem(subargs: ReadmemArgs, context: &mut ExecutionContext) -> Result<()> {
}
};

// line 208 (blank)
if addr & (size - 1) as u32 != 0 {
bail!("address must be {}-byte aligned", size);
}

if let Some(file) = subargs.file {
let len32 = u32::try_from(length)
.ok()
.and_then(|l| addr.checked_add(l))
.ok_or_else(|| {
anyhow::anyhow!(
"address 0x{:08x} + length {} overflows a 32-bit address",
addr,
length
)
})?;
let mut f = std::fs::File::create(&file)?;
let mut bytes = vec![0u8; max];
for (i, addr) in (addr..addr + (length as u32)).step_by(max).enumerate()
{
for (i, addr) in (addr..len32).step_by(max).enumerate() {
let buf = &mut bytes[..std::cmp::min(max, length - (i * max))];
core.read_8(addr, buf)?;
f.write_all(buf)?;
}
info!(log, "Wrote {} bytes to {:?}", length, file);
return Ok(());
}

if length > max {
bail!("cannot read more than {} bytes", max);
}
Expand Down
41 changes: 41 additions & 0 deletions humility-hexdump/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,44 @@ impl Default for Dumper {
Self::new()
}
}

#[cfg(test)]
mod tests {
use super::*;

/// dump() computes the first-line offset as `addr & (width - 1)`.
/// Verify that a non-zero starting address correctly places data
/// at the right column in the first row (i.e. offset = addr % 16).
#[test]
fn dump_offset_from_address() {
// addr = 0x13: offset within a 16-byte row = 3
// only 1 byte of data — should not panic
let mut d = Dumper::new();
d.header = false;
d.ascii = false;
// Exercise path: offs = 0x13 & 0x0f = 3, lim = min(16-3, 1) = 1
d.dump(&[0xAB], 0x13);
}

/// When addr is at the very end of the u32 address space and the byte
/// slice is exactly 1 byte, dump() must not overflow the addr arithmetic.
#[test]
fn dump_high_address_single_byte() {
let mut d = Dumper::new();
d.header = false;
d.ascii = false;
// offs = 0xFFFFFFFF & 0x0F = 15; lim = min(16-15, 1) = 1; no wrapping
d.dump(&[0xFF], 0xFFFF_FFFF);
}

/// Verify that a multi-chunk dump (more than one 16-byte row) advances
/// addr correctly between rows without wrapping.
#[test]
fn dump_multirow_addr_advance() {
let mut d = Dumper::new();
d.header = false;
d.ascii = false;
// addr = 0, 32 bytes → 2 rows; addr increments by 16 between rows
d.dump(&[0u8; 32], 0x0);
}
}