"""
Cheap-o-cigar hexdump, somewhat cribbed from somewhere that I cannot recall.
2018-06-10 texadactyl Version 1
"""
import struct

def _bytes2ints(arg_bytes):
    ilist = []
    for i in range(0, len(arg_bytes)):
        ilist.append(int(arg_bytes[i]))
    return ilist

def _printable(arg_int):
    if arg_int > 31 and arg_int < 127:
        return chr(arg_int)
    return '.'

def execute(arg_label, arg_data, arg_line_size):
    """
    Dump hex portion on the lect, displayable portion on the right.
    Non-printable characters are replaced with a '.' in the displayable portion.

    arg_label: first line.
    arg_data: blob to dump.
    arg_line_size = number of characters dumped per line.

    Result returned is a string of the form: "label\nline1\nline2\netc.\n".

    Sample invocation:
    >>>print(hexdump.execute("100 ASCII zeroes:", b'0' * 100, 16))
    100 ASCII zeroes:
    0000   30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30   0000000000000000
    0010   30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30   0000000000000000
    0020   30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30   0000000000000000
    0030   30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30   0000000000000000
    0040   30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30   0000000000000000
    0050   30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30   0000000000000000
    0060   30 30 30 30                                       0000
    """
    byte_array = bytearray(arg_data)
    cookedlines = [arg_label]
    fmt = "%%04X   %%-%ds   %%s" % (3 * arg_line_size - 1,)
    fmtlinesize = 7 + 3*arg_line_size + 3 + arg_line_size
    for i in range(0, len(byte_array), arg_line_size):
        line = byte_array[i:i+arg_line_size]
        intlist = _bytes2ints(line)
        hextext = " ".join('%02x' % b for b in intlist)
        rawtext = "".join(_printable(b) for b in intlist)
        cookedlines.append(fmt % (i, str(hextext), str(rawtext)))
    if cookedlines:
        cookedlines[-1] = cookedlines[-1].ljust(fmtlinesize)
    cookedlines.append("")
    return "\n".join(cookedlines)

if __name__ == "__main__":
    DATA = b'\x21\x22'
    print(execute("Bytes 0x21 and 0x22:", DATA, 16))
    print(execute("Integer 42:", struct.pack('i', 42), 16))
    print(execute("Double-precision Pi:", struct.pack('d', 3.14159265), 16))
    print(execute("Alphabet:", "ABCDEFGHIJKLMNOPQRSTUVWXYZ".encode(), 16))
    print(execute("100 ASCII zeroes:", b'0' * 100, 16))