Yet-Another CRC Calculator
Project description
Yet-Another CRC Calculator (yacrc)
yacrc is a Python package that provides comprehensive support for CRC (Cyclic Redundancy Check) calculations. It offers flexible and optimized algorithms for CRC calculation on various data types. Additionally, the package includes a catalog of predefined CRC models covering standard and widely used configurations.
Features
- Flexible Input Types: Supports
bytes,bytearray,List[int], and binarystrinputs. The default data width is8 bits, but it can be configured to any positive number. - CRC Model Catalog: Includes a catalog of more than 100 predefined CRC models based on Greg Cook's "Catalogue of Parametrised CRC Algorithms" [2].
- Customizable Parameters: Allows customization of CRC model parameters (e.g., CRC width, polynomial, initial value, input and output reflection, final XOR operation, etc.).
- Optimized Calculation: Offers both bit-by-bit and table-driven CRC calculation for speed and flexibility. The table-driven approach can be enabled when the data width is
16 bitsor less. - Polynomial Division: Provides a method to calculate CRC directly by mathematical definition, i.e., via a polynomial division in CRC arithmetic with detailed log of performed calculation steps.
Quick Start
This section provides a brief guide to quickly get started with the yacrc package.
Install the yacrc package via pip as follows:
pip install yacrc
Import the package, find the appropriate CRC model in the catalog, and calculate the CRC value for a buffer:
>>> from yacrc import CRC16
>>> crc = CRC16.MODBUS.crc(b'123456789')
>>> hex(crc)
'0x4b37'
Getting Started
This section provides detailed instructions on how to create and use CRC calculation objects with the yacrc package. You can either use predefined models from the catalog or customize your own CRC parameters. Follow the steps below to get started with CRC calculations.
Creation of CRC calculation objects
To perform CRC calculations, you need to create CRC calculation objects. These objects can be created either by using predefined models from the catalog or by directly instantiating the CRC class. The constructor takes the following input arguments:
width (int)- CRC width (number of bits).poly (int)- CRC poly, i.e., polynomial without implied leading bit'1'.init (int)- CRC shift register initial value. Defaults to0.refin (bool)- Input data reflection. Defaults toFalse.refout (bool)- Output data reflection. Defaults toFalse.xorout (int)- Operand for the final XOR operation. Defaults to0.check (int | None)- CRC value forb'123456789'. Defaults toNone.residue (int | None)- Residue value. Defaults toNone.name (str)- CRC model name. Defaults to''.alias (str)- CRC model alias(es). Defaults to''.data (int)- Data width (number of bits). Defaults to8.reverse (bool)- CRC is appended in reverse order. Defaults toFalse.optimized (bool | None)- Optimized CRC calculations. Defaults toNone.
The only mandatory parameters are the width and poly. Refer to [1] for more information on how different parameters influence CRC calculation. The built-in catalog classes (CRC3, CRC4, ..., CRC82) contain more than 100 predefined CRC models [2].
CRC calculation objects can also be created by overriding parameters of any CRC model from the built-in catalog:
>>> from yacrc import CRC16
>>> obj = CRC16.MODBUS(xorout=0xFFFF)
CRC-16/USB has the same set of parameters
>>> obj is CRC16.MODBUS
False
>>> obj is CRC16.USB
False
>>> obj == CRC16.USB
True
>>> obj
CRC-16/MODBUS*
It is possible to override any parameter except the CRC width. If the name is not specified, it is formed by adding a trailing '*' to the existing CRC model name. When the new set of parameters matches an existing CRC model in the catalog, the appropriate message will be displayed.
Check and residue parameters
The check parameter is defined as a CRC value for the test message b'123456789'. The residue parameter is defined as the CRC value before the final XOR, calculated over a buffer with its CRC appended at the end.
When the check and residue parameters are provided as inputs to the CRC constructor, their values are compared against the expected values. The check parameter should not be specified for data widths other than 8 bits.
>>> from yacrc import CRC16
>>> obj = CRC16.MODBUS
>>> obj.crc(b'123456789') == obj.check
True
>>> appended = obj.append([1])
>>> residue = obj.crc(appended) ^ obj.xorout
>>> residue == obj.residue
True
Note that the crc() method returns a CRC value after the final XOR operation. To undo the final XOR, the CRC value should be XORed once more with the xorout parameter.
Unoptimized and optimized CRC calculation
The CRC calculation algorithm can be optimized for calculation speed by precomputing a table of CRC values for each possible data value in an input buffer. For a data width of data bits, the CRC lookup table will contain 2**data elements. Due to the memory requirements, the table-based optimization is limited to data widths of 16 bits or less. The table() and table_2d() methods provide access to the CRC lookup table.
>>> from yacrc import CRC4
>>> obj = CRC4.G_704(data=8)
>>> print(obj.table_2d(cols=16))
0x0, 0x7, 0xE, 0x9, 0x5, 0x2, 0xB, 0xC, 0xA, 0xD, 0x4, 0x3, 0xF, 0x8, 0x1, 0x6,
0xD, 0xA, 0x3, 0x4, 0x8, 0xF, 0x6, 0x1, 0x7, 0x0, 0x9, 0xE, 0x2, 0x5, 0xC, 0xB,
0x3, 0x4, 0xD, 0xA, 0x6, 0x1, 0x8, 0xF, 0x9, 0xE, 0x7, 0x0, 0xC, 0xB, 0x2, 0x5,
0xE, 0x9, 0x0, 0x7, 0xB, 0xC, 0x5, 0x2, 0x4, 0x3, 0xA, 0xD, 0x1, 0x6, 0xF, 0x8,
0x6, 0x1, 0x8, 0xF, 0x3, 0x4, 0xD, 0xA, 0xC, 0xB, 0x2, 0x5, 0x9, 0xE, 0x7, 0x0,
0xB, 0xC, 0x5, 0x2, 0xE, 0x9, 0x0, 0x7, 0x1, 0x6, 0xF, 0x8, 0x4, 0x3, 0xA, 0xD,
0x5, 0x2, 0xB, 0xC, 0x0, 0x7, 0xE, 0x9, 0xF, 0x8, 0x1, 0x6, 0xA, 0xD, 0x4, 0x3,
0x8, 0xF, 0x6, 0x1, 0xD, 0xA, 0x3, 0x4, 0x2, 0x5, 0xC, 0xB, 0x7, 0x0, 0x9, 0xE,
0xC, 0xB, 0x2, 0x5, 0x9, 0xE, 0x7, 0x0, 0x6, 0x1, 0x8, 0xF, 0x3, 0x4, 0xD, 0xA,
0x1, 0x6, 0xF, 0x8, 0x4, 0x3, 0xA, 0xD, 0xB, 0xC, 0x5, 0x2, 0xE, 0x9, 0x0, 0x7,
0xF, 0x8, 0x1, 0x6, 0xA, 0xD, 0x4, 0x3, 0x5, 0x2, 0xB, 0xC, 0x0, 0x7, 0xE, 0x9,
0x2, 0x5, 0xC, 0xB, 0x7, 0x0, 0x9, 0xE, 0x8, 0xF, 0x6, 0x1, 0xD, 0xA, 0x3, 0x4,
0xA, 0xD, 0x4, 0x3, 0xF, 0x8, 0x1, 0x6, 0x0, 0x7, 0xE, 0x9, 0x5, 0x2, 0xB, 0xC,
0x7, 0x0, 0x9, 0xE, 0x2, 0x5, 0xC, 0xB, 0xD, 0xA, 0x3, 0x4, 0x8, 0xF, 0x6, 0x1,
0x9, 0xE, 0x7, 0x0, 0xC, 0xB, 0x2, 0x5, 0x3, 0x4, 0xD, 0xA, 0x6, 0x1, 0x8, 0xF,
0x4, 0x3, 0xA, 0xD, 0x1, 0x6, 0xF, 0x8, 0xE, 0x9, 0x0, 0x7, 0xB, 0xC, 0x5, 0x2
When the optimize parameter is not specified, CRC calculation optimization is enabled by default for data widths of 8 bits or less. Additionally, the optimization() method lets users to enable or disable CRC calculation optimization.
>>> from yacrc import CRC16
>>> obj = CRC16.MODBUS
>>> obj_e = obj.optimization(True)
>>> obj_e is obj
True
>>> obj_e.crc(b'1')
38014
>>> obj_d = obj.optimization(False)
>>> obj_d is obj
True
>>> obj_d.crc(b'1')
38014
>>> obj_x = obj(optimized=False)
>>> obj_x is obj
False
>>> obj_x.crc(b'1')
38014
Depending on the CRC width, data width, and buffer size, the optimized CRC calculation algorithms are 3-5x faster than unoptimized algorithms.
>>> import timeit
>>> main = "obj.crc(buffer)"
>>> setup = """
... from yacrc import CRC16
... buffer = list(range({}))
... obj = CRC16.MODBUS.optimization({})
... """
>>> timeit.timeit(main, setup.format(100, True), number=100000)
1.7694603000127245
>>> timeit.timeit(main, setup.format(100, False), number=100000)
6.940266099991277
Polynomial division in CRC arithmetic
The crc_steps() method calculates a CRC value directly by mathematical definition, i.e., via a polynomial division in CRC arithmetic. In addition to the calculated CRC value, it also outputs a detailed log of the performed calculation steps, which can be used to visualize the CRC calculation process. It should be noted that this calculation method is 40-50x slower than the unoptimized algorithm.
>>> from yacrc import CRC4
>>> obj = CRC4.G_704
>>> crc, steps = obj.crc_steps(b'0')
>>> hex(crc)
'0xe'
>>> print(steps)
======================
MODEL: CRC-4/G-704
======================
MESSAGE 00110000
----------------------
REFIN 00001100
AUGMENT 000011000000
POLY 10011...
-----...
0101100.
POLY 10011..
-----..
00101..
POLY 10011
-----
00111
----
REFOUT 1110
----------------------
CRC = 0xE
======================
Different data types and data widths
The package supports bytes, bytearray, List[int], and binary str inputs. This flexibility allows users to calculate CRC values for data in various formats. The following code shows how to calculate a CRC value for the {0x31, 0x32} sequence of bytes in different representations.
>>> from yacrc import CRC16
>>> obj = CRC16.MODBUS
>>> crc_1 = obj.crc(b'12')
>>> crc_2 = obj.crc(bytearray(b'12'))
>>> crc_3 = obj.crc([0x31, 0x32])
>>> crc_4 = obj.crc('0011000100110010')
>>> crc_1 == crc_2 == crc_3 == crc_4
True
>>> hex(crc_1)
'0xf595'
The data parameter defines how many lower bits are relevant in each element of an integer buffer (bytes, bytearray, or List[int]). For binary string (str) buffers, their length must be a multiple of data. The data width will be 8 bits in most practical cases, which is the default setting.
>>> from yacrc import CRC16
>>> obj = CRC16.MODBUS(data=4)
>>> obj.crc([1, 2]) == obj.crc('00010010')
True
>>> obj.crc([0, 1, 0, 2]) == obj.crc('0000000100000010')
True
>>> obj.crc([0, 1, 0, 2]) == obj.crc([1, 2])
False
Buffer with its CRC appended at the end
The append() method calculates a CRC value over a given buffer and appends it to its end. The CRC value is appended (i) in big-endian when no input data reflection is used (refin == False), or (ii) in little-endian when input data reflection is used (refin == True). The separate() method extracts the original message and its CRC from the appended buffer, and the verify() method validates the given buffer by calculating its residue.
>>> from yacrc import CRC16
>>> buffer = b'123456789'
>>> obj = CRC16.MODBUS
>>> crc = obj.crc(buffer)
>>> appended = obj.append(buffer)
>>> appended
b'1234567897K'
>>> obj.separate(appended) == (buffer, crc)
True
>>> obj.verify(appended)
True
Some CRC calculation implementations do not take into account the refin parameter when appending the CRC value to the buffer. A common example is when the CRC value is appended in big-endian format for CRC models with refin == True. The reverse parameter allows reversing the append order:
>>> from yacrc import CRC16
>>> buffer = b'123456789'
>>> obj = CRC16.MODBUS
>>> obj.crc(buffer).to_bytes(2, 'big')
b'K7'
>>> obj.append(buffer)
b'1234567897K'
>>> rev = CRC16.MODBUS(reverse=True)
>>> rev.crc(buffer).to_bytes(2, 'big')
b'K7'
>>> rev.append(buffer)
b'123456789K7'
Changelog
All notable changes to the project will be documented in this section.
[0.0.1] - 2025-02-26
- Initial release
References
[1] Ross Williams, "A Painless Guide to CRC Error Detection Algorithms," accessed January 18, 2025.
[2] Greg Cook, "Catalogue of Parametrised CRC Algorithms," accessed January 18, 2025.
License
This project is licensed under the MIT License.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file yacrc-0.0.1.tar.gz.
File metadata
- Download URL: yacrc-0.0.1.tar.gz
- Upload date:
- Size: 28.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
03a1bd05e7e13004a612daca6ea4aa312f07434ec7dbfd3f703a83c9affb6c6a
|
|
| MD5 |
33bae8e4c309ea2ada1a6f8e018b9a17
|
|
| BLAKE2b-256 |
1d95337fd74fb94b2e78472e009a9f17d328cfa85d5aa427958d3565067058f7
|
File details
Details for the file yacrc-0.0.1-py3-none-any.whl.
File metadata
- Download URL: yacrc-0.0.1-py3-none-any.whl
- Upload date:
- Size: 24.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7de08d616efadb2f82dceabedf842735a81c069c0debfb72be2f3a81f1f06fd8
|
|
| MD5 |
2316ed7b4b4a80da5ccdd3d8048ed886
|
|
| BLAKE2b-256 |
0745a8e96bb6a9b7c839155431f38a7420f5a50aa20e3ff76824d731147b4f69
|