Skip to main content

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 binary str inputs. The default data width is 8 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 bits or 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 to 0.
  • refin (bool) - Input data reflection. Defaults to False.
  • refout (bool) - Output data reflection. Defaults to False.
  • xorout (int) - Operand for the final XOR operation. Defaults to 0.
  • check (int | None) - CRC value for b'123456789'. Defaults to None.
  • residue (int | None) - Residue value. Defaults to None.
  • name (str) - CRC model name. Defaults to ''.
  • alias (str) - CRC model alias(es). Defaults to ''.
  • data (int) - Data width (number of bits). Defaults to 8.
  • reverse (bool) - CRC is appended in reverse order. Defaults to False.
  • optimized (bool | None) - Optimized CRC calculations. Defaults to None.

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

yacrc-0.0.1.tar.gz (28.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

yacrc-0.0.1-py3-none-any.whl (24.2 kB view details)

Uploaded Python 3

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

Hashes for yacrc-0.0.1.tar.gz
Algorithm Hash digest
SHA256 03a1bd05e7e13004a612daca6ea4aa312f07434ec7dbfd3f703a83c9affb6c6a
MD5 33bae8e4c309ea2ada1a6f8e018b9a17
BLAKE2b-256 1d95337fd74fb94b2e78472e009a9f17d328cfa85d5aa427958d3565067058f7

See more details on using hashes here.

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

Hashes for yacrc-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 7de08d616efadb2f82dceabedf842735a81c069c0debfb72be2f3a81f1f06fd8
MD5 2316ed7b4b4a80da5ccdd3d8048ed886
BLAKE2b-256 0745a8e96bb6a9b7c839155431f38a7420f5a50aa20e3ff76824d731147b4f69

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page