Source code for objutils.hexdump
#!/usr/bin/env python
__version__ = "0.1.2"
__all__ = [
"Dumper",
"CanonicalDumper",
"isprintable",
]
__copyright__ = """
objutils - Object file library for Python.
(C) 2010-2024 by Christoph Schueler <cpu12.gems@googlemail.com>
All Rights Reserved
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
from typing import BinaryIO, Iterable, Sequence, TextIO
import sys
[docs]
def isprintable(ch: int) -> bool:
return 0x1F < ch < 0x80
def unpack(*args: int) -> Sequence[int]:
return args
[docs]
class Dumper:
LINE_LENGTH: int = 16
def __init__(self, fp: TextIO = sys.stdout, num_address_bits: int = 32) -> None:
self._fp: TextIO = fp
self._rolloverMask: int = 2**num_address_bits
self._nibbles: int = num_address_bits >> 2
self._addressMask: str = f"%0{self._nibbles:d}x "
self.previous_row: bytes = b""
self.elided: bool = False
[docs]
def dump_data(self, section, offset: int = 0) -> None:
end = section.length
start_pos = 0
line_num = 0
end_pos = self.LINE_LENGTH
while end_pos < end:
line_num += 1
row = section.data[start_pos:end_pos]
if row == self.previous_row:
if not self.elided:
print(" *", file=self._fp)
self.elided = True
else:
self.dump_row(row, start_pos + section.start_address)
self.elided = False
start_pos = end_pos
end_pos = end_pos + self.LINE_LENGTH
self.previous_row = row
row = section.data[start_pos:end_pos]
self.dump_row(row, start_pos + section.start_address)
print("-" * 15, file=self._fp)
print(f"{section.length:-9d} bytes", file=self._fp)
print("-" * 15, file=self._fp)
[docs]
class CanonicalDumper(Dumper):
LINE_LENGTH = 0x10
[docs]
def printhex_bytes(self, row: Sequence[int]) -> None:
row = list(row)
filler = [0x20] * (self.LINE_LENGTH - len(row))
ascii_part = [isprintable(x) and chr(x) or "." for x in row + filler]
print("|{}|".format(("%s" * self.LINE_LENGTH) % unpack(*ascii_part)), file=self._fp)
[docs]
def dump_row(self, row: Sequence[int] | bytes | bytearray, startAddr: int) -> None:
start_pos = 0
print(self._addressMask % ((start_pos + startAddr) % self._rolloverMask), file=self._fp, end=" ")
# Convert row to a tuple of ints to avoid TypeError for empty sequences
ints = tuple(row)
if ints:
print("%02x " * len(ints) % unpack(*ints), file=self._fp, end=" ")
else:
print("", file=self._fp, end="")
if len(ints) < self.LINE_LENGTH:
spaces = " " * (self.LINE_LENGTH - len(ints))
print(spaces, file=self._fp, end="")
self.printhex_bytes(ints)