blob: 635f7d37759ea27c3e87342f5d92db047479e35e [file] [log] [blame]
Chris Liechti3e02f702015-12-16 23:06:04 +01001#! python
2#
3# This is a codec to create and decode hexdumps with spaces between characters. used by miniterm.
4#
5# This file is part of pySerial. https://github.com/pyserial/pyserial
Chris Liechti4e34c4c2016-02-19 23:54:14 +01006# (C) 2015-2016 Chris Liechti <cliechti@gmx.net>
Chris Liechti3e02f702015-12-16 23:06:04 +01007#
8# SPDX-License-Identifier: BSD-3-Clause
Chris Liechtic0c660a2015-08-25 00:55:51 +02009"""\
10Python 'hex' Codec - 2-digit hex with spaces content transfer encoding.
Chris Liechti4e34c4c2016-02-19 23:54:14 +010011
12Encode and decode may be a bit missleading at first sight...
13
14The textual representation is a hex dump: e.g. "40 41"
15The "encoded" data of this is the binary form, e.g. b"@A"
16
17Therefore decoding is binary to text and thus converting binary data to hex dump.
18
Chris Liechtic0c660a2015-08-25 00:55:51 +020019"""
20
21import codecs
22import serial
23
Chris Liechti7bb26e42016-03-08 22:59:48 +010024
25try:
26 unicode
27except (NameError, AttributeError):
28 unicode = str # for Python 3, pylint: disable=redefined-builtin,invalid-name
29
30
Chris Liechtic0c660a2015-08-25 00:55:51 +020031HEXDIGITS = '0123456789ABCDEF'
32
Chris Liechtic0c660a2015-08-25 00:55:51 +020033
Chris Liechti92df95a2016-02-09 23:30:37 +010034# Codec APIs
Chris Liechti033f17c2015-08-30 21:28:04 +020035
Chris Liechti4e34c4c2016-02-19 23:54:14 +010036def hex_encode(data, errors='strict'):
Chris Liechti7bb26e42016-03-08 22:59:48 +010037 """'40 41 42' -> b'@ab'"""
Chris Liechti4e34c4c2016-02-19 23:54:14 +010038 return (serial.to_bytes([int(h, 16) for h in data.split()]), len(data))
Chris Liechtic0c660a2015-08-25 00:55:51 +020039
Chris Liechti033f17c2015-08-30 21:28:04 +020040
Chris Liechti4e34c4c2016-02-19 23:54:14 +010041def hex_decode(data, errors='strict'):
Chris Liechti7bb26e42016-03-08 22:59:48 +010042 """b'@ab' -> '40 41 42'"""
43 return (unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data))), len(data))
Chris Liechtic0c660a2015-08-25 00:55:51 +020044
Chris Liechti033f17c2015-08-30 21:28:04 +020045
Chris Liechtic0c660a2015-08-25 00:55:51 +020046class Codec(codecs.Codec):
Chris Liechti4e34c4c2016-02-19 23:54:14 +010047 def encode(self, data, errors='strict'):
Chris Liechti7bb26e42016-03-08 22:59:48 +010048 """'40 41 42' -> b'@ab'"""
Chris Liechti4e34c4c2016-02-19 23:54:14 +010049 return serial.to_bytes([int(h, 16) for h in data.split()])
Chris Liechti033f17c2015-08-30 21:28:04 +020050
Chris Liechti4e34c4c2016-02-19 23:54:14 +010051 def decode(self, data, errors='strict'):
Chris Liechti7bb26e42016-03-08 22:59:48 +010052 """b'@ab' -> '40 41 42'"""
53 return unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data)))
Chris Liechtic0c660a2015-08-25 00:55:51 +020054
Chris Liechti033f17c2015-08-30 21:28:04 +020055
Chris Liechtic0c660a2015-08-25 00:55:51 +020056class IncrementalEncoder(codecs.IncrementalEncoder):
Chris Liechti4e34c4c2016-02-19 23:54:14 +010057 """Incremental hex encoder"""
Chris Liechtic0c660a2015-08-25 00:55:51 +020058
59 def __init__(self, errors='strict'):
60 self.errors = errors
61 self.state = 0
62
63 def reset(self):
64 self.state = 0
65
66 def getstate(self):
67 return self.state
68
69 def setstate(self, state):
70 self.state = state
71
Chris Liechti4e34c4c2016-02-19 23:54:14 +010072 def encode(self, data, final=False):
73 """\
74 Incremental encode, keep track of digits and emit a byte when a pair
75 of hex digits is found. The space is optional unless the error
76 handling is defined to be 'strict'.
77 """
Chris Liechtic0c660a2015-08-25 00:55:51 +020078 state = self.state
79 encoded = []
Chris Liechti4e34c4c2016-02-19 23:54:14 +010080 for c in data.upper():
Chris Liechtic0c660a2015-08-25 00:55:51 +020081 if c in HEXDIGITS:
82 z = HEXDIGITS.index(c)
83 if state:
84 encoded.append(z + (state & 0xf0))
85 state = 0
86 else:
87 state = 0x100 + (z << 4)
88 elif c == ' ': # allow spaces to separate values
89 if state and self.errors == 'strict':
90 raise UnicodeError('odd number of hex digits')
91 state = 0
92 else:
93 if self.errors == 'strict':
94 raise UnicodeError('non-hex digit found: %r' % c)
95 self.state = state
96 return serial.to_bytes(encoded)
97
Chris Liechti033f17c2015-08-30 21:28:04 +020098
Chris Liechtic0c660a2015-08-25 00:55:51 +020099class IncrementalDecoder(codecs.IncrementalDecoder):
Chris Liechti4e34c4c2016-02-19 23:54:14 +0100100 """Incremental decoder"""
101 def decode(self, data, final=False):
Chris Liechti7bb26e42016-03-08 22:59:48 +0100102 return unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data)))
Chris Liechtic0c660a2015-08-25 00:55:51 +0200103
Chris Liechti033f17c2015-08-30 21:28:04 +0200104
Chris Liechtic0c660a2015-08-25 00:55:51 +0200105class StreamWriter(Codec, codecs.StreamWriter):
Chris Liechti4e34c4c2016-02-19 23:54:14 +0100106 """Combination of hexlify codec and StreamWriter"""
Chris Liechtic0c660a2015-08-25 00:55:51 +0200107
Chris Liechti033f17c2015-08-30 21:28:04 +0200108
Chris Liechtic0c660a2015-08-25 00:55:51 +0200109class StreamReader(Codec, codecs.StreamReader):
Chris Liechti4e34c4c2016-02-19 23:54:14 +0100110 """Combination of hexlify codec and StreamReader"""
Chris Liechtic0c660a2015-08-25 00:55:51 +0200111
Chris Liechtic0c660a2015-08-25 00:55:51 +0200112
Chris Liechtic0c660a2015-08-25 00:55:51 +0200113def getregentry():
Chris Liechti4e34c4c2016-02-19 23:54:14 +0100114 """encodings module API"""
Chris Liechtic0c660a2015-08-25 00:55:51 +0200115 return codecs.CodecInfo(
116 name='hexlify',
117 encode=hex_encode,
118 decode=hex_decode,
119 incrementalencoder=IncrementalEncoder,
120 incrementaldecoder=IncrementalDecoder,
121 streamwriter=StreamWriter,
122 streamreader=StreamReader,
Chris Liechti7bb26e42016-03-08 22:59:48 +0100123 #~ _is_text_encoding=True,
Chris Liechtic0c660a2015-08-25 00:55:51 +0200124 )