Skip to content

Instantly share code, notes, and snippets.

@yyogo
Last active September 20, 2016 14:26
Show Gist options
  • Save yyogo/2ec6133ab75770df25fa8de815a37366 to your computer and use it in GitHub Desktop.
Save yyogo/2ec6133ab75770df25fa8de815a37366 to your computer and use it in GitHub Desktop.
Hex dumper for Python (I really care about my hex dumps, okay?!)
class HexDump(object):
"""
HexDump(data, width=16, groups=(2,4), prefix='')
Dump hex.
width - Number of bytes per dump line
groups - How to group the bytes, i.e where to put spaces. Ex:
(1,) - space after every byte
(2,4) - space after every two bytes, and another after
every 4 2-byte groups
prefix - Optional prefix for each line in the dump
"""
fmt = u'{self.prefix}{offset:06x}| {dump} |{asc:{self.width}}|'
def __init__(self, data, width=16, groups=(2, 4), prefix=''):
self.data = data
self._mv = memoryview(data) # to support all buffer types
self.width = width
self.groups = groups
self.length = len(data)
self.prefix = prefix
pattern = [u'{}'] * self.width
for g in groups:
pattern = [''.join(pattern[i:i + g]) +
' ' for i in xrange(0, width, g)]
self._dump_fmt_pattern = ''.join(pattern).strip()
self._dump_fmt = self._dump_fmt_pattern.format(
*([u'{:02x}'] * self.width))
def __len__(self):
""" len(hd) <=> hd.__len__
Return the number of lines in the hexdump
"""
return (self.length + self.width - 1) / self.width
def _dump_line(self, offset):
if offset < 0 or offset >= len(self._mv):
raise IndexError(offset)
line = bytearray(self._mv[offset:offset + self.width])
if len(line) < self.width: # partial line
dump_fmt = self._dump_fmt_pattern.format(
*([u'{:02x}'] * len(line) + [' '] * (self.width - len(line))))
else:
dump_fmt = self._dump_fmt
asc = bytearray(x if 32 <= x < 127 else '.' for x in line)
return self.fmt.format(
offset=offset, dump=dump_fmt.format(*line), asc=asc, self=self)
def __getitem__(self, index):
if isinstance(index, (int, long)):
if index < 0:
index += len(self)
return self._dump_line(index * self.width)
else:
return [self[i] for i in xrange(*index.indices(len(self)))]
def dump(self):
return u'\n'.join(self)
def partial(self, maxlines=20):
" Return a partial dump, with some lines from the start and from the end. "
if len(self) <= maxlines:
return self.dump()
start = maxlines * 6 / 10
end = maxlines - start - 1
hidden_bytes = (len(self) - end - start) * self.width
lines = self[:start] + [self.prefix + ' <... %d more bytes ...>' % hidden_bytes] + self[-end:]
return u'\n'.join(lines)
def __bytes__(self):
return unicode(self).encode('ascii')
__str__ = __bytes__
def __unicode__(self):
return self.dump()
def __repr__(self):
return "<%r of %r object, %s bytes:\n%s >" % (self.__class__.__name__,
type(self.data).__name__, self.length, HexDump(
self.data, width=self.width, groups=self.groups, prefix=' '
).partial().rstrip()
)
hd = hexdump = HexDump
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment