Skip to content

Commit bc4675e

Browse files
ES-Alexanderpatrickelectric
authored andcommitted
pingmessage: [performance] break parser logic into a state-indexed tuple of callbacks
Swaps up to 11 conditionals per byte with a consistent single tuple index and function call.
1 parent 721e6ac commit bc4675e

1 file changed

Lines changed: 94 additions & 63 deletions

File tree

brping/pingmessage.py

Lines changed: 94 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -294,84 +294,115 @@ class PingParser(object):
294294

295295
def __init__(self):
296296
self.buf = bytearray()
297-
self.state = PingParser.WAIT_START
298-
self.payload_length = 0 # payload length remaining to be parsed for the message currently being parsed
299-
self.message_id = 0 # message id of the message currently being parsed
297+
self.state = self.WAIT_START
298+
self.payload_length = 0 # remaining for the message currently being parsed
299+
self.message_id = 0 # of the message currently being parsed
300300
self.errors = 0
301301
self.parsed = 0
302-
self.rx_msg = None # most recently parsed message
302+
self.rx_msg = None # most recently parsed message
303303

304-
# Feed the parser a single byte
305-
# Returns the current parse state
306-
# If the byte fed completes a valid message, return PingParser.NEW_MESSAGE
307-
# The decoded message will be available in the self.rx_msg attribute until a new message is decoded
308-
def parse_byte(self, msg_byte):
309-
if type(msg_byte) != int:
310-
msg_byte = ord(msg_byte)
311-
# print("byte: %d, state: %d, rem: %d, id: %d" % (msg_byte, self.state, self.payload_length, self.message_id))
312-
if self.state == PingParser.WAIT_START:
313-
self.buf = bytearray()
314-
if msg_byte == ord('B'):
315-
self.buf.append(msg_byte)
316-
self.state += 1
317-
elif self.state == PingParser.WAIT_HEADER:
318-
if msg_byte == ord('R'):
319-
self.buf.append(msg_byte)
320-
self.state += 1
321-
else:
322-
self.state = PingParser.WAIT_START
323-
elif self.state == PingParser.WAIT_LENGTH_L:
324-
self.payload_length = msg_byte
325-
self.buf.append(msg_byte)
326-
self.state += 1
327-
elif self.state == PingParser.WAIT_LENGTH_H:
328-
self.payload_length = (msg_byte << 8) | self.payload_length
329-
self.buf.append(msg_byte)
330-
self.state += 1
331-
elif self.state == PingParser.WAIT_MSG_ID_L:
332-
self.message_id = msg_byte
333-
self.buf.append(msg_byte)
334-
self.state += 1
335-
elif self.state == PingParser.WAIT_MSG_ID_H:
336-
self.message_id = (msg_byte << 8) | self.message_id
304+
def wait_start(self, msg_byte):
305+
self.buf = bytearray()
306+
if msg_byte == ord('B'):
337307
self.buf.append(msg_byte)
338308
self.state += 1
339-
elif self.state == PingParser.WAIT_SRC_ID:
309+
310+
def wait_header(self, msg_byte):
311+
if msg_byte == ord('R'):
340312
self.buf.append(msg_byte)
341313
self.state += 1
342-
elif self.state == PingParser.WAIT_DST_ID:
343-
self.buf.append(msg_byte)
314+
else:
315+
self.state = self.WAIT_START
316+
317+
def wait_length_l(self, msg_byte):
318+
self.payload_length = msg_byte
319+
self.buf.append(msg_byte)
320+
self.state += 1
321+
322+
def wait_length_h(self, msg_byte):
323+
self.payload_length |= (msg_byte << 8)
324+
self.buf.append(msg_byte)
325+
self.state += 1
326+
327+
def wait_msg_id_l(self, msg_byte):
328+
self.message_id = msg_byte
329+
self.buf.append(msg_byte)
330+
self.state += 1
331+
332+
def wait_msg_id_h(self, msg_byte):
333+
self.message_id |= (msg_byte << 8)
334+
self.buf.append(msg_byte)
335+
self.state += 1
336+
337+
def wait_src_id(self, msg_byte):
338+
self.buf.append(msg_byte)
339+
self.state += 1
340+
341+
def wait_dst_id(self, msg_byte):
342+
self.buf.append(msg_byte)
343+
self.state += 1
344+
if self.payload_length == 1: # no payload bytes -> skip waiting
344345
self.state += 1
345-
if self.payload_length == 0: # no payload bytes
346-
self.state += 1
347-
elif self.state == PingParser.WAIT_PAYLOAD:
348-
self.buf.append(msg_byte)
349-
self.payload_length -= 1
350-
if self.payload_length == 0:
351-
self.state += 1
352-
elif self.state == PingParser.WAIT_CHECKSUM_L:
353-
self.buf.append(msg_byte)
346+
347+
def wait_payload(self, msg_byte):
348+
self.buf.append(msg_byte)
349+
self.payload_length -= 1
350+
if self.payload_length == 0: # no payload bytes remaining -> stop waiting:
354351
self.state += 1
355-
elif self.state == PingParser.WAIT_CHECKSUM_H:
356-
self.buf.append(msg_byte)
357-
self.rx_msg = PingMessage(msg_data=self.buf)
358352

359-
# print(self.rx_msg)
353+
def wait_checksum_l(self, msg_byte):
354+
self.buf.append(msg_byte)
355+
self.state += 1
360356

361-
self.state = PingParser.WAIT_START
362-
self.payload_length = 0
363-
self.message_id = 0
357+
def wait_checksum_h(self, msg_byte):
358+
self.state = self.WAIT_START
359+
self.payload_length = 0
360+
self.message_id = 0
364361

365-
if self.rx_msg.verify_checksum():
366-
self.parsed += 1
367-
return PingParser.NEW_MESSAGE
368-
else:
369-
# TODO add/return error state
370-
print("parse error")
371-
self.errors += 1
362+
self.buf.append(msg_byte)
363+
self.rx_msg = PingMessage(msg_data=self.buf)
364+
365+
if self.rx_msg.verify_checksum():
366+
self.parsed += 1
367+
return self.NEW_MESSAGE
368+
else:
369+
# TODO add/return error state
370+
print("parse error")
371+
self.errors += 1
372372

373373
return self.state
374374

375+
def parse_byte(self, msg_byte):
376+
""" Returns the current parse state after feeding the parser a single byte.
377+
378+
'msg_byte' is the byte to parse.
379+
If it completes a valid message, returns PingParser.NEW_MESSAGE.
380+
The decoded PingMessage will be available in the self.rx_msg attribute
381+
until a new message is decoded.
382+
383+
"""
384+
# Apply the relevant parsing method for the current state.
385+
# (offset by 1 because NEW_MESSAGE isn't processed - start at WAIT_START)
386+
result = self._PARSE_BYTE[self.state - 1](self, msg_byte)
387+
388+
return self.state if result is None else result
389+
390+
# Tuple of parsing methods, in order of parser state
391+
# at bottom because otherwise methods won't be defined
392+
_PARSE_BYTE = (
393+
wait_start,
394+
wait_header,
395+
wait_length_l,
396+
wait_length_h,
397+
wait_msg_id_l,
398+
wait_msg_id_h,
399+
wait_src_id,
400+
wait_dst_id,
401+
wait_payload,
402+
wait_checksum_l,
403+
wait_checksum_h,
404+
)
405+
375406

376407
if __name__ == "__main__":
377408
# Hand-written data buffers for testing and verification

0 commit comments

Comments
 (0)