2021-04-01 20:02:35 +02:00
|
|
|
"""Helper functions and classes."""
|
2021-04-01 17:19:16 +02:00
|
|
|
import typing as t
|
2020-09-17 02:35:09 +02:00
|
|
|
|
|
|
|
|
2021-04-02 01:32:36 +02:00
|
|
|
class CRC16:
|
2021-04-01 20:02:35 +02:00
|
|
|
"""Helps with CRC-16 calculation.
|
2020-09-17 02:35:09 +02:00
|
|
|
|
2021-04-01 20:02:35 +02:00
|
|
|
CRC tables are cached for performance.
|
|
|
|
"""
|
2020-09-17 02:35:09 +02:00
|
|
|
|
2021-04-05 19:02:56 +02:00
|
|
|
_cache: t.Dict[int, t.List[int]] = {}
|
2020-09-20 11:16:49 +02:00
|
|
|
|
2021-04-01 20:02:35 +02:00
|
|
|
@classmethod
|
2021-04-05 19:02:56 +02:00
|
|
|
def get_table(cls, polynomial: int) -> t.List[int]:
|
2021-04-02 01:32:36 +02:00
|
|
|
"""Return the CRC-16 table for a polynomial."""
|
2021-04-01 20:02:35 +02:00
|
|
|
try:
|
|
|
|
crc_table = cls._cache[polynomial]
|
|
|
|
except KeyError:
|
|
|
|
crc_table = []
|
|
|
|
for dividend in range(0, 256):
|
|
|
|
remainder = dividend
|
|
|
|
for _ in range(0, 8):
|
|
|
|
if remainder & 1:
|
|
|
|
remainder = remainder >> 1 ^ polynomial
|
|
|
|
else:
|
|
|
|
remainder = remainder >> 1
|
|
|
|
crc_table.append(remainder)
|
|
|
|
cls._cache[polynomial] = crc_table
|
2021-04-02 01:32:36 +02:00
|
|
|
return crc_table
|
2021-04-01 20:02:35 +02:00
|
|
|
|
2021-04-02 01:32:36 +02:00
|
|
|
@classmethod
|
|
|
|
def calculate(
|
|
|
|
cls,
|
|
|
|
sequence: t.Sequence[int],
|
|
|
|
polynomial: int = 0xA001, # CRC-16-ANSI.
|
|
|
|
init_value: int = 0xFFFF,
|
|
|
|
) -> int:
|
|
|
|
"""Calculate the CRC-16 of a sequence of integers."""
|
|
|
|
crc_table = cls.get_table(polynomial)
|
2021-04-01 20:02:35 +02:00
|
|
|
crc = init_value
|
|
|
|
for item in sequence:
|
|
|
|
crc = crc >> 8 ^ crc_table[(crc ^ item) & 0xFF]
|
|
|
|
return crc
|