#!/usr/bin/python3 def make_nibbles(dec, high): """Translates half of the dec digits into BCD nibble values Arguments: dec -- a bytes object of decimal digits. Length has to be a multiple of 2 high -- defines if the high or low nibble per byte should be calculated Return value: A bytes object representing the nibbles. The nibble that is not calculated (per byte) is set to 0 """ assert(not (len(dec) & 1)) # build a translation table from decimal digits to BCD nibble values translate_from = b'0123456789' bitshift = 4 if high else 0 translate_to = bytes(num << bitshift for num in range(10)) # TODO: This table should only be calculated once and reused afterwards table = bytes.maketrans(translate_from, translate_to) byteshift = 0 if high else 1 bytes_ = dec[byteshift::2] return bytes_.translate(table) def int2bcd(value): """Encodes an integer into a bytes object of packed BCD values""" dec = bytes(str(value), 'ascii') padded_len = (len(dec) + 1) >> 1 << 1 padded_dec = dec.rjust(padded_len, b'0') nibbles_low = make_nibbles(padded_dec, high=False) nibbles_high = make_nibbles(padded_dec, high=True) return bytes(high | low for high, low in zip(nibbles_high, nibbles_low)) def bcd2int(bcd): lowmask = (1 << 4) - 1 bytevals = [(byte_ & lowmask) + 10 * (byte_ >> 4) for byte_ in bcd] return sum(byteval * 100**pow_ for pow_, byteval in enumerate(reversed(bytevals))) def main(): value = int('42132801305034541=4101210013390'.replace("=", 'D'), 16) bcd = int2bcd(value) intval = bcd2int(bcd) print("%d (%d) -> %s (%d) = %d" % (value, len(str(value)), bcd, len(bcd), intval)) if __name__ == '__main__': main()