Last active
September 20, 2016 14:26
-
-
Save yyogo/2ec6133ab75770df25fa8de815a37366 to your computer and use it in GitHub Desktop.
Hex dumper for Python (I really care about my hex dumps, okay?!)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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