#! python # # This is a codec to create and decode hexdumps with spaces between characters. used by miniterm. # # This file is part of pySerial. https://github.com/pyserial/pyserial # (C) 2015-2016 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause """\ Python 'hex' Codec - 2-digit hex with spaces content transfer encoding. Encode and decode may be a bit missleading at first sight... The textual representation is a hex dump: e.g. "40 41" The "encoded" data of this is the binary form, e.g. b"@A" Therefore decoding is binary to text and thus converting binary data to hex dump. """ from __future__ import absolute_import import codecs import serial try: unicode except (NameError, AttributeError): unicode = str # for Python 3, pylint: disable=redefined-builtin,invalid-name HEXDIGITS = '0123456789ABCDEF' # Codec APIs def hex_encode(data, errors='strict'): """'40 41 42' -> b'@ab'""" return (serial.to_bytes([int(h, 16) for h in data.split()]), len(data)) def hex_decode(data, errors='strict'): """b'@ab' -> '40 41 42'""" return (unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data))), len(data)) class Codec(codecs.Codec): def encode(self, data, errors='strict'): """'40 41 42' -> b'@ab'""" return serial.to_bytes([int(h, 16) for h in data.split()]) def decode(self, data, errors='strict'): """b'@ab' -> '40 41 42'""" return unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data))) class IncrementalEncoder(codecs.IncrementalEncoder): """Incremental hex encoder""" def __init__(self, errors='strict'): self.errors = errors self.state = 0 def reset(self): self.state = 0 def getstate(self): return self.state def setstate(self, state): self.state = state def encode(self, data, final=False): """\ Incremental encode, keep track of digits and emit a byte when a pair of hex digits is found. The space is optional unless the error handling is defined to be 'strict'. """ state = self.state encoded = [] for c in data.upper(): if c in HEXDIGITS: z = HEXDIGITS.index(c) if state: encoded.append(z + (state & 0xf0)) state = 0 else: state = 0x100 + (z << 4) elif c == ' ': # allow spaces to separate values if state and self.errors == 'strict': raise UnicodeError('odd number of hex digits') state = 0 else: if self.errors == 'strict': raise UnicodeError('non-hex digit found: {!r}'.format(c)) self.state = state return serial.to_bytes(encoded) class IncrementalDecoder(codecs.IncrementalDecoder): """Incremental decoder""" def decode(self, data, final=False): return unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data))) class StreamWriter(Codec, codecs.StreamWriter): """Combination of hexlify codec and StreamWriter""" class StreamReader(Codec, codecs.StreamReader): """Combination of hexlify codec and StreamReader""" def getregentry(): """encodings module API""" return codecs.CodecInfo( name='hexlify', encode=hex_encode, decode=hex_decode, incrementalencoder=IncrementalEncoder, incrementaldecoder=IncrementalDecoder, streamwriter=StreamWriter, streamreader=StreamReader, #~ _is_text_encoding=True, )