[tools] Move functions to utilities, add universal CRC16 class

This commit is contained in:
Kuba Szczodrzyński
2022-06-10 11:02:49 +02:00
parent bbf8e0b6b6
commit c3f2ce57f0
6 changed files with 303 additions and 43 deletions

133
tools/util/crc16.py Normal file
View File

@@ -0,0 +1,133 @@
# Copyright (c) Kuba Szczodrzyński 2022-06-02.
from enum import Enum
from typing import List
class CRC16(Enum):
# based on https://crccalc.com/ and https://reveng.sourceforge.io/crc-catalogue/16.htm
ANSI = dict(poly=0x8005, init=0x0000, ref=False, out=0x0000)
ARC = dict(poly=0x8005, init=0x0000, ref=True, out=0x0000)
AUG_CCITT = dict(poly=0x1021, init=0x1D0F, ref=False, out=0x0000)
AUTOSAR = dict(poly=0x1021, init=0xFFFF, ref=False, out=0x0000)
BUYPASS = dict(poly=0x8005, init=0x0000, ref=False, out=0x0000)
CCITT = dict(poly=0x1021, init=0x0000, ref=True, out=0x0000)
CCITT_FALSE = dict(poly=0x1021, init=0xFFFF, ref=False, out=0x0000)
CCITT_TRUE = dict(poly=0x1021, init=0x0000, ref=True, out=0x0000)
CDMA2000 = dict(poly=0xC867, init=0xFFFF, ref=False, out=0x0000)
CMS = dict(poly=0x8005, init=0xFFFF, ref=False, out=0x0000)
CRC_A = dict(poly=0x1021, init=0xC6C6, ref=True, out=0x0000)
CRC_B = dict(poly=0x1021, init=0xFFFF, ref=True, out=0xFFFF)
DARC = dict(poly=0x1021, init=0xFFFF, ref=False, out=0xFFFF)
DDS_110 = dict(poly=0x8005, init=0x800D, ref=False, out=0x0000)
DECT_R = dict(poly=0x0589, init=0x0000, ref=False, out=0x0001)
DECT_X = dict(poly=0x0589, init=0x0000, ref=False, out=0x0000)
DNP = dict(poly=0x3D65, init=0x0000, ref=True, out=0xFFFF)
EN_13757 = dict(poly=0x3D65, init=0x0000, ref=False, out=0xFFFF)
EPC = dict(poly=0x1021, init=0xFFFF, ref=False, out=0xFFFF)
EPC_C1G2 = dict(poly=0x1021, init=0xFFFF, ref=False, out=0xFFFF)
GENIBUS = dict(poly=0x1021, init=0xFFFF, ref=False, out=0xFFFF)
GSM = dict(poly=0x1021, init=0x0000, ref=False, out=0xFFFF)
I_CODE = dict(poly=0x1021, init=0xFFFF, ref=False, out=0xFFFF)
IBM = dict(poly=0x8005, init=0x0000, ref=False, out=0x0000)
IBM_3740 = dict(poly=0x1021, init=0xFFFF, ref=False, out=0x0000)
IBM_SDLC = dict(poly=0x1021, init=0xFFFF, ref=True, out=0xFFFF)
IEC_61158_2 = dict(poly=0x1DCF, init=0xFFFF, ref=False, out=0xFFFF)
ISO_14443_3_A = dict(poly=0x1021, init=0xC6C6, ref=True, out=0x0000)
ISO_14443_3_B = dict(poly=0x1021, init=0xFFFF, ref=True, out=0xFFFF)
ISO_HDLC = dict(poly=0x1021, init=0xFFFF, ref=True, out=0xFFFF)
KERMIT = dict(poly=0x1021, init=0x0000, ref=True, out=0x0000)
LHA = dict(poly=0x8005, init=0x0000, ref=True, out=0x0000)
LJ1200 = dict(poly=0x6F63, init=0x0000, ref=False, out=0x0000)
M17 = dict(poly=0x5935, init=0xFFFF, ref=False, out=0x0000)
MAXIM = dict(poly=0x8005, init=0x0000, ref=True, out=0xFFFF)
MCRF4XX = dict(poly=0x1021, init=0xFFFF, ref=True, out=0x0000)
MODBUS = dict(poly=0x8005, init=0xFFFF, ref=True, out=0x0000)
NRSC_5 = dict(poly=0x080B, init=0xFFFF, ref=True, out=0x0000)
OPENSAFETY_A = dict(poly=0x5935, init=0x0000, ref=False, out=0x0000)
OPENSAFETY_B = dict(poly=0x755B, init=0x0000, ref=False, out=0x0000)
PROFIBUS = dict(poly=0x1DCF, init=0xFFFF, ref=False, out=0xFFFF)
RIELLO = dict(poly=0x1021, init=0xB2AA, ref=True, out=0x0000)
SPI_FUJITSU = dict(poly=0x1021, init=0x1D0F, ref=False, out=0x0000)
T10_DIF = dict(poly=0x8BB7, init=0x0000, ref=False, out=0x0000)
TELEDISK = dict(poly=0xA097, init=0x0000, ref=False, out=0x0000)
TMS37157 = dict(poly=0x1021, init=0x89EC, ref=True, out=0x0000)
UMTS = dict(poly=0x8005, init=0x0000, ref=False, out=0x0000)
USB = dict(poly=0x8005, init=0xFFFF, ref=True, out=0xFFFF)
V_41_LSB = dict(poly=0x1021, init=0x0000, ref=True, out=0x0000)
VERIFONE = dict(poly=0x8005, init=0x0000, ref=False, out=0x0000)
X_25 = dict(poly=0x1021, init=0xFFFF, ref=True, out=0xFFFF)
XMODEM = dict(poly=0x1021, init=0x0000, ref=False, out=0x0000)
poly: int
init: int
ref: bool
out: int
table: List[int]
def __init__(self, params: dict) -> None:
super().__init__()
self.poly = params["poly"]
self.init = params["init"]
self.ref = params["ref"]
self.out = params["out"]
self.table = None
if self.ref:
self.poly = self.reverse16(self.poly)
self.init = self.reverse16(self.init)
@staticmethod
def reverse16(num: int) -> int:
out = 0
for i in range(16):
out |= ((num & (1 << i)) >> i) << (15 - i)
return out
def calc(self, data: bytes) -> int:
if self.ref:
self._init_ref()
return self._calc_ref(data)
self._init_std()
return self._calc_std(data)
def _init_std(self):
if self.table:
return
self.table = []
for b in range(256):
crc = b << 8
for _ in range(8):
if crc & 0x8000:
crc <<= 1
crc ^= self.poly
else:
crc <<= 1
self.table.append(crc & 0xFFFF)
def _init_ref(self):
if self.table:
return
self.table = []
for b in range(256):
crc = b
for _ in range(8):
if crc & 0x0001:
crc >>= 1
crc ^= self.poly
else:
crc >>= 1
self.table.append(crc)
def _calc_std(self, data: bytes) -> int:
crc = self.init
for b in data:
b ^= crc // 256
crc = self.table[b] ^ (crc * 256 % 0x10000)
return crc ^ self.out
def _calc_ref(self, data: bytes) -> int:
crc = self.init
for b in data:
b ^= crc % 256
crc = self.table[b] ^ (crc // 256)
return crc ^ self.out

View File

@@ -1,14 +0,0 @@
# Copyright (c) Kuba Szczodrzyński 2022-06-02.
def crc16(data):
# https://gist.github.com/pintoXD/a90e398bba5a1b6c121de4e1265d9a29
crc = 0x0000
for b in data:
crc ^= b
for j in range(0, 8):
if (crc & 0x0001) > 0:
crc = (crc >> 1) ^ 0xA001
else:
crc = crc >> 1
return crc

53
tools/util/fileio.py Normal file
View File

@@ -0,0 +1,53 @@
# Copyright (c) Kuba Szczodrzyński 2022-06-10.
import json
from io import BytesIO
from os.path import dirname, getmtime, isfile, join
from typing import Union
def chname(path: str, name: str) -> str:
"""Change the basename of 'path' to 'name'."""
return join(dirname(path), name)
def chext(path: str, ext: str) -> str:
"""Change the file extension of 'path' to 'ext' (without the dot)."""
return path.rpartition(".")[0] + "." + ext
def isnewer(what: str, than: str) -> bool:
"""Check if 'what' is newer than 'than'.
Returns False if 'what' is not a file.
Returns True if 'than' is not a file.
"""
if not isfile(what):
return False
if not isfile(than):
return True
return getmtime(what) > getmtime(than)
def readbin(file: str) -> bytes:
"""Read a binary file into a bytes object."""
with open(file, "rb") as f:
data = f.read()
return data
def writebin(file: str, data: Union[bytes, BytesIO]):
"""Write data into a binary file."""
with open(file, "wb") as f:
if isinstance(data, BytesIO):
f.write(data.getvalue())
else:
f.write(data)
# same as load_json
def readjson(file: str) -> Union[dict, list]:
"""Read a JSON file into a dict or list."""
with open(file, "r", encoding="utf-8") as f:
return json.load(f)

View File

@@ -2,60 +2,138 @@
def bswap(data: bytes) -> bytes:
"""Reverse the byte array (big-endian <-> little-endian)."""
return bytes(reversed(data))
def betoint(data: bytes) -> int:
"""Convert bytes to big-endian unsigned integer."""
return int.from_bytes(data, byteorder="big")
def letoint(data: bytes) -> int:
"""Convert bytes to little-endian unsigned integer."""
return int.from_bytes(data, byteorder="little")
def betosint(data: bytes) -> int:
"""Convert bytes to big-endian signed integer."""
return int.from_bytes(data, byteorder="big", signed=True)
def letosint(data: bytes) -> int:
"""Convert bytes to little-endian signed integer."""
return int.from_bytes(data, byteorder="little", signed=True)
def inttobe32(data: int) -> bytes:
"""Convert unsigned integer to 32 bits, big-endian."""
return data.to_bytes(length=4, byteorder="big")
def inttole32(data: int) -> bytes:
"""Convert unsigned integer to 32 bits, little-endian."""
return data.to_bytes(length=4, byteorder="little")
def inttobe24(data: int) -> bytes:
"""Convert unsigned integer to 24 bits, big-endian."""
return data.to_bytes(length=3, byteorder="big")
def inttole24(data: int) -> bytes:
"""Convert unsigned integer to 24 bits, little-endian."""
return data.to_bytes(length=3, byteorder="little")
def inttobe16(data: int) -> bytes:
"""Convert unsigned integer to 16 bits, big-endian."""
return data.to_bytes(length=2, byteorder="big")
def inttole16(data: int) -> bytes:
"""Convert unsigned integer to 16 bits, little-endian."""
return data.to_bytes(length=2, byteorder="little")
def intto8(data: int) -> bytes:
"""Convert unsigned integer to 8 bits."""
return data.to_bytes(length=1, byteorder="big")
def sinttobe32(data: int) -> bytes:
"""Convert signed integer to 32 bits, big-endian."""
return data.to_bytes(length=4, byteorder="big", signed=True)
def sinttole32(data: int) -> bytes:
"""Convert signed integer to 32 bits, little-endian."""
return data.to_bytes(length=4, byteorder="little", signed=True)
def sinttobe24(data: int) -> bytes:
"""Convert signed integer to 24 bits, big-endian."""
return data.to_bytes(length=3, byteorder="big", signed=True)
def sinttole24(data: int) -> bytes:
"""Convert signed integer to 24 bits, little-endian."""
return data.to_bytes(length=3, byteorder="little", signed=True)
def sinttobe16(data: int) -> bytes:
"""Convert signed integer to 16 bits, big-endian."""
return data.to_bytes(length=2, byteorder="big", signed=True)
def sinttole16(data: int) -> bytes:
"""Convert signed integer to 16 bits, little-endian."""
return data.to_bytes(length=2, byteorder="little", signed=True)
def sintto8(data: int) -> bytes:
"""Convert signed integer to 8 bits."""
return data.to_bytes(length=1, byteorder="little", signed=True)
def align_up(x: int, n: int) -> int:
"""Return x aligned up to block size of n."""
return int((x - 1) // n + 1) * n
def align_down(x: int, n: int) -> int:
"""Return 'x' aligned down to block size of 'n'."""
return int(x // n) * n
def pad_up(x: int, n: int) -> int:
"""Return how many bytes of padding is needed to align 'x'
up to block size of 'n'."""
return n - (x % n)
def pad_data(data: bytes, n: int, char: int) -> bytes:
"""Add 'char'-filled padding to 'data' to align to a 'n'-sized block."""
if len(data) % n == 0:
return data
return data + (bytes([char]) * pad_up(len(data), n))
def uint8(val):
"""Get only the least-significant 8 bits of the value."""
return val & 0xFF
def uint16(val):
"""Get only the least-significant 16 bits of the value."""
return val & 0xFFFF
def uint32(val):
"""Get only the least-significant 32 bits of the value."""
return val & 0xFFFFFFFF
def uintmax(bits: int) -> int:
"""Get maximum integer size for given bit width."""
return (2**bits) - 1