zvodd
5/21/2018 - 11:47 AM

hex to base64 without using batteries and and excessive generators in python

hex to base64 without using batteries and and excessive generators in python

BT_ZERO, BT_NINE, BT_A, BT_F, BT_Z = ord(
    '0'), ord('9'), ord('A'), ord('F'), ord('Z')
BT_LIL_A, BT_LIL_Z = ord('a'), ord('z')
BT_CHR62, BT_CHR63 = ord('+'), ord('/')
BT_PAD = ord('=')
B64_BYTES = list(range(BT_A, BT_Z + 1)) +\
    list(range(BT_LIL_A, BT_LIL_Z + 1)) +\
    list(range(BT_ZERO, BT_NINE + 1)) + \
    [BT_CHR62, BT_CHR63]

# print(''.join([chr(x) for x in B64_BYTES])) # make string


def hex_digit_conv(x):
    if x > BT_A:
        return (x - BT_A) + 10
    else:
        return x - BT_ZERO


def nibble_pairs(ingen):
    pair = []
    watchdog = None
    for count, val in enumerate(ingen):
        watchdog = count
        pair.append(val)
        if (count & 1) == 1:  # odd check (conceptualy it's even if we weren't counting up from zero)
            yield pair
            pair = []
    if (watchdog & 1) == 0:
        raise IndexError("uneven number of digits")


def hex_bytes_2_bytes_gen(hb):
    hb = (x for x in hb.upper() if x > BT_ZERO - 1 and x <
          BT_NINE + 1 or x > BT_A - 1 and x < BT_F + 1)

    btgen = (hex_digit_conv(pair[0]) * 16 +
             hex_digit_conv(pair[1]) for pair in nibble_pairs(hb))
    # print(''.join([chr(x) for x in btgen]))  # make string
    return btgen


def slice_n_gen(ingen, n):
    """ How to reimpliment itertools.islice for no reason"""
    group = []
    count = 0
    countmod = 0
    for val in ingen:
        count += 1
        countmod = count % n
        if countmod == 0:
            group += [val]
            yield group
            group = []
        else:
            group += [val]

    if countmod != 0:
        yield group


def hex2b64(hexbytes):
    return bytes2b64(hex_bytes_2_bytes_gen(hexbytes))

def bytes2b64(inbytes):
    bytegrouping = 3
    for bits in slice_n_gen(inbytes, bytegrouping):
        chunk = []
        extra = bytegrouping - len(bits)
        total = (bits[0] << 16)
        if extra < 2:
            total |= (bits[1] << 8)
        if extra < 1:
            total |= (bits[2])
        nchrs = [(total >> 18) & 63, (total >> 12) &
                 63, (total >> 6) & 63, total & 63]
        if extra == 0:
            chunk += [nchrs[0], nchrs[1], nchrs[2], nchrs[3]]
        elif extra == 1:
            chunk += [nchrs[0], nchrs[1], nchrs[2], None]
        elif extra == 2:
            chunk += [nchrs[0], nchrs[1], None, None]

        for c in map(lambda x: BT_PAD if x is None else B64_BYTES[x], chunk):
            yield c


def main():
    import sys
    arg = sys.argv[1]
    r = ''.join(chr(x) for x in hex2b64(bytes(arg, 'latin-1')))
    print(r)


def test1():
    import pprint
    import codecs

    for i in range(0, 9):
        pprint.pprint([x for x in slice_n_gen(range(0, i), 3)])
    expected = """
        []
        [[0]]
        [[0, 1]]
        [[0, 1, 2]]
        [[0, 1, 2], [3]]
        [[0, 1, 2], [3, 4]]
        [[0, 1, 2], [3, 4, 5]]
        [[0, 1, 2], [3, 4, 5], [6]]
        [[0, 1, 2], [3, 4, 5], [6, 7]]

    """

    def sanity_check(h_bt_str):
        print("______________")
        print("Sanity Check:")
        r = codecs.encode(codecs.decode(h_bt_str, 'hex'),
                          'base64').decode("latin-1")
        print(r)
        print("Result:")
        r = ''.join(map(chr, hex2b64(bytes(h_bt_str))))
        print(r)
        print("---------------")

    hexvals = [
        b"49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6573206d757368726f6f6d21",
        b"49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d"]
    testvals = ["hello", "hello!", "hello!!", "hello!!!", "hello!!!?", ]
    testvals = [codecs.encode(x.encode('latin-1'), 'hex') for x in testvals]
    testvals += hexvals

    for x in testvals:
        # if len(x) % 3 == 2:
        #     import ipdb;            ipdb.set_trace()
        sanity_check(x)


if __name__ == '__main__':
    main()
    # test1()