pycrc-0.12.0/AUTHORS0000644000000000000000000000105413615410400010702 0ustar00Thomas Pircher: main developer. Matthias Urlichs: removed the unused 'direct' parameter and added a fix not to recurse into main() when an unknown algorithm is selected. Marti Raudsepp: improved spacing for the table-driven algorithm. Stephan Brumme: his implementation of the slice-by-x algorithm was used as a basis for the implementation in pycrc. Danjel McGougan: whose Universal Crc project was highly influential in the implementation of widths < 8 in the table-driven algorithm. André Hartmann, ashelly and others for minor fixes. pycrc-0.12.0/CHANGELOG.md0000644000000000000000000005062013615410400011446 0ustar00# Change Log All notable changes to this project will be documented in this file. For a detailed list of changes see the [pycrc GitHub][pycrc github] page. This project adheres to [Semantic Versioning](http://semver.org/). ## [v0.12.0] - 2025-10-18 ### Added - A typed `CrcParams` model, and a read-only `CrcModels` mapping from model name to `CrcParams`. - `Crc.from_model()` and `Crc.from_params()` constructors. - A top-level `pycrc.crc()` convenience function and `Crc`, `CrcModels` and `CrcParams` re-exports. - An incremental `Crc.update()` / `Crc.digest()` API for streaming CRC calculation. - A PEP 561 `py.typed` marker so the inline type annotations are visible to type checkers. ### Changed - Minimum Python version is 3.10. - Modernize the build environment: - Switch build backend to hatchling. - Switch from flake8 to ruff. - Check if `SOURCE_DATE_EPOCH` is set, for reproducible builds. - Replace deprecated optparse with argparse. - Add type annotations to the source and test code and a `mypy` configuration. - Document how to run the tests in the README. - Build the HTML manual and man page from the DocBook sources in `doc` during the sdist build, and include the generated files in the source distribution. - `Crc.table_driven()` supports every table index width that divides 8 (i.e. 1, 2, 4 and 8). - `Crc` validates its constructor arguments and exposes `table_idx_width` consistently (`tbl_idx_width` is kept as an alias). - `CrcModels.get_params()` keeps returning a mutable `dict`; the typed `CrcParams` models are available through the new mapping interface. ### Fixed - Fix table-driven initialisation for partially defined models. - Use the configured symbol prefix in the generated code. - Fix unsound simplification of the expression 0 - x. - Fix `tbl_width` when reverting the table index width. - Use 32-bit functions to manipulate 32-bit values. - Fix the generated `--table-idx-width` option. ## [v0.11.0] - 2025-08-19 ### Added - Add support for GitHub CI - Add build instructions for Debian/Ubuntu and derivatives. ### Changed - Minimum Python version is 3.8 - The lookup table for the table-driven algorithm is now initialised at startup, speeding up the CRC calculation when called multiple times. - Remove the obsolete `__init__.py` file. - Minor speed improvement. ### Fixed - Make flake8 happy ## [v0.10.0] - 2022-11-01 ### Added - Use pytest for regression tests - Update to pyproject.toml for packaging ### Removed - Removed Python 2 compatibility. Use pycrc v0.9.x if Python 2 is required. ### Fixed - The xor-in value is never reflected Thanks to Ralf Schlatterbeck ## [v0.9.3] - 2022-11-01 ### Fixed - Fix compatibility with Python 3.10. Fixes #46. - Slightly improved the performance of the bit-by-bit-fast algorithm. ## [v0.9.2] - 2019-02-06 ### Fixed - Fix code generator to output the table correctly. Fixes #28. - Ensure the data is aligned when using slice-by. Fixes #24. ## [v0.9.1] - 2017-09-09 ### Added - Added setup.py script to install pycrc on the system, if desired. - Added checks about possibly unsuitable polynomials. Use the --force-poly option to override the error message. ### Changed - Completely rewritten the code generator back-end. The new back-end is more flexible and allows for a better optimisation of the generated expressions. - Moved the python code under the `pycrc` directory and changed how the pycrc sub-modules are included. Before one would write `import crc_xxx`, now one would write `import pycrc.xxx`. - New license for the documentation: [Creative Commons Attribution-Share Alike 4.0 Unported License][CC-BY-SA-4.0]. ### Fixed - Fixed binary files handling with Python 2.7. Fixes #11. Thanks to James Bowman. - Fixed some spelling. Thanks to Frank (ftheile) and ashelly. - Fixed Header Guard generation. Don't use underscores to start the header guard name. Thanks to Andre Hartmann. ## [v0.9] - 2016-01-06 ### Added - Added Stephan Brumme to the `AUTHORS` file as his implementation of the slice-by algorithm is the basis for pycrc's implementation. - Added a new option `--slice-by`. This option is still experimental and limited in its use. ### Changed - Documented the experimental `--slice-by option`. - Simplified the implementation where Width is less than 8 bits. - Run the code through `pylint`. - __API change__: changed the names of the member variables from `CamelCase` to the format suggested in `PEP 0008` (lowercase letters and words separated by underscore). ### Fixed - Suppressed the `crc_reflect` function where not needed. Addresses part of #8. Thanks to Craig McQueen. - Allow strings with values greater than 0x80 in `--check-hexstring`. - When the CRC width is less than 8 then the `bit-by-bit` algorithm needs to apply the CRC mask to the final value. Fixes #10. Thanks to Steve Geo. - Fixed the initial value of the 16-bit `CCITT` algorithm. Renamed the model from `ccitt` to `crc-16-ccitt`. Fixes #7. Thanks to Craig McQueen. ## [v0.8.3] - 2015-08-31 ### Changed - pycrc has a new [homepage][pycrc home]. - The [issue tracker on GitHub][pycrc issues] is now advertised as the preferred issue tracker. - Applied some minor optimisations to the generated table-driven code. - Belatedly added an authors file. Should I have forgotten to mention someone please don't hesitate to send a mail. - Upgraded documentation to DocBook 5. - Removed sourceforge mailing list from `README.md` in an effort to move pycrc away from sourceforge. - Removed the experimental `--bitwise-expression` option to facilitate restructuring of the code. - The preferred format for the input data for the Python API is now a byte array. But if a string is supplied it is decoded as UTF-8 string. Alternative formats are not supported and must be passed to the functions as byte arrays. - Changed the signature of the `crc_update()` function: the data argument is now a pointer to void to improve compatibility with C++. Thanks to Kamil Szczygieł. This closes GitHub issue #4. ## [v0.8.2] - 2014-12-04 ### Changed - Smaller code cleanups. - Stated more clearly that the bitwise expression algorithm is experimental in the documentation. - Fixed a typo in the documentation. The description previously stated: "The reflected value of 0xa3 (10100010b) is 0x45 (01000101b)" but this should be: "The reflected value of 0xa2 (10100010b) is 0x45 (01000101b)" Thanks to Andreas Nebenfuehr for reporting the mistake. - Small cleanups. Added a tests for special cases. For now, added `crc-5` with non-inverted input. This test is currently failing. - Removed warning about even polynomials. As Lars Pötter rightly pointed out, polynomials may be even. Added a caveat emptor about even polinomials in the documentation. ### Fixed - The table-driven code for polynomials of width < 8 using a table index width < 8 was producing a wrong checksum. Thanks to Radosław Gancarz. - Updated the generated code to cope with big Widths (>32 bits) on 32 bit processors. Since C89 does not give a way to specify the minimum length of a data type, the test does not validate C89 code using Widths > 32. For C99, the `uint_fastN_t` data types are used or long long, if the Width is unknown. ## [v0.8.1] - 2013-05-17 ### Changed - Updated [qm.py from GitHub][qm github]. - Explicitly stated that the output of pycrc is not considered a substantial part of the code of pycrc in `README.md`. - Re-organised the symbol table: grouped the code by functionality, not by algorithm. - The input to the CRC routines can now be bytes or strings. - Minor formatting change in the manpage. - Better python3 compatibility. - Added the files generated with the `bwe` algorithm to `check_files.sh`. ### Fixed - Fixed a bug in the handling of hexstrings in Python3. Thanks to Matthias Kuehlewein. - Added `--check-hexstring` to the tests. - Remove obsolete and unused `direct` parameter. Merge pull request #2 from smurfix/master. Thanks to Matthias Urlichs. - Don't recurse into main() when an unknown algorithm is selected. Merge pull request #2 from smurfix/master. Thanks to Matthias Urlichs. ## [v0.8] - 2013-01-04 ### Added - Merged (private) bitwise-expression branch to main. This adds the highly experimental `bitwise-expression` (`bwe`) code generator target, which might one day be almost as fast as the table-driven code but much smaller. At the moment the generated code is bigger and slower than any other algorithm, so use at your own risk. ### Changed - Now it is possible to specify the `--include` option multiple times. - Completely revisited and reworked the documentation. - Updated the command line help screen with more useful command descriptions. - Removed the `-*- coding: Latin-1 -*-` string. - Updated the copyright year to 2013. - Renamed abbreviations to `bbb`, `bbf`, `tbl`. - It is possible now to abbreviate the algorithms (`bbb` for `bit-by-bit`, `bbf` for `bit-by-bit-fast` and `tbl` for `table-driven`). Added the list of supported CRC models to the error message when an unknown model parameter was supplied. - Documented the possibility to abbreviate the algorithms. Minor improvements in the documentation. - Added a note to `README.md` that version 0.7.10 of pycrc is the last one known to work with Python 2.4. - Updated a link to the list of CRC models. - Renamed i`README` to `README.md`. - Updated link to the [Catalogue of parametrised CRC algorithms][crc catalogue]. ## [v0.7.11] - 2012-10-20 ### Changed - Improved Python3 compatibility. pycrc now requires Python 2.6 or later. - Added a test for compiled standard models. ### Fixed - Fixed a wrong `check` value of the `crc-64-jones` model. - Don't use `snprintf()` with `c89` code, changed to `sprintf()`. - Deleted `test.sh` shell script and replaced it with `test.py`. ## [v0.7.10] - 2012-02-13 ### Added - Added the models `crc-12-3gpp`, `crc-16-genibus`, `crc-32-bzip2` and `crc-64-xz`. Taken from [Greg Cook's Catalogue of parametrised CRC algorithms][crc catalogue]. ### Changed - Bad-looking C code generated; make sure the `bit-by-bit`(`-fast`) code does not contain two instructions on one line. Thanks to "intgr" for the fix. - Some small code clean-up: use `set()` when appropriate. ### Fixed - Fixed a mistake in the man page that still used the old model name `crc-32mpeg` instead of `crc-32-mpeg`. Thanks to Marek Erban. ## [v0.7.9] - 2011-12-08 ### Fixed - Fixed a bug in the generated C89 code that included `stdint.h`. Thanks to Frank (ftheile). Closes issue 3454356. - Fixed a bug in the generated C89 code when using a 64 bit CRC. - Using the `--verbose` option made pycrc quit without error message. ## [v0.7.8] - 2011-07-10 ### Changed - When generating C code for the C89 or ANSI standard, don't include `stdint.h`. This closes issue 3338930 - If no output file name is given while generating a C file, then pycrc will `#include` a hypothetical `pycrc.h` file instead of a `stdout.h` file. Also, added a comment on that line to make debugging easier. Closes issue 3325109. - Removed unused variable `this_option_optind` in the generated option parser. ## [v0.7.7] - 2011-02-11 ### Changed - Updated the copyright year. Fixed some coding style issues found by `pylint` and `pychecker`. ### Fixed - Substituted the deprecated function `atoi()` with `int()`. Closes issue 3136566. Thanks to Tony Smith. - Updated the documentation using Windows-style calls to the Python interpreter. ## [v0.7.6] - 2010-10-21 ### Changed - Rewritten macro parser from scratch. Simplified the macro language. - Changed a simple division (/) to a integer division (//) for Python3 compatibility. ### Fixed - Fixed a minor bug in the command line parsing of the generated main function. ## [v0.7.5] - 2010-03-28 ### Added - Python implementation of the `table-driven` algorithm can handle widths less than 8. - Suppressed warnings of unused cfg structure on partially defined models. ### Removed - Removed the half-baken and confusing `--direct` option. ### Changed - C/C++ code can now be generated for the table-driven algorithm with widths that are not byte-aligned or less than 8. This feature was heavily inspired by a similar feature in Danjel McGougan's [Universal Crc][universal crc]. - __API change__: introduced new variable `crc_shift`, member of the `crc_cfg_t` structure, which must be initialised manually when the width was undefined during C/C++ code generation. - Minor code cleanup. ## [v0.7.4] - 2010-01-24 ### Added - Added `crc-16-modbus`. Closes issue 2896611. ### Changed - Changed the `xor-in` value of the `crc-64-jones` model. - Set xmodem parameters equal to the `zmodem` parameters. - Generate more uniform error messages. - Added a warning for even polynomials. ### Fixed - Fix for unused variable argv. Closes issue 2893224. Thanks to Marko von Oppen. ## [v0.7.3] - 2009-10-25 ### Added - Added `crc-64-jones` CRC model. Thanks to Waterspirit. ### Changed - Renamed `crc-32mpeg` to `crc-32-mpeg`. ## [v0.7.2] - 2009-09-30 ### Fixed - Fixed a bug that caused the result of the Python `table-driven` code not being evaluated at all. Closes issue 2870630. Thanks to Ildar Muslukhov. ## [v0.7.1] - 2009-04-05 ### Added - Added `crc-32mpeg`. Thanks to Thomas Edwards. ## [v0.7] - 2009-02-27 ### Added - Added the `--direct` option. - Added `--check-hexstring` option. Closes issue 2545183. Thanks to Arnim Littek. - Added a check for extra arguments on the command line. Closes issue 2545185. Thanks to Arnim Littek. ### Changed - Added one more example in the documentation. ## [v0.6.7] - 2008-12-11 ### Changed - Run Python's 2to3 script on all files. Check the code on a x64 platform. - Fixed a bug that raised an exception when an unknown model was selected. ## [v0.6.6] - 2008-06-05 ### Changed - New license for the documentation: [Creative Commons Attribution-Share Alike 3.0 Unported License][CC-BY-SA-3.0]. ### Fixed - Fixed a bug in the `print_params()` function. Closes issue 1985197. Thanks to Artur Lipowski. ## [v0.6.5] - 2008-03-03 ### Added - Added dallas-1-wire 8 bit CRC. - Added `r-crc-16 model` (DECT (cordless digital standard) packets A-field according to ETSI EN 300 175-3 v2.1.1). Thanks to "raimondo". - Added `--crc-type` and `--include-file` options. - Added new file to handle CRC models. ### Changed - Added extern "C" declaration to the generated C header file. Thanks to Nathan Royer. - Changed the API to take the CRC model direct as parameter. Deleted the need for an obscure `opt` object. ### Fixed - Fixed a problem with the generated code for bit-by-bit-fast algorithms. Thanks to Hans Bacher. ## [v0.6.4] - 2007-12-05 ### Fixed - Fixed a bug in the code generator for the `table-driven` algorithm. Thanks to Tom McDermott. Closes issue 1843774 ## [v0.6.3] - 2007-10-13 ### Added - Added new models: `crc-5`, `crc-15`, `crc-16-usb`, `crc-24`, `crc-64`. The new models are taken from Ray Burr's CrcMoose. ### Fixed - Fixed some portability problems in the generated code. Thanks to Helmut Bauer. Closes issue 1812894. - The option `--check-file` works now with `--width` < 8. Closes issue 1794343. - Removed unnecessary restriction on the width when using the `bit-by-bit-fast` algorithm. Closes issue 1794344. ## [v0.6.2] - 2007-08-25 ### Changed - Simplified the table-driven code. Closes issue 1727128. - Changed the macro language syntax to a better format. - Renamed `crc_code_gen.py` to `crc_parser.py`. - Documented the usage of the `crc_*` modules. ### Fixed - The parameter to `--check-string` was ignored. Closes issue 1781637. ## [v0.6.1] - 2007-08-12 ### Added - Added test for C89 compilation. - Added a test case to loop over the input bytes one by one. ### Removed - Deleted obsolete options. ### Changed - Tidied up the documentation. Code cleanup. ### Fixed - Bugfix in the source code generator for C89: Compilation error due to mismatch of parameters in the `crc_finalize` funtion. - Changes related to 919107: Code generator includes `reflect()` function even if not needed. - Fixed a typo in the C89 source code generator. Thanks to Helmut Bauer. ## [v0.6] - 2007-05-21 ### Added - Added the `--std` option to generate C89 (ANSI) compliant code. - Added a new check to the test script which validate all possible combination of undefined parameters. - Made the generated main function cope with command line arguments. - Added the `--generate` table option. - Added a template engine for the code generation. Split up `pycrc.py` into smaller modules. - Added obsolete options again tor legacy reasons. - Added a better handling of the `--model` parameter. ### Changed - Reduced the size of the symbol table by re-arranging items. - Changed licence to the MIT licence. This makes the additional clause for generated source code obsolete. Changed all command line options with underscores to hyphen (e.g. `table_driven` becomes `table-driven`). Added the option `--generate` which obsoletes the old options `--generate_c` `--generate_h` etc. ## [v0.5] - 2007-03-25 ### Fixed - Fixed bug 1686404: unhandled exception when called with both options `--table_idx_width` and `--check_file`. - Eliminated useless declaration of `crc_reflect`, when not used. - Corrected typos in the documentation. ## [v0.4] - 2007-01-26 ### Added - Added more parameter sets (now supported: `crc-8`, `crc-16`, `citt`, `kermit`, `x-25`, `xmodem`, `zmodem`, `crc-32`, `crc-32c`, `posix`, `jam`, `xfer`) from [Greg Cook's Catalogue of parametrised CRC algorithms][crc catalogue]. - Added Doxygen documentation strings to the functions. - Added the `--symbol_prefix` option. - Added the `--check_file` option. - Added a non-regression test on the generated C source. ### Changed - Eliminated needless documentation of not generated functions. - Many corrections to the manual (thanks Francesca) Documented the new parameter sets. - Added some new tests, disabled the random loop. ### Fixed - Corrected many typos and bad phrasing (still a lot to do) Documented the `--symbol_prefix` option. ## [v0.3] - 2007-01-14 ### Added - First public release pycrc v0.3 [Unreleased]: https://github.com/tpircher/pycrc [v0.10.0]: https://github.com/tpircher/pycrc/releases/tag/v0.10.0 [v0.9.3]: https://github.com/tpircher/pycrc/releases/tag/v0.9.3 [v0.9.2]: https://github.com/tpircher/pycrc/releases/tag/v0.9.2 [v0.9.1]: https://github.com/tpircher/pycrc/releases/tag/v0.9.1 [v0.9]: https://github.com/tpircher/pycrc/releases/tag/v0.9 [v0.8.3]: https://github.com/tpircher/pycrc/releases/tag/v0.8.3 [v0.8.2]: https://github.com/tpircher/pycrc/releases/tag/v0.8.2 [v0.8.1]: https://github.com/tpircher/pycrc/releases/tag/v0.8.1 [v0.8]: https://github.com/tpircher/pycrc/releases/tag/v0.8 [v0.7.11]: https://github.com/tpircher/pycrc/releases/tag/v0.7.11 [v0.7.10]: https://github.com/tpircher/pycrc/releases/tag/v0.7.10 [v0.7.9]: https://github.com/tpircher/pycrc/releases/tag/v0.7.9 [v0.7.8]: https://github.com/tpircher/pycrc/releases/tag/v0.7.8 [v0.7.7]: https://github.com/tpircher/pycrc/releases/tag/v0.7.7 [v0.7.6]: https://github.com/tpircher/pycrc/releases/tag/v0.7.6 [v0.7.5]: https://github.com/tpircher/pycrc/releases/tag/v0.7.5 [v0.7.4]: https://github.com/tpircher/pycrc/releases/tag/v0.7.4 [v0.7.3]: https://github.com/tpircher/pycrc/releases/tag/v0.7.3 [v0.7.2]: https://github.com/tpircher/pycrc/releases/tag/v0.7.2 [v0.7.1]: https://github.com/tpircher/pycrc/releases/tag/v0.7.1 [v0.7]: https://github.com/tpircher/pycrc/releases/tag/v0.7 [v0.6.7]: https://github.com/tpircher/pycrc/releases/tag/v0.6.7 [v0.6.6]: https://github.com/tpircher/pycrc/releases/tag/v0.6.6 [v0.6.5]: https://github.com/tpircher/pycrc/releases/tag/v0.6.5 [v0.6.4]: https://github.com/tpircher/pycrc/releases/tag/v0.6.4 [v0.6.3]: https://github.com/tpircher/pycrc/releases/tag/v0.6.3 [v0.6.2]: https://github.com/tpircher/pycrc/releases/tag/v0.6.2 [v0.6.1]: https://github.com/tpircher/pycrc/releases/tag/v0.6.1 [v0.6]: https://github.com/tpircher/pycrc/releases/tag/v0.6 [v0.5]: https://github.com/tpircher/pycrc/releases/tag/v0.5 [v0.4]: https://github.com/tpircher/pycrc/releases/tag/v0.4 [v0.3]: https://github.com/tpircher/pycrc/releases/tag/v0.3 [pycrc home]: https://pycrc.org [pycrc github]: https://github.com/tpircher/pycrc [pycrc issues]: https://github.com/tpircher/pycrc/issues [crc catalogue]: http://regregex.bbcmicro.net/crc-catalogue.htm [universal crc]: http://mcgougan.se/universal_crc/ [qm github]: https://github.com/tpircher/quine-mccluskey [CC-BY-SA-3.0]: https://creativecommons.org/licenses/by-sa/3.0/ [CC-BY-SA-4.0]: https://creativecommons.org/licenses/by-sa/4.0/ pycrc-0.12.0/doc/Makefile0000644000000000000000000000077613615410400012051 0ustar00HTML_PARAMS = style-html.xsl MAN_PARAMS = style-man.xsl XSLTPROC ?= xsltproc source = pycrc.xml targets = $(source:.xml=.html) $(source:.xml=.1) generated = $(targets) docbook.css all: $(targets) .PHONY: clean clean: $(RM) $(generated) .PHONY: check check: xmllint --valid --noout $(source) %.html: %.xml $(HTML_PARAMS) $(XSLTPROC) --nonet -o $@ $(HTML_PARAMS) $< %.1: %.xml $(MAN_PARAMS) $(XSLTPROC) --nonet -o $@ $(MAN_PARAMS) $< %.txt: %.html links -dump -no-numbering -no-references $< > $@ pycrc-0.12.0/doc/docbook.css0000644000000000000000000000306113615410400012531 0ustar00 /********************************/ /* start of styles in block.xsl */ .formalpara-title { font-weight: bold; } div.blockquote-title { font-weight: bold; margin-top: 1em; margin-bottom: 1em; } span.msgmain-title { font-weight: bold; } span.msgsub-title { font-weight: bold; } span.msgrel-title { font-weight: bold; } div.msglevel, div.msgorig, div.msgaud { margin-top: 1em; margin-bottom: 1em; } span.msglevel-title, span.msgorig-title, span.msgaud-title { font-weight: bold; } div.msgexplan { margin-top: 1em; margin-bottom: 1em; } span.msgexplan-title { font-weight: bold; } /* end of styles in block.xsl */ /********************************/ /********************************/ /* start of styles in autotoc.xsl */ /* end of styles in autotoc.xsl */ /********************************/ /********************************/ /* start of styles in formal.xsl */ div.figure-title { font-weight: bold; } div.example-title { font-weight: bold; } div.equation-title { font-weight: bold; } div.table-title { font-weight: bold; } div.sidebar-title { font-weight: bold; } /* end of styles in formal.xsl */ /********************************/ /********************************/ /* start of styles in verbatim.xsl */ div.programlisting { white-space: pre; font-family: monospace; } div.screen { white-space: pre; font-family: monospace; } div.synopsis { white-space: pre; font-family: monospace; } /* end of styles in verbatim.xsl */ /********************************/ /* footnote rule */ hr.footnote-hr { width: 100; } pycrc-0.12.0/doc/pycrc.10000644000000000000000000004112313615410400011602 0ustar00'\" t .\" Title: pycrc .\" Author: Thomas Pircher .\" Generator: DocBook XSL Stylesheets v1.79.2 .\" Date: 2017-08-11 .\" Manual: pycrc .\" Source: pycrc 0.9.1 .\" Language: English .\" .TH "PYCRC" "1" "2017\-08\-11" "pycrc 0.9.1" "pycrc" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" pycrc \- a free, easy to use Cyclic Redundancy Check (CRC) calculator and C source code generator\&. .SH "SYNOPSIS" .HP \w'\fBpython\ pycrc\&.py\fR\ 'u \fBpython pycrc\&.py\fR [OPTIONS] .SH "DESCRIPTION" .PP \m[blue]\fBpycrc\fR\m[]\&\s-2\u[1]\d\s+2 is a CRC reference implementation in Python and a C source code generator for parametrised CRC models\&. The generated C source code can be optimised for simplicity, speed or small memory footprint, as required on small embedded systems\&. The following operations are implemented: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} calculate the checksum of a string (ASCII or hex) .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} calculate the checksum of a file .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} generate the header and source files for a C implementation\&. .RE .PP pycrc supports the following variants of the CRC algorithm: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fIbit\-by\-bit\fR or \fIbbb\fR: the basic algorithm which operates individually on every bit of the augmented message (i\&.e\&. the input data with \fIWidth\fR zero bits added at the end)\&. This algorithm is a straightforward implementation of the basic polynomial division and is the easiest one to understand, but it is also the slowest one among all possible variants\&. .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fIbit\-by\-bit\-fast\fR or \fIbbf\fR: a variation of the simple \fIbit\-by\-bit\fR algorithm\&. This algorithm still iterates over every bit of the message, but does not augment it (does not add \fIWidth\fR zero bits at the end)\&. It gives the same result as the \fIbit\-by\-bit\fR method by carefully choosing the initial value of the algorithm\&. This method might be a good choice for embedded platforms, where code space is more important than execution speed\&. .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fItable\-driven\fR or \fItbl\fR: the standard table driven algorithm\&. This is the fastest variant because it operates on one byte at a time, as opposed to one bit at the time\&. This method uses a look\-up table (usually of 256 elements), which might not be acceptable for small embedded systems\&. The number of elements in the look\-up table can be reduced with the \fB\-\-table\-idx\-width\fR command line switch\&. The value of 4 bits for the table index (16 elements in the look\-up table) can be a good compromise between execution speed and code size\&. .sp The \fB\-\-slice\-by\fR option enables a variant of the \fItable\-driven\fR algorithm that operates on 32 bits of data or more at a time rather than 8 bits\&. This can dramatically speed\-up the calculation of the CRC, at the cost of increased code and data size\&. \fINote\fR: this option is experimental and not well\-tested\&. Check your results and please raise bugs if you find problems\&. .RE .sp .SH "OPTIONS" .PP \fB\-\-version\fR .RS 4 show the program version number and exit\&. .RE .PP \fB\-h\fR, \fB\-\-help\fR .RS 4 show this help message and exit\&. .RE .PP \fB\-\-verbose\fR .RS 4 be more verbose; in particular, print the value of the parameters and the chosen model to stdout\&. .RE .PP \fB\-\-check\-string=\fR\fISTRING\fR .RS 4 calculate the checksum of a string (default: \(lq\fI123456789\fR\(rq)\&. If the string contains non\-ASCII characters then it will be UTF\-8 decoded\&. .RE .PP \fB\-\-check\-hexstring=\fR\fISTRING\fR .RS 4 calculate the checksum of a hexadecimal number string\&. .RE .PP \fB\-\-check\-file=\fR\fIFILE\fR .RS 4 calculate the checksum of a file\&. If the file contains non\-ASCII characters then it will be UTF\-8 decoded\&. .RE .PP \fB\-\-generate=\fR\fICODE\fR .RS 4 generate C source code; choose the type from {\fIh\fR, \fIc\fR, \fIc\-main\fR, \fItable\fR}\&. .RE .PP \fB\-\-std=\fR\fISTD\fR .RS 4 specify the C dialect of the generated code from {C89, ANSI, C99}\&. .RE .PP \fB\-\-algorithm=\fR\fIALGO\fR .RS 4 choose an algorithm from {\fIbit\-by\-bit\fR, \fIbbb\fR, \fIbit\-by\-bit\-fast\fR, \fIbbf\fR, \fItable\-driven\fR, \fItbl\fR, \fIall\fR}\&. .RE .PP \fB\-\-model=\fR\fIMODEL\fR .RS 4 choose a parameter set from {\fIcrc\-5\fR, \fIcrc\-8\fR, \fIdallas\-1\-wire\fR, \fIcrc\-12\-3gpp\fR, \fIcrc\-15\fR, \fIcrc\-16\fR, \fIcrc\-16\-usb\fR, \fIcrc\-16\-modbus\fR, \fIcrc\-16\-genibus\fR, \fIcrc\-16\-ccitt\fR, \fIr\-crc\-16\fR, \fIkermit\fR, \fIx\-25\fR, \fIxmodem\fR, \fIzmodem\fR, \fIcrc\-24\fR, \fIcrc\-32\fR, \fIcrc\-32c\fR, \fIcrc\-32\-mpeg\fR, \fIcrc\-32\-bzip2\fR, \fIposix\fR, \fIjam\fR, \fIxfer\fR, \fIcrc\-64\fR, \fIcrc\-64\-jones\fR, \fIcrc\-64\-xz\fR}\&. .RE .PP \fB\-\-width=\fR\fINUM\fR .RS 4 use \fINUM\fR bits in the \fIPolynomial\fR\&. .RE .PP \fB\-\-poly=\fR\fIHEX\fR .RS 4 use \fIHEX\fR as \fIPolynomial\fR\&. .RE .PP \fB\-\-reflect\-in=\fR\fIBOOL\fR .RS 4 reflect the octets in the input message\&. .RE .PP \fB\-\-xor\-in=\fR\fIHEX\fR .RS 4 use \fIHEX\fR as initial value\&. .RE .PP \fB\-\-reflect\-out=\fR\fIBOOL\fR .RS 4 reflect the resulting checksum before applying the XorOut value\&. .RE .PP \fB\-\-xor\-out=\fR\fIHEX\fR .RS 4 xor the final CRC value with \fIHEX\fR\&. .RE .PP \fB\-\-slice\-by=\fR\fINUM\fR .RS 4 speed\-up the table\-driven calculation by operating on \fINUM\fR octets of data rather than a single octet at a time\&. \fINUM\fR must be one of the values {\fI4\fR, \fI8\fR, \fI16\fR}\&. .RE .PP \fB\-\-table\-idx\-width=\fR\fINUM\fR .RS 4 use \fINUM\fR bits to index the CRC table; \fINUM\fR must be one of the values {\fI1\fR, \fI2\fR, \fI4\fR, \fI8\fR}\&. .RE .PP \fB\-\-force\-poly\fR .RS 4 override any errors about possibly unsuitable polynoms\&. pycrc does not allow even polynoms or polynoms that are wider than Width\&. Use this option to override the error, if you know what you are doing\&. .RE .PP \fB\-\-symbol\-prefix=\fR\fISTRING\fR .RS 4 when generating source code, use \fISTRING\fR as prefix to the exported C symbols\&. .RE .PP \fB\-\-crc\-type=\fR\fISTRING\fR .RS 4 when generating source code, use \fISTRING\fR as crc_t type\&. .RE .PP \fB\-\-include\-file=\fR\fIFILE\fR .RS 4 when generating source code, include also \fIFILE\fR as header file\&. This option can be specified multiple times\&. .RE .PP \fB\-o\fR\fIFILE\fR, \fB\-\-output=\fR\fIFILE\fR .RS 4 write the generated code to \fIFILE\fR instead of stdout\&. .RE .SH "THE CRC PARAMETRIC MODEL" .PP The parametric model follows Ross N\&. Williams\*(Aq convention described in \m[blue]\fBA Painless Guide to CRC Error Detection Algorithms\fR\m[]\&\s-2\u[2]\d\s+2, often called the Rocksoft Model\&. Since most people are familiar with this kind of parameters, pycrc follows this convention, described as follows: .PP \fIWidth\fR .RS 4 The number of significant bits in the CRC \fIPolynomial\fR, excluding the most significant 1\&. This will also be the number of bits in the final CRC result\&. In previous versions of pycrc only multiples of 8 could be used as \fIWidth\fR for the \fItable\-driven\fR algorithm\&. As of version 0\&.7\&.5 any value is accepted for \fIWidth\fR for all algorithms\&. .RE .PP \fIPolynomial\fR .RS 4 The unreflected polynomial of the CRC algorithm\&. .sp The \fIPolynomial\fR may be specified in its standard form, i\&.e\&. with bit \fIWidth\fR+1 set to 1, but the most significant bit may also be omitted\&. For example, both numbers 0x18005 and 0x8005 are accepted for a 16\-bit \fIPolynomial\fR\&. .sp Most polynomials used in real world applications are odd (the least significant bit is 1), but there are some good even ones\&. pycrc allows the use of even polynomials with the \fB\-\-force\-poly\fR option\&. Some even polynomials may yield incorrect checksums depending on the used algorithm\&. Use at your own risk and if at all possible use a well\-known \fIMODEL\fR above\&. .RE .PP \fIReflectIn\fR .RS 4 Reflect the octets of the message before processing them\&. .sp A word is reflected or reversed by \(lqflipping\(rq its bits around the mid\-point of the word\&. The most significant bit of the word is moved to the least significant position, the second\-most significant bit is moved to the second\-least significant position and so on\&. The reflected value of 0xa2 (10100010b) is 0x45 (01000101b), for example\&. .sp Some CRC algorithms can be implemented more efficiently in a bit reversed version, that\*(Aqs why many of the standard CRC models use reflected input octets\&. .RE .PP \fIXorIn\fR .RS 4 The initial value (usually all 0 or all 1) for algorithms which operate on the non\-augmented message, that is, any algorithm other than the \fIbit\-by\-bit\fR one\&. This value can be interpreted as a value which will be XOR\-ed into the CRC register after \fIWidth\fR iterations of the \fIbit\-by\-bit\fR algorithm\&. This implies that the simple \fIbit\-by\-bit\fR algorithm must calculate the initial value using some sort of reverse CRC algorithm on the \fIXorIn\fR value\&. .RE .PP \fIReflectOut\fR .RS 4 Reflect the final CRC result\&. This operation takes place before XOR\-ing the final CRC value with the \fIXorOut\fR parameter\&. .RE .PP \fIXorOut\fR .RS 4 A value (usually all bits 0 or all 1) which will be XOR\-ed to the final CRC value\&. .RE .PP \fICheck\fR .RS 4 This value is not exactly a parameter of a model but it is sometimes given together with the Rocksoft Model parameters\&. It is the CRC value of the parametrised model over the string \(lq\fI123456789\fR\(rq and can be used as a sanity check for a particular CRC implementation\&. .RE .SH "CODE GENERATION" .PP In the default configuration, the generated code is strict ISO C99\&. A minimal set of three functions are defined for each algorithm: \fBcrc_init()\fR, \fBcrc_update()\fR and \fBcrc_finalize()\fR\&. Depending on the number of parameters given to pycrc, a different interface will be defined\&. A fully parametrised model has a simpler API, while the generated code for a runtime\-specified implementation requires a pointer to a configuration structure as first parameter to all functions\&. .PP The generated source code uses the type \fBcrc_t\fR, which is used throughout the code to hold intermediate results and also the final CRC value\&. It is defined in the generated header file and its type may be overridden with the \fB\-\-crc\-type\fR option\&. .SS "Fully parametrised models" .PP The prototypes of the CRC functions are normally generated by pycrc using the \fI\-\-generate h\fR option\&. The CRC functions for a fully parametrised model will look like: .sp .ft B .nf #include typedef uint16_t crc_t; /* pycrc will use an appropriate size here */ .fi .ft .HP \w'crc_t\ crc_init('u .BI "crc_t crc_init(void);" .HP \w'crc_t\ crc_update('u .BI "crc_t crc_update(crc_t\ " "crc" ", const\ unsigned\ char\ *" "data" ", size_t\ " "data_len" ");" .HP \w'crc_t\ crc_finalize('u .BI "crc_t crc_finalize(crc_t\ " "crc" ");" .PP The code snippet below shows how to use the generated functions\&. .sp .if n \{\ .RS 4 .\} .nf #include "pycrc_generated_crc\&.h" #include int main(void) { static const unsigned char str1[] = "1234"; static const unsigned char str2[] = "56789"; crc_t crc; crc = crc_init(); crc = crc_update(crc, str1, sizeof(str1) \- 1); crc = crc_update(crc, str2, sizeof(str2) \- 1); /* more calls to crc_update\&.\&.\&. */ crc = crc_finalize(crc); printf("0x%lx\en", (long)crc); return 0; } .fi .if n \{\ .RE .\} .sp .SS "Models with runtime\-configurable parameters" .PP When the model is not fully defined then the missing parameters are stored in a structure of type \fBcrc_cfg_t\fR\&. If a CRC function requires a value from the \fBcrc_cfg_t\fR structure, then the first function argument is always a pointer to that structure\&. All fields of the configuration structure must be properly initialised before the first call to any CRC function\&. .PP If the \fIWidth\fR was not specified when the code was generated, then the \fBcrc_cfg_t\fR structure will contain three more fields: \fImsb_mask\fR, \fIcrc_mask\fR and \fIcrc_shift\fR\&. They are defined for performance reasons and must be initialised to the value given next to the field definition\&. .PP For example, a completely undefined CRC implementation will generate a \fBcrc_cfg_t\fR structure as below: .sp .if n \{\ .RS 4 .\} .nf typedef struct { unsigned int width; crc_t poly; bool reflect_in; crc_t xor_in; bool reflect_out; crc_t xor_out; // internal parameters crc_t msb_mask; // initialise as (crc_t)1u << (cfg\->width \- 1) crc_t crc_mask; // initialise as (cfg\->msb_mask \- 1) | cfg\->msb_mask unsigned int crc_shift; // initialise as cfg\->width < 8 ? 8 \- cfg\->width : 0 } crc_cfg_t; .fi .if n \{\ .RE .\} .PP \fImsb_mask\fR is a bitmask with the most significant bit of a \fIWidth\fR bits wide data type set to 1\&. \fIcrc_mask\fR is a bitmask with all bits of a \fIWidth\fR bits wide data type set to 1\&. \fIcrc_shift\fR is a shift counter that is used when \fIWidth\fR is less than 8\&. It is the number of bits to shift the CRC register to align its top bit to a byte boundary\&. .PP The file test/main\&.c in the source package of pycrc contains a fully featured example of how to use the generated source code\&. A shorter, more compact main() function can be generated with the \fI\-\-generate c\-main\fR option\&. This second variant is the better option as it will always output valid code when some of the CRC parameters are known and some are unknown during code generation\&. .SH "EXAMPLES" .PP .PP Calculate the CRC\-32 checksum of the string \(lq123456789\(rq: .RS 4 \fBpython pycrc\&.py \-\-model crc\-32 \-\-check\-string 123456789\fR .RE .PP Generate the source code of the table\-driven algorithm for an embedded application\&. .RS 4 The table index width of 4 bits ensures a moderate memory usage\&. To be precise, the size of the resulting table will be 16 * sizeof(crc_t)\&. .sp \fBpython pycrc\&.py \-\-model crc\-16 \-\-algorithm table\-driven \-\-table\-idx\-width 4 \-\-generate h \-o crc\&.h\fR .sp \fBpython pycrc\&.py \-\-model crc\-16 \-\-algorithm table\-driven \-\-table\-idx\-width 4 \-\-generate c \-o crc\&.c\fR .sp A variant of the \fIc\fR target is \fIc\-main\fR: this target will generate a simple \fImain()\fR function in addition to the CRC functions: .sp \fBpython pycrc\&.py \-\-model crc\-16 \-\-algorithm table\-driven \-\-table\-idx\-width 4 \-\-generate c\-main \-o crc\&.c\fR .RE .PP Generate the CRC table only: .RS 4 \fBpython pycrc\&.py \-\-model kermit \-\-generate table \-o crc\-table\&.txt\fR .RE .SH "SEE ALSO" .PP The homepage of pycrc is \m[blue]\fBhttps://pycrc\&.org\fR\m[]\&. .PP A list of common CRC models is at \m[blue]\fBhttps://pycrc\&.org/models\&.html\fR\m[]\&. For a long list of known CRC models, see Greg Cook\*(Aqs \m[blue]\fBCatalogue of Parameterised CRC Algorithms\fR\m[]\&\s-2\u[3]\d\s+2\&. .SH "COPYRIGHT" .PP This work is licensed under a \m[blue]\fBCreative Commons Attribution\-ShareAlike 4\&.0 International\fR\m[]\&\s-2\u[4]\d\s+2\&. .SH "AUTHOR" .PP \fBThomas Pircher\fR <\&thp\&.oss@p5r\&.uk\&> .RS 4 Author of pycrc and this manual page\&. .RE .SH "NOTES" .IP " 1." 4 pycrc .RS 4 \%https://pycrc.org .RE .IP " 2." 4 A Painless Guide to CRC Error Detection Algorithms .RS 4 \%http://www.ross.net/crc/crcpaper.html .RE .IP " 3." 4 Catalogue of Parameterised CRC Algorithms .RS 4 \%http://reveng.sourceforge.net/crc-catalogue/ .RE .IP " 4." 4 Creative Commons Attribution-ShareAlike 4.0 International .RS 4 \%https://creativecommons.org/licenses/by-sa/4.0/ .RE pycrc-0.12.0/doc/pycrc.html0000644000000000000000000007165313615410400012421 0ustar00pycrc

Name

pycrc — a free, easy to use Cyclic Redundancy Check (CRC) calculator and C source code generator.

Synopsis

python pycrc.py [OPTIONS]

Description

pycrc is a CRC reference implementation in Python and a C source code generator for parametrised CRC models. The generated C source code can be optimised for simplicity, speed or small memory footprint, as required on small embedded systems. The following operations are implemented:

  • calculate the checksum of a string (ASCII or hex)

  • calculate the checksum of a file

  • generate the header and source files for a C implementation.

pycrc supports the following variants of the CRC algorithm:

  • bit-by-bit or bbb: the basic algorithm which operates individually on every bit of the augmented message (i.e. the input data with Width zero bits added at the end). This algorithm is a straightforward implementation of the basic polynomial division and is the easiest one to understand, but it is also the slowest one among all possible variants.

  • bit-by-bit-fast or bbf: a variation of the simple bit-by-bit algorithm. This algorithm still iterates over every bit of the message, but does not augment it (does not add Width zero bits at the end). It gives the same result as the bit-by-bit method by carefully choosing the initial value of the algorithm. This method might be a good choice for embedded platforms, where code space is more important than execution speed.

  • table-driven or tbl: the standard table driven algorithm. This is the fastest variant because it operates on one byte at a time, as opposed to one bit at the time. This method uses a look-up table (usually of 256 elements), which might not be acceptable for small embedded systems. The number of elements in the look-up table can be reduced with the --table-idx-width command line switch. The value of 4 bits for the table index (16 elements in the look-up table) can be a good compromise between execution speed and code size.

    The --slice-by option enables a variant of the table-driven algorithm that operates on 32 bits of data or more at a time rather than 8 bits. This can dramatically speed-up the calculation of the CRC, at the cost of increased code and data size. Note: this option is experimental and not well-tested. Check your results and please raise bugs if you find problems.

Options

--version

show the program version number and exit.

-h , --help

show this help message and exit.

--verbose

be more verbose; in particular, print the value of the parameters and the chosen model to stdout.

--check-string=STRING

calculate the checksum of a string (default: “123456789”). If the string contains non-ASCII characters then it will be UTF-8 decoded.

--check-hexstring=STRING

calculate the checksum of a hexadecimal number string.

--check-file=FILE

calculate the checksum of a file. If the file contains non-ASCII characters then it will be UTF-8 decoded.

--generate=CODE

generate C source code; choose the type from {h, c, c-main, table}.

--std=STD

specify the C dialect of the generated code from {C89, ANSI, C99}.

--algorithm=ALGO

choose an algorithm from {bit-by-bit, bbb, bit-by-bit-fast, bbf, table-driven, tbl, all}.

--model=MODEL

choose a parameter set from {crc-5, crc-8, dallas-1-wire, crc-12-3gpp, crc-15, crc-16, crc-16-usb, crc-16-modbus, crc-16-genibus, crc-16-ccitt, r-crc-16, kermit, x-25, xmodem, zmodem, crc-24, crc-32, crc-32c, crc-32-mpeg, crc-32-bzip2, posix, jam, xfer, crc-64, crc-64-jones, crc-64-xz}.

--width=NUM

use NUM bits in the Polynomial.

--poly=HEX

use HEX as Polynomial.

--reflect-in=BOOL

reflect the octets in the input message.

--xor-in=HEX

use HEX as initial value.

--reflect-out=BOOL

reflect the resulting checksum before applying the XorOut value.

--xor-out=HEX

xor the final CRC value with HEX.

--slice-by=NUM

speed-up the table-driven calculation by operating on NUM octets of data rather than a single octet at a time. NUM must be one of the values {4, 8, 16}.

--table-idx-width=NUM

use NUM bits to index the CRC table; NUM must be one of the values {1, 2, 4, 8}.

--force-poly

override any errors about possibly unsuitable polynoms. pycrc does not allow even polynoms or polynoms that are wider than Width. Use this option to override the error, if you know what you are doing.

--symbol-prefix=STRING

when generating source code, use STRING as prefix to the exported C symbols.

--crc-type=STRING

when generating source code, use STRING as crc_t type.

--include-file=FILE

when generating source code, include also FILE as header file. This option can be specified multiple times.

-oFILE , --output=FILE

write the generated code to FILE instead of stdout.

The CRC Parametric Model

The parametric model follows Ross N. Williams' convention described in A Painless Guide to CRC Error Detection Algorithms, often called the Rocksoft Model. Since most people are familiar with this kind of parameters, pycrc follows this convention, described as follows:

Width

The number of significant bits in the CRC Polynomial, excluding the most significant 1. This will also be the number of bits in the final CRC result. In previous versions of pycrc only multiples of 8 could be used as Width for the table-driven algorithm. As of version 0.7.5 any value is accepted for Width for all algorithms.

Polynomial

The unreflected polynomial of the CRC algorithm.

The Polynomial may be specified in its standard form, i.e. with bit Width+1 set to 1, but the most significant bit may also be omitted. For example, both numbers 0x18005 and 0x8005 are accepted for a 16-bit Polynomial.

Most polynomials used in real world applications are odd (the least significant bit is 1), but there are some good even ones. pycrc allows the use of even polynomials with the --force-poly option. Some even polynomials may yield incorrect checksums depending on the used algorithm. Use at your own risk and if at all possible use a well-known MODEL above.

ReflectIn

Reflect the octets of the message before processing them.

A word is reflected or reversed by “flipping” its bits around the mid-point of the word. The most significant bit of the word is moved to the least significant position, the second-most significant bit is moved to the second-least significant position and so on. The reflected value of 0xa2 (10100010b) is 0x45 (01000101b), for example.

Some CRC algorithms can be implemented more efficiently in a bit reversed version, that's why many of the standard CRC models use reflected input octets.

XorIn

The initial value (usually all 0 or all 1) for algorithms which operate on the non-augmented message, that is, any algorithm other than the bit-by-bit one. This value can be interpreted as a value which will be XOR-ed into the CRC register after Width iterations of the bit-by-bit algorithm. This implies that the simple bit-by-bit algorithm must calculate the initial value using some sort of reverse CRC algorithm on the XorIn value.

ReflectOut

Reflect the final CRC result. This operation takes place before XOR-ing the final CRC value with the XorOut parameter.

XorOut

A value (usually all bits 0 or all 1) which will be XOR-ed to the final CRC value.

Check

This value is not exactly a parameter of a model but it is sometimes given together with the Rocksoft Model parameters. It is the CRC value of the parametrised model over the string “123456789” and can be used as a sanity check for a particular CRC implementation.

Code generation

In the default configuration, the generated code is strict ISO C99. A minimal set of three functions are defined for each algorithm: crc_init(), crc_update() and crc_finalize(). Depending on the number of parameters given to pycrc, a different interface will be defined. A fully parametrised model has a simpler API, while the generated code for a runtime-specified implementation requires a pointer to a configuration structure as first parameter to all functions.

The generated source code uses the type crc_t, which is used throughout the code to hold intermediate results and also the final CRC value. It is defined in the generated header file and its type may be overridden with the --crc-type option.

Fully parametrised models

The prototypes of the CRC functions are normally generated by pycrc using the --generate h option. The CRC functions for a fully parametrised model will look like:

#include <stdlib.h>
typedef uint16_t crc_t;         /* pycrc will use an appropriate size here */
                
crc_t crc_init(void); 
 
crc_t crc_update(crc_t crc,
 const unsigned char *data,
 size_t data_len);
 
crc_t crc_finalize(crc_t crc);
 

The code snippet below shows how to use the generated functions.

#include "pycrc_generated_crc.h"
#include <stdio.h>

int main(void)
{
    static const unsigned char str1[] = "1234";
    static const unsigned char str2[] = "56789";
    crc_t crc;

    crc = crc_init();
    crc = crc_update(crc, str1, sizeof(str1) - 1);
    crc = crc_update(crc, str2, sizeof(str2) - 1);
    /* more calls to crc_update... */
    crc = crc_finalize(crc);

    printf("0x%lx\n", (long)crc);
    return 0;
}
            

Models with runtime-configurable parameters

When the model is not fully defined then the missing parameters are stored in a structure of type crc_cfg_t. If a CRC function requires a value from the crc_cfg_t structure, then the first function argument is always a pointer to that structure. All fields of the configuration structure must be properly initialised before the first call to any CRC function.

If the Width was not specified when the code was generated, then the crc_cfg_t structure will contain three more fields: msb_mask, crc_mask and crc_shift. They are defined for performance reasons and must be initialised to the value given next to the field definition.

For example, a completely undefined CRC implementation will generate a crc_cfg_t structure as below:

typedef struct {
    unsigned int width;
    crc_t poly;
    bool reflect_in;
    crc_t xor_in;
    bool reflect_out;
    crc_t xor_out;

    // internal parameters
    crc_t msb_mask;             // initialise as (crc_t)1u << (cfg->width - 1)
    crc_t crc_mask;             // initialise as (cfg->msb_mask - 1) | cfg->msb_mask
    unsigned int crc_shift;     // initialise as cfg->width < 8 ? 8 - cfg->width : 0
} crc_cfg_t;
            

msb_mask is a bitmask with the most significant bit of a Width bits wide data type set to 1. crc_mask is a bitmask with all bits of a Width bits wide data type set to 1. crc_shift is a shift counter that is used when Width is less than 8. It is the number of bits to shift the CRC register to align its top bit to a byte boundary.

The file test/main.c in the source package of pycrc contains a fully featured example of how to use the generated source code. A shorter, more compact main() function can be generated with the --generate c-main option. This second variant is the better option as it will always output valid code when some of the CRC parameters are known and some are unknown during code generation.

Examples

Calculate the CRC-32 checksum of the string “123456789”:

python pycrc.py --model crc-32 --check-string 123456789

Generate the source code of the table-driven algorithm for an embedded application.

The table index width of 4 bits ensures a moderate memory usage. To be precise, the size of the resulting table will be 16 * sizeof(crc_t).

python pycrc.py --model crc-16 --algorithm table-driven --table-idx-width 4 --generate h -o crc.h

python pycrc.py --model crc-16 --algorithm table-driven --table-idx-width 4 --generate c -o crc.c

A variant of the c target is c-main: this target will generate a simple main() function in addition to the CRC functions:

python pycrc.py --model crc-16 --algorithm table-driven --table-idx-width 4 --generate c-main -o crc.c

Generate the CRC table only:

python pycrc.py --model kermit --generate table -o crc-table.txt

See Also

The homepage of pycrc is https://pycrc.org.

A list of common CRC models is at https://pycrc.org/models.html. For a long list of known CRC models, see Greg Cook's Catalogue of Parameterised CRC Algorithms.

Copyright

This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International.

pycrc-0.12.0/doc/pycrc.xml0000644000000000000000000010046413615410400012246 0ustar00 ]> pycrc pycrc &project_version; &author_firstname; &author_surname; Author of pycrc and this manual page. &author_email; &date; pycrc 1 pycrc a free, easy to use Cyclic Redundancy Check (CRC) calculator and C source code generator. python pycrc.py OPTIONS Description pycrc is a CRC reference implementation in Python and a C source code generator for parametrised CRC models. The generated C source code can be optimised for simplicity, speed or small memory footprint, as required on small embedded systems. The following operations are implemented: calculate the checksum of a string (ASCII or hex) calculate the checksum of a file generate the header and source files for a C implementation. pycrc supports the following variants of the CRC algorithm: &bit-by-bit; or &bbb;: the basic algorithm which operates individually on every bit of the augmented message (i.e. the input data with &width; zero bits added at the end). This algorithm is a straightforward implementation of the basic polynomial division and is the easiest one to understand, but it is also the slowest one among all possible variants. &bit-by-bit-fast; or &bbf;: a variation of the simple &bit-by-bit; algorithm. This algorithm still iterates over every bit of the message, but does not augment it (does not add &width; zero bits at the end). It gives the same result as the &bit-by-bit; method by carefully choosing the initial value of the algorithm. This method might be a good choice for embedded platforms, where code space is more important than execution speed. &table-driven; or &tbl;: the standard table driven algorithm. This is the fastest variant because it operates on one byte at a time, as opposed to one bit at the time. This method uses a look-up table (usually of 256 elements), which might not be acceptable for small embedded systems. The number of elements in the look-up table can be reduced with the command line switch. The value of 4 bits for the table index (16 elements in the look-up table) can be a good compromise between execution speed and code size. The option enables a variant of the &table-driven; algorithm that operates on 32 bits of data or more at a time rather than 8 bits. This can dramatically speed-up the calculation of the CRC, at the cost of increased code and data size. Note: this option is experimental and not well-tested. Check your results and please raise bugs if you find problems. Options show the program version number and exit. show this help message and exit. be more verbose; in particular, print the value of the parameters and the chosen model to stdout. STRING calculate the checksum of a string (default: 123456789). If the string contains non-ASCII characters then it will be UTF-8 decoded. STRING calculate the checksum of a hexadecimal number string. FILE calculate the checksum of a file. If the file contains non-ASCII characters then it will be UTF-8 decoded. CODE generate C source code; choose the type from {h, c, c-main, table}. STD specify the C dialect of the generated code from {C89, ANSI, C99}. ALGO choose an algorithm from {bit-by-bit, bbb, bit-by-bit-fast, bbf, table-driven, tbl, all}. MODEL choose a parameter set from {crc-5, crc-8, dallas-1-wire, crc-12-3gpp, crc-15, crc-16, crc-16-usb, crc-16-modbus, crc-16-genibus, crc-16-ccitt, r-crc-16, kermit, x-25, xmodem, zmodem, crc-24, crc-32, crc-32c, crc-32-mpeg, crc-32-bzip2, posix, jam, xfer, crc-64, crc-64-jones, crc-64-xz}. NUM use NUM bits in the &poly;. HEX use HEX as &poly;. BOOL reflect the octets in the input message. HEX use HEX as initial value. BOOL reflect the resulting checksum before applying the &xor_out; value. HEX xor the final CRC value with HEX. NUM speed-up the &table-driven; calculation by operating on NUM octets of data rather than a single octet at a time. NUM must be one of the values {4, 8, 16}. NUM use NUM bits to index the CRC table; NUM must be one of the values {1, 2, 4, 8}. override any errors about possibly unsuitable polynoms. pycrc does not allow even polynoms or polynoms that are wider than &width;. Use this option to override the error, if you know what you are doing. STRING when generating source code, use STRING as prefix to the exported C symbols. STRING when generating source code, use STRING as crc_t type. FILE when generating source code, include also FILE as header file. This option can be specified multiple times. FILE FILE write the generated code to FILE instead of stdout. The CRC Parametric Model The parametric model follows Ross N. Williams' convention described in A Painless Guide to CRC Error Detection Algorithms, often called the Rocksoft Model. Since most people are familiar with this kind of parameters, pycrc follows this convention, described as follows: &width; The number of significant bits in the CRC &poly;, excluding the most significant 1. This will also be the number of bits in the final CRC result. In previous versions of pycrc only multiples of 8 could be used as &width; for the &table-driven; algorithm. As of version 0.7.5 any value is accepted for &width; for all algorithms. &poly; The unreflected polynomial of the CRC algorithm. The &poly; may be specified in its standard form, i.e. with bit &width;+1 set to 1, but the most significant bit may also be omitted. For example, both numbers 0x18005 and 0x8005 are accepted for a 16-bit &poly;. Most polynomials used in real world applications are odd (the least significant bit is 1), but there are some good even ones. pycrc allows the use of even polynomials with the option. Some even polynomials may yield incorrect checksums depending on the used algorithm. Use at your own risk and if at all possible use a well-known MODEL above. &reflect_in; Reflect the octets of the message before processing them. A word is reflected or reversed by flipping its bits around the mid-point of the word. The most significant bit of the word is moved to the least significant position, the second-most significant bit is moved to the second-least significant position and so on. The reflected value of 0xa2 (10100010b) is 0x45 (01000101b), for example. Some CRC algorithms can be implemented more efficiently in a bit reversed version, that's why many of the standard CRC models use reflected input octets. &xor_in; The initial value (usually all 0 or all 1) for algorithms which operate on the non-augmented message, that is, any algorithm other than the &bit-by-bit; one. This value can be interpreted as a value which will be XOR-ed into the CRC register after &width; iterations of the &bit-by-bit; algorithm. This implies that the simple &bit-by-bit; algorithm must calculate the initial value using some sort of reverse CRC algorithm on the &xor_in; value. &reflect_out; Reflect the final CRC result. This operation takes place before XOR-ing the final CRC value with the &xor_out; parameter. &xor_out; A value (usually all bits 0 or all 1) which will be XOR-ed to the final CRC value. ✓ This value is not exactly a parameter of a model but it is sometimes given together with the Rocksoft Model parameters. It is the CRC value of the parametrised model over the string 123456789 and can be used as a sanity check for a particular CRC implementation. Code generation In the default configuration, the generated code is strict ISO C99. A minimal set of three functions are defined for each algorithm: crc_init(), crc_update() and crc_finalize(). Depending on the number of parameters given to pycrc, a different interface will be defined. A fully parametrised model has a simpler API, while the generated code for a runtime-specified implementation requires a pointer to a configuration structure as first parameter to all functions. The generated source code uses the type crc_t, which is used throughout the code to hold intermediate results and also the final CRC value. It is defined in the generated header file and its type may be overridden with the option. Fully parametrised models The prototypes of the CRC functions are normally generated by pycrc using the --generate h option. The CRC functions for a fully parametrised model will look like: #include <stdlib.h> typedef uint16_t crc_t; /* pycrc will use an appropriate size here */ crc_t crc_init crc_t crc_update crc_t crc const unsigned char *data size_t data_len crc_t crc_finalize crc_t crc The code snippet below shows how to use the generated functions. #include "pycrc_generated_crc.h" #include <stdio.h> int main(void) { static const unsigned char str1[] = "1234"; static const unsigned char str2[] = "56789"; crc_t crc; crc = crc_init(); crc = crc_update(crc, str1, sizeof(str1) - 1); crc = crc_update(crc, str2, sizeof(str2) - 1); /* more calls to crc_update... */ crc = crc_finalize(crc); printf("0x%lx\n", (long)crc); return 0; } Models with runtime-configurable parameters When the model is not fully defined then the missing parameters are stored in a structure of type crc_cfg_t. If a CRC function requires a value from the crc_cfg_t structure, then the first function argument is always a pointer to that structure. All fields of the configuration structure must be properly initialised before the first call to any CRC function. If the &width; was not specified when the code was generated, then the crc_cfg_t structure will contain three more fields: msb_mask, crc_mask and crc_shift. They are defined for performance reasons and must be initialised to the value given next to the field definition. For example, a completely undefined CRC implementation will generate a crc_cfg_t structure as below: typedef struct { unsigned int width; crc_t poly; bool reflect_in; crc_t xor_in; bool reflect_out; crc_t xor_out; // internal parameters crc_t msb_mask; // initialise as (crc_t)1u << (cfg->width - 1) crc_t crc_mask; // initialise as (cfg->msb_mask - 1) | cfg->msb_mask unsigned int crc_shift; // initialise as cfg->width < 8 ? 8 - cfg->width : 0 } crc_cfg_t; msb_mask is a bitmask with the most significant bit of a &width; bits wide data type set to 1. crc_mask is a bitmask with all bits of a &width; bits wide data type set to 1. crc_shift is a shift counter that is used when &width; is less than 8. It is the number of bits to shift the CRC register to align its top bit to a byte boundary. The file test/main.c in the source package of pycrc contains a fully featured example of how to use the generated source code. A shorter, more compact main() function can be generated with the --generate c-main option. This second variant is the better option as it will always output valid code when some of the CRC parameters are known and some are unknown during code generation. Examples Calculate the CRC-32 checksum of the string 123456789: python pycrc.py --model crc-32 --check-string 123456789 Generate the source code of the table-driven algorithm for an embedded application. The table index width of 4 bits ensures a moderate memory usage. To be precise, the size of the resulting table will be 16 * sizeof(crc_t). python pycrc.py --model crc-16 --algorithm table-driven --table-idx-width 4 --generate h -o crc.h python pycrc.py --model crc-16 --algorithm table-driven --table-idx-width 4 --generate c -o crc.c A variant of the c target is c-main: this target will generate a simple main() function in addition to the CRC functions: python pycrc.py --model crc-16 --algorithm table-driven --table-idx-width 4 --generate c-main -o crc.c Generate the CRC table only: python pycrc.py --model kermit --generate table -o crc-table.txt See Also The homepage of pycrc is &project_homepage;. A list of common CRC models is at &project_models;. For a long list of known CRC models, see Greg Cook's Catalogue of Parameterised CRC Algorithms. Copyright This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International. pycrc-0.12.0/doc/style-html.xsl0000644000000000000000000000106613615410400013234 0ustar00 pycrc-0.12.0/doc/style-man.xsl0000644000000000000000000000033213615410400013036 0ustar00 pycrc-0.12.0/src/pycrc.py0000644000000000000000000000256313615410400012121 0ustar00#!/usr/bin/env python3 # pycrc -- parameterisable CRC calculation utility and C source code generator # # Copyright (c) 2006-2026 Thomas Pircher # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. """This is a simple wrapper/executable for the pycrc library.""" import sys from pycrc.main import main if __name__ == "__main__": sys.exit(main()) pycrc-0.12.0/src/pycrc/__init__.py0000644000000000000000000000214013615410400013647 0ustar00"""pycrc -- parameterisable CRC calculation utility and C source code generator.""" from pycrc.algorithms import Crc from pycrc.models import CrcModels, CrcParams __version__ = "0.12.0" __author__ = "Thomas Pircher" __all__ = ["Crc", "CrcModels", "CrcParams", "__author__", "__version__", "crc"] def crc(model: str, data: str | bytes | bytearray, algorithm: str = "bit-by-bit-fast") -> int: """ Return the CRC of *data* for the named CRC *model*. This is a small convenience wrapper around :class:`pycrc.algorithms.Crc` for one-shot calculations: import pycrc pycrc.crc("crc-32", b"123456789") # -> 0xCBF43926 The *algorithm* may be one of "bit-by-bit", "bit-by-bit-fast" (the default) or "table-driven". """ crc_obj = Crc.from_model(model) if algorithm in {"bit-by-bit", "bbb"}: return crc_obj.bit_by_bit(data) if algorithm in {"bit-by-bit-fast", "bbf"}: return crc_obj.bit_by_bit_fast(data) if algorithm in {"table-driven", "tbl"}: return crc_obj.table_driven(data) raise ValueError(f"unknown algorithm {algorithm!r}") pycrc-0.12.0/src/pycrc/__main__.py0000644000000000000000000000260513615410400013636 0ustar00#!/usr/bin/env python # pycrc -- parameterisable CRC calculation utility and C source code generator # # Copyright (c) 2006-2026 Thomas Pircher # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. """ Command line entry point, allowing pycrc to be run as ``python -m pycrc``. """ import sys from pycrc.main import main if __name__ == "__main__": sys.exit(main()) pycrc-0.12.0/src/pycrc/algorithms.py0000644000000000000000000003332313615410400014270 0ustar00# pycrc -- parameterisable CRC calculation utility and C source code generator # # Copyright (c) 2006-2026 Thomas Pircher # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. """ CRC algorithms implemented in Python. If you want to study the Python implementation of the CRC routines, then this is a good place to start from. The algorithms Bit by Bit, Bit by Bit Fast and Table-Driven are implemented. This module can also be used as a library from within Python. Examples ======== This is an example use of the different algorithms: from pycrc.algorithms import Crc crc = Crc(width = 16, poly = 0x8005, reflect_in = True, xor_in = 0x0000, reflect_out = True, xor_out = 0x0000) print("{0:#x}".format(crc.bit_by_bit("123456789"))) print("{0:#x}".format(crc.bit_by_bit_fast("123456789"))) print("{0:#x}".format(crc.table_driven("123456789"))) A model can also be selected by name: from pycrc.algorithms import Crc crc = Crc.from_model("crc-32") print("{0:#x}".format(crc.bit_by_bit_fast("123456789"))) """ from __future__ import annotations from typing import TypeAlias from pycrc.models import CrcModels, CrcParams #: The input accepted by the CRC routines. Plain strings are encoded with the #: encoding passed to the respective routine (UTF-8 by default). BytesLike: TypeAlias = str | bytes | bytearray class Crc: """ A base class for CRC routines. The configuration attributes (width, poly, reflect_in, ...) are set once by the constructor and must not be modified afterwards; changing them would leave the cached lookup table and the derived masks inconsistent. """ def __init__( self, width: int, poly: int, reflect_in: bool, xor_in: int, reflect_out: bool, xor_out: int, table_idx_width: int | None = None, slice_by: int = 1, ) -> None: """ Create a CRC object, using the Rocksoft model. The parameters are as follows: width poly reflect_in xor_in reflect_out xor_out Raises ValueError if a parameter has an unsuitable type or value. The table_idx_width parameter selects the size of the lookup table; any value that divides 8 (i.e. 1, 2, 4 or 8) is supported by the Python table_driven() implementation. The slice_by parameter is only used by the generated C code; it does not affect the result of the Python algorithms. """ self.width: int = width self.poly: int = poly self.reflect_in: bool = reflect_in self.xor_in: int = xor_in self.reflect_out: bool = reflect_out self.xor_out: int = xor_out self.slice_by: int = slice_by if table_idx_width is None: table_idx_width = 8 self.table_idx_width: int = table_idx_width if 8 % self.table_idx_width != 0: raise ValueError(f"table_idx_width must divide 8, got {self.table_idx_width}") self.msb_mask: int = 0x1 << (self.width - 1) self.mask: int = ((self.msb_mask - 1) << 1) | 1 self.tbl_width: int = 1 << self.table_idx_width self.direct_init: int = self.xor_in self.nondirect_init: int = self.__get_nondirect_init(self.xor_in) if self.width < 8: self.crc_shift: int = 8 - self.width else: self.crc_shift = 0 self.tbl: list[list[int]] = self._gen_table() # State used by the incremental update()/digest() API. None means that # no computation is in progress. self._register: int | None = None def __repr__(self) -> str: return ( f"Crc(width={self.width}, poly={self.poly:#x}, reflect_in={self.reflect_in}, " f"xor_in={self.xor_in:#x}, reflect_out={self.reflect_out}, xor_out={self.xor_out:#x})" ) @property def tbl_idx_width(self) -> int: """Deprecated alias for :attr:`table_idx_width`.""" return self.table_idx_width @tbl_idx_width.setter def tbl_idx_width(self, value: int) -> None: """Deprecated alias for :attr:`table_idx_width`.""" self.table_idx_width = value self.tbl_width = 1 << value self.tbl = self._gen_table() @property def table(self) -> list[list[int]]: """The cached lookup table used by the table-driven algorithm.""" return self.tbl @classmethod def from_params(cls, params: CrcParams) -> Crc: """ Create a Crc object from a :class:`~pycrc.models.CrcParams` model. """ return cls( width=params.width, poly=params.poly, reflect_in=params.reflect_in, xor_in=params.xor_in, reflect_out=params.reflect_out, xor_out=params.xor_out, ) @classmethod def from_model(cls, model: str) -> Crc: """ Create a Crc object for a named model (e.g. "crc-32"). Raises ValueError if the model is unknown. """ params = CrcModels().get(model) if params is None: raise ValueError(f"unknown CRC model {model!r}") return cls.from_params(params) def __get_nondirect_init(self, init: int) -> int: """ return the non-direct init if the direct algorithm has been selected. """ crc = init for _ in range(self.width): bit = crc & 0x01 if bit: crc ^= self.poly crc >>= 1 if bit: crc |= self.msb_mask return crc & self.mask @staticmethod def _as_bytes(data: BytesLike, encoding: str = "utf-8") -> bytes | bytearray: """ Return *data* as a bytes-like object. Strings are encoded using *encoding*; bytes and bytearrays are returned unchanged. """ if isinstance(data, str): return bytearray(data, encoding) return data @staticmethod def reflect(data: int, width: int) -> int: """ reflect a data word, i.e. reverts the bit order. """ res = data & 0x01 for _ in range(width - 1): data >>= 1 res = (res << 1) | (data & 0x01) return res def bit_by_bit(self, in_data: BytesLike, encoding: str = "utf-8") -> int: """ Classic simple and slow CRC implementation. This function iterates bit by bit over the augmented input message and returns the calculated CRC value at the end. Strings are encoded using *encoding* (UTF-8 by default). """ data = self._as_bytes(in_data, encoding) reg = self.nondirect_init for octet in data: if self.reflect_in: octet = self.reflect(octet, 8) for i in range(8): topbit = reg & self.msb_mask reg = ((reg << 1) & self.mask) | ((octet >> (7 - i)) & 0x01) if topbit: reg ^= self.poly for _ in range(self.width): topbit = reg & self.msb_mask reg = (reg << 1) & self.mask if topbit: reg ^= self.poly if self.reflect_out: reg = self.reflect(reg, self.width) return (reg ^ self.xor_out) & self.mask def bit_by_bit_fast_update(self, reg: int, in_data: BytesLike, encoding: str = "utf-8") -> int: """ Update a CRC register with the bit-by-bit-fast algorithm and return the new register value. This is the incremental core of bit_by_bit_fast(): it does not apply the initial value, the output reflection or the final xor-out value, so it can be used to process data in chunks. Strings are encoded using *encoding* (UTF-8 by default). """ data = self._as_bytes(in_data, encoding) for octet in data: if self.reflect_in: octet = self.reflect(octet, 8) for i in range(8): topbit = reg & self.msb_mask if octet & (0x80 >> i): topbit ^= self.msb_mask reg <<= 1 if topbit: reg ^= self.poly reg &= self.mask return reg def bit_by_bit_fast(self, in_data: BytesLike, encoding: str = "utf-8") -> int: """ This is a slightly modified version of the bit-by-bit algorithm: it does not need to loop over the augmented bits, i.e. the Width 0-bits which are appended to the input message in the bit-by-bit algorithm. Strings are encoded using *encoding* (UTF-8 by default). """ reg = self.bit_by_bit_fast_update(self.direct_init, in_data, encoding) if self.reflect_out: reg = self.reflect(reg, self.width) return (reg ^ self.xor_out) & self.mask def reset(self) -> None: """ Discard the state of any in-progress incremental computation. """ self._register = None def update(self, data: BytesLike, encoding: str = "utf-8") -> None: """ Feed more data into an incremental bit-by-bit-fast computation. The result is obtained with :meth:`digest`. The computation is initialised implicitly on the first call. Strings are encoded using *encoding* (UTF-8 by default). """ if self._register is None: self._register = self.direct_init self._register = self.bit_by_bit_fast_update(self._register, data, encoding) def digest(self) -> int: """ Return the CRC of all data fed to :meth:`update` so far. The value is identical to calling :meth:`bit_by_bit_fast` on the concatenation of all the chunks. If no data has been fed, the CRC of the empty message is returned. """ reg = self.direct_init if self._register is None else self._register if self.reflect_out: reg = self.reflect(reg, self.width) return (reg ^ self.xor_out) & self.mask def _gen_table(self) -> list[list[int]]: """ Generate the CRC table used for the table_driven CRC algorithm. This is an internal helper; use the read-only :attr:`table` property instead. The table is generated for the requested index width. """ table_length = 1 << self.table_idx_width tbl = [[0 for i in range(table_length)] for j in range(self.slice_by)] for i in range(table_length): reg = i if self.reflect_in: reg = self.reflect(reg, self.table_idx_width) reg = reg << (self.width - self.table_idx_width + self.crc_shift) for _ in range(self.table_idx_width): reg = ( (reg << 1) ^ (self.poly << self.crc_shift) if reg & (self.msb_mask << self.crc_shift) != 0 else (reg << 1) ) if self.reflect_in: reg = self.reflect(reg >> self.crc_shift, self.width) << self.crc_shift tbl[0][i] = (reg >> self.crc_shift) & self.mask for j in range(1, self.slice_by): for i in range(table_length): tbl[j][i] = (tbl[j - 1][i] >> 8) ^ tbl[0][tbl[j - 1][i] & 0xFF] return tbl def gen_table(self) -> list[list[int]]: """Deprecated alias for :meth:`_gen_table`; use :attr:`table`.""" return self._gen_table() def table_driven(self, in_data: BytesLike, encoding: str = "utf-8") -> int: """ The standard table-driven CRC algorithm. Any table index width that divides 8 (i.e. 1, 2, 4 or 8) is supported. Strings are encoded using *encoding* (UTF-8 by default). """ data = self._as_bytes(in_data, encoding) idx_width = self.table_idx_width table_mask = (1 << idx_width) - 1 table = self.tbl[0] if self.reflect_in: reg = self.reflect(self.direct_init, self.width) for octet in data: for i in range(8 // idx_width): tblidx = (reg ^ (octet >> (i * idx_width))) & table_mask reg = (table[tblidx] ^ (reg >> idx_width)) & self.mask reg = self.reflect(reg, self.width) & self.mask else: shift = self.width - idx_width reg = self.direct_init & self.mask for octet in data: for i in range(8 // idx_width): part = (octet >> (8 - (i + 1) * idx_width)) & table_mask crc_high = reg >> shift if shift >= 0 else reg << (-shift) tblidx = (crc_high ^ part) & table_mask reg = (table[tblidx] ^ (reg << idx_width)) & self.mask if self.reflect_out: reg = self.reflect(reg, self.width) return (reg ^ self.xor_out) & self.mask pycrc-0.12.0/src/pycrc/codegen.py0000644000000000000000000030762113615410400013530 0ustar00# pycrc -- parameterisable CRC calculation utility and C source code generator # # Copyright (c) 2017-2026 Thomas Pircher # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. """ use as follows: import pycrc.codegen as cg import pycrc.opt as opt opt = opt.Options() print(cg.CodeGen(opt, '', [ 'if (a == b) {', cg.CodeGen(opt, 4*' ', ['print("a equals b\\n");']), '}', ])) """ from __future__ import annotations import weakref from collections.abc import Sequence import pycrc.expr as expr from pycrc.opt import Options from pycrc.symtable import SymbolTable _symbol_tables: weakref.WeakKeyDictionary[Options, SymbolTable] = weakref.WeakKeyDictionary() def _get_symbol_table(opt: Options) -> SymbolTable: """ Return the symbol table for *opt*, creating it on first use. Every node in the generated code tree needs the same derived symbols, so they are computed once per Options object instead of once per node. """ sym = _symbol_tables.get(opt) if sym is None: sym = SymbolTable(opt) _symbol_tables[opt] = sym return sym class CodeGen: """ A node in the generated source code tree. Subclasses add conditionals, comments and other constructs. The gen() method renders the tree to a list of indented lines. """ def __init__(self, opt: Options, indent: str | None, content: Sequence[CodeGen | str] | None = None) -> None: """ The class constructor. """ self.opt: Options = opt self.sym: SymbolTable = _get_symbol_table(opt) self.indent: str | None = indent self.content: Sequence[CodeGen | str] = content if content is not None else [] def gen(self, indent: str = "") -> list[str]: """ Return an array code lines. """ out: list[str] = [] if self.indent is None: indent = "" else: indent += self.indent for item in self.content: if isinstance(item, str): out.append(indent + item) else: out += item.gen(indent) return out def __str__(self) -> str: """ Return the generated code as one big string. """ return "\n".join([i.rstrip() for i in self.gen()]) class Conditional(CodeGen): """ A conditional block of code. """ def __init__(self, opt: Options, indent: str | None, condition: bool | None, content: Sequence[CodeGen | str]) -> None: """ The class constructor. """ super().__init__(opt, indent) if condition: self.content = content class Conditional2(CodeGen): """ A conditional block of code with an else block. """ def __init__( self, opt: Options, indent: str | None, condition: bool | None, content_true: Sequence[CodeGen | str], content_false: Sequence[CodeGen | str], ) -> None: """ The class constructor. """ super().__init__(opt, indent) if condition: self.content = content_true else: self.content = content_false class Comment(CodeGen): """ A comment wrapper. """ def __init__(self, opt: Options, indent: str | None, content: Sequence[CodeGen | str]) -> None: """ The class constructor. """ super().__init__(opt, indent) self.content = ["/**", CodeGen(opt, (indent or "") + " * ", content), " */"] class ParamBlock(CodeGen): """ Render the parameters of the model. """ def __init__(self, opt: Options, indent: str | None, algorithm: bool = False) -> None: """ The class constructor. """ super().__init__(opt, indent) self.content = [ "- {0:13s} = {1}".format("Width", self.sym.crc_width), "- {0:13s} = {1}".format("Poly", self.sym.crc_poly), "- {0:13s} = {1}".format("XorIn", self.sym.crc_xor_in), "- {0:13s} = {1}".format("ReflectIn", self.sym.crc_reflect_in), "- {0:13s} = {1}".format("XorOut", self.sym.crc_xor_out), "- {0:13s} = {1}".format("ReflectOut", self.sym.crc_reflect_out), Conditional(opt, "", algorithm, ["- {0:13s} = {1}".format("Algorithm", self.sym.crc_algorithm)]), Conditional(opt, "", opt.slice_by > 1, ["- {0:13s} = {1}".format("SliceBy", opt.slice_by)]), ] class File(CodeGen): """ Generate the code for a C/C++ source file (header or implementation). """ def __init__(self, opt: Options, indent: str | None) -> None: """ The class constructor. """ super().__init__(opt, indent) self.content = [] if opt.action == opt.action_generate_h: self.content = self._code_file() + self._header_file() if opt.action == opt.action_generate_c: self.content = self._code_file() + self._c_file() if opt.action == opt.action_generate_c_main: self.content = self._code_file() + self._c_file() + self._main_file() if opt.action == opt.action_generate_table: self.content = [f"{self.sym.crc_table_init}"] def _code_file(self) -> list[CodeGen | str]: """ Add code file """ out: list[CodeGen | str] = [ Comment( self.opt, "", [ "\\file", "Functions and types for CRC checks.", "", f"Generated on {self.sym.datetime}", f"by {self.sym.program_version}, {self.sym.program_url}", "using the configuration:", ParamBlock(self.opt, " ", algorithm=True), Conditional( self.opt, "", self.opt.action == self.opt.action_generate_h, [ "", f"This file defines the functions {self.sym.crc_init_function}(), {self.sym.crc_update_function}() and {self.sym.crc_finalize_function}().", "", f"The {self.sym.crc_init_function}() function returns the initial \\c crc value and must be called", f"before the first call to {self.sym.crc_update_function}().", f"Similarly, the {self.sym.crc_finalize_function}() function must be called after the last call", f"to {self.sym.crc_update_function}(), before the \\c crc is being used.", "", f"The {self.sym.crc_update_function}() function can be called any number of times (including zero", f"times) in between the {self.sym.crc_init_function}() and {self.sym.crc_finalize_function}() calls.", "", "This pseudo-code shows an example usage of the API:", "\\code{.c}", Conditional( self.opt, "", self.opt.undefined_crc_parameters, [ f"{self.sym.cfg_t} cfg = " + "{", Conditional( self.opt, 4 * " ", self.opt.width is None, [ "0, // width", ], ), Conditional( self.opt, 4 * " ", self.opt.poly is None, [ "0, // poly", ], ), Conditional( self.opt, 4 * " ", self.opt.reflect_in is None, [ "0, // reflect_in", ], ), Conditional( self.opt, 4 * " ", self.opt.xor_in is None, [ "0, // xor_in", ], ), Conditional( self.opt, 4 * " ", self.opt.reflect_out is None, [ "0, // reflect_out", ], ), Conditional( self.opt, 4 * " ", self.opt.xor_out is None, [ "0, // xor_out", ], ), Conditional( self.opt, 4 * " ", self.opt.width is None, [ "", "0, // crc_mask", "0, // msb_mask", "0, // crc_shift", ], ), "};", ], ), f"{self.sym.crc_t} crc;", "unsigned char data[MAX_DATA_LEN];", "size_t data_len;", "", Conditional( self.opt, "", _use_crc_table_gen(self.opt), [ f"{self.sym.crc_table_gen_function}(&cfg);", ], ), "crc = {0}({1});".format( self.sym.crc_init_function, "" if _use_constant_crc_init(self.sym) else "&cfg" ), "while ((data_len = read_data(data, MAX_DATA_LEN)) > 0) {", CodeGen( self.opt, 4 * " ", [ "crc = {0}({1}crc, data, data_len);".format( self.sym.crc_update_function, "" if _crc_update_without_cfg(self.opt) else "&cfg, " ), ], ), "}", "crc = {0}({1}crc);".format( self.sym.crc_finalize_function, "" if _crc_finalize_without_cfg(self.opt) else "&cfg, " ), "\\endcode", ], ), ], ), ] return out def _header_file(self) -> list[CodeGen | str]: """ Generate the content of a header file. """ out: list[CodeGen | str] = [ f"#ifndef {self.sym.header_protection}", f"#define {self.sym.header_protection}", "", CodeGen(self.opt, "", _includes(self.opt)), "#include ", Conditional(self.opt, "", self.opt.c_std != "C89", ["#include "]), Conditional(self.opt, "", _use_cfg(self.opt) and self.opt.c_std != "C89", ["#include "]), "", "#ifdef __cplusplus", 'extern "C" {', "#endif", "", "", Comment( self.opt, "", [ "The definition of the used algorithm.", "", "This is not used anywhere in the generated code, but it may be used by the", "application code to call algorithm-specific code, if desired.", ], ), f"#define {_crc_algo_define(self.opt, self.sym)} 1", "", "", Comment( self.opt, self.indent, [ "The type of the CRC values.", "", f"This type must be big enough to contain at least {self.sym.cfg_width} bits.", ], ), f"typedef {self.sym.underlying_crc_t} {self.sym.crc_t};", Conditional( self.opt, "", _use_cfg(self.opt), [ "", "", Comment(self.opt, self.indent, ["The configuration type of the CRC algorithm."]), "typedef struct {", Conditional( self.opt, 4 * " ", self.opt.width is None, ["{0:24s} {1}".format("unsigned int width;", "/*!< The width of the polynomial */")], ), Conditional( self.opt, 4 * " ", self.opt.poly is None, ["{0:24s} {1}".format(self.sym.crc_t + " poly;", "/*!< The CRC polynomial */")], ), Conditional( self.opt, 4 * " ", self.opt.reflect_in is None, [ "{0:24s} {1}".format( self.sym.c_bool + " reflect_in;", "/*!< Whether the input shall be reflected or not */" ) ], ), Conditional( self.opt, 4 * " ", self.opt.xor_in is None, ["{0:24s} {1}".format(self.sym.crc_t + " xor_in;", "/*!< The initial value of the register */")], ), Conditional( self.opt, 4 * " ", self.opt.reflect_out is None, [ "{0:24s} {1}".format( self.sym.c_bool + " reflect_out;", "/*!< Whether the output shall be reflected or not */" ) ], ), Conditional( self.opt, 4 * " ", self.opt.xor_out is None, [ "{0:24s} {1}".format( self.sym.crc_t + " xor_out;", "/*!< The value which shall be XOR-ed to the final CRC value */" ) ], ), Conditional( self.opt, 4 * " ", self.opt.width is None, [ "", "/* internal parameters */", "{0:24s} {1}".format( self.sym.crc_t + " msb_mask;", "/*!< a bitmask with the Most Significant Bit set to 1" ), 33 * " " + f"initialise as ({self.sym.crc_t})1u << (width - 1) */", "{0:24s} {1}".format( self.sym.crc_t + " crc_mask;", "/*!< a bitmask with all width bits set to 1" ), 33 * " " + "initialise as (cfg->msb_mask - 1) | cfg->msb_mask */", "{0:24s} {1}".format( "unsigned int crc_shift;", "/*!< a shift count that is used when width < 8" ), 33 * " " + "initialise as cfg->width < 8 ? 8 - cfg->width : 0 */", ], ), f"}} {self.sym.cfg_t};", ], ), Conditional( self.opt, "", _use_reflect_func(self.opt) and not _use_static_reflect_func(self.opt), [ "", "", Comment( self.opt, "", [ "Reflect all bits of a \\a data word of \\a data_len bytes.", "", "\\param[in] data The data word to be reflected.", "\\param[in] data_len The width of \\a data expressed in number of bits.", "\\return The reflected data.", ], ), f"{self.sym.crc_t} {self.sym.crc_reflect_function}({self.sym.crc_t} data, size_t data_len);", ], ), Conditional( self.opt, "", _use_crc_table_gen(self.opt), [ "", "", Comment( self.opt, "", [ "Populate the private static crc table.", "", f"\\param[in] cfg A pointer to an initialised {self.sym.cfg_t} structure.", ], ), f"void {self.sym.crc_table_gen_function}(const {self.sym.cfg_t} *cfg);", ], ), "", "", Comment( self.opt, "", [ "Calculate the initial crc value.", "", Conditional( self.opt, "", _use_cfg(self.opt), [ f"\\param[in] cfg A pointer to an initialised {self.sym.cfg_t} structure.", ], ), "\\return The initial crc value.", ], ), Conditional2( self.opt, "", _use_constant_crc_init(self.sym), [ Conditional2( self.opt, "", self.opt.c_std == "C89", [ f"#define {self.sym.crc_init_function}() ({self.sym.crc_init_value})", ], [ f"static inline {_crc_init_function_def(self.opt, self.sym)}", "{", f" return {self.sym.crc_init_value};", "}", ], ), ], [ f"{_crc_init_function_def(self.opt, self.sym)};", ], ), "", "", Comment( self.opt, "", [ "Update the crc value with new data.", "", "\\param[in] crc The current crc value.", Conditional( self.opt, "", not _crc_update_without_cfg(self.opt), [ f"\\param[in] cfg A pointer to an initialised {self.sym.cfg_t} structure.", ], ), "\\param[in] data Pointer to a buffer of \\a data_len bytes.", "\\param[in] data_len Number of bytes in the \\a data buffer.", "\\return The updated crc value.", ], ), f"{_crc_update_function_def(self.opt, self.sym)};", "", "", Comment( self.opt, "", [ "Calculate the final crc value.", "", Conditional( self.opt, "", not _crc_finalize_without_cfg(self.opt), [ f"\\param[in] cfg A pointer to an initialised {self.sym.cfg_t} structure.", ], ), "\\param[in] crc The current crc value.", "\\return The final crc value.", ], ), Conditional2( self.opt, "", _use_inline_crc_finalize(self.opt), [ Conditional2( self.opt, "", self.opt.c_std == "C89", [ f"#define {self.sym.crc_finalize_function}(crc) ({_crc_final_value(self.opt, self.sym)})", ], [ f"static inline {_crc_finalize_function_def(self.opt, self.sym)}", "{", f" return {_crc_final_value(self.opt, self.sym)};", "}", ], ), ], [ f"{_crc_finalize_function_def(self.opt, self.sym)};", ], ), "", "", "#ifdef __cplusplus", '} /* closing brace for extern "C" */', "#endif", "", f"#endif /* {self.sym.header_protection} */", "", ] return out def _c_file(self) -> list[CodeGen | str]: """ Generate the content of a C/C++ source file. """ out: list[CodeGen | str] = [ CodeGen(self.opt, "", _includes(self.opt)), f'#include "{self.sym.header_filename}" /* include the header file generated with pycrc */', "#include ", Conditional( self.opt, "", self.opt.c_std != "C89", [ "#include ", Conditional( self.opt, "", self.opt.undefined_crc_parameters or self.opt.algorithm == self.opt.algo_bit_by_bit or self.opt.algorithm == self.opt.algo_bit_by_bit_fast, [ "#include ", ], ), ], ), Conditional( self.opt, "", self.opt.slice_by > 1, [ "#include ", ], ), Conditional( self.opt, "", _use_reflect_func(self.opt) and _use_static_reflect_func(self.opt), [ "", f"static {self.sym.crc_t} {self.sym.crc_reflect_function}({self.sym.crc_t} data, size_t data_len);", ], ), "", CodeGen(self.opt, "", _crc_table(self.opt, self.sym)), CodeGen(self.opt, "", _crc_reflect_function_gen(self.opt, self.sym)), CodeGen(self.opt, "", _crc_init_function_gen(self.opt, self.sym)), CodeGen(self.opt, "", _crc_table_gen(self.opt, self.sym)), CodeGen(self.opt, "", _crc_update_function_gen(self.opt, self.sym)), CodeGen(self.opt, "", _crc_finalize_function_gen(self.opt, self.sym)), "", ] return out def _main_file(self) -> list[CodeGen | str]: """ Generate the content of a main source file. """ out: list[CodeGen | str] = [ "", "", CodeGen(self.opt, "", _includes(self.opt)), "#include ", "#include ", Conditional( self.opt, "", self.opt.undefined_crc_parameters, [ "#include ", "#include ", ], ), Conditional( self.opt, "", self.opt.c_std != "C89", [ "#include ", ], ), "#include ", "", 'static char str[256] = "123456789";', f"static {self.sym.c_bool} verbose = {self.sym.c_false};", self._getopt_template(), "", "", Conditional2( self.opt, "", self.opt.undefined_crc_parameters, [ f"static void print_params(const {self.sym.cfg_t} *cfg)", ], [ "static void print_params(void)", ], ), "{", CodeGen( self.opt, 4 * " ", [ "char format[32];", "", Conditional2( self.opt, "", self.opt.c_std == "C89", [ f'sprintf(format, "%%-16s = 0x%%0%dlx\\n", (unsigned int)({self.sym.cfg_width} + 3) / 4);', f'printf("%-16s = %d\\n", "width", (unsigned int){self.sym.cfg_width});', f'printf(format, "poly", (unsigned long int){self.sym.cfg_poly});', 'printf("%-16s = %s\\n", "reflect_in", {0});'.format( self.sym.cfg_reflect_in + ' ? "true": "false"' if self.opt.reflect_in is None else ('"true"' if self.opt.reflect_in else '"false"') ), f'printf(format, "xor_in", (unsigned long int){self.sym.cfg_xor_in});', 'printf("%-16s = %s\\n", "reflect_out", {0});'.format( self.sym.cfg_reflect_out + ' ? "true": "false"' if self.opt.reflect_out is None else ('"true"' if self.opt.reflect_out else '"false"') ), f'printf(format, "xor_out", (unsigned long int){self.sym.cfg_xor_out});', f'printf(format, "crc_mask", (unsigned long int){self.sym.cfg_mask});', f'printf(format, "msb_mask", (unsigned long int){self.sym.cfg_msb_mask});', ], [ f'snprintf(format, sizeof(format), "%%-16s = 0x%%0%dllx\\n", (unsigned int)({self.sym.cfg_width} + 3) / 4);', f'printf("%-16s = %d\\n", "width", (unsigned int){self.sym.cfg_width});', f'printf(format, "poly", (unsigned long long int){self.sym.cfg_poly});', 'printf("%-16s = %s\\n", "reflect_in", {0});'.format( self.sym.cfg_reflect_in + ' ? "true": "false"' if self.opt.reflect_in is None else ('"true"' if self.opt.reflect_in else '"false"') ), f'printf(format, "xor_in", (unsigned long long int){self.sym.cfg_xor_in});', 'printf("%-16s = %s\\n", "reflect_out", {0});'.format( self.sym.cfg_reflect_out + ' ? "true": "false"' if self.opt.reflect_out is None else ('"true"' if self.opt.reflect_out else '"false"') ), f'printf(format, "xor_out", (unsigned long long int){self.sym.cfg_xor_out});', f'printf(format, "crc_mask", (unsigned long long int){self.sym.cfg_mask});', f'printf(format, "msb_mask", (unsigned long long int){self.sym.cfg_msb_mask});', ], ), ], ), "}", "", "", Comment( self.opt, "", [ "C main function.", "\\param[in] argc the number of arguments in \\a argv.", "\\param[in] argv a NULL-terminated array of pointers to the argument strings.", "\\retval 0 on success.", "\\retval >0 on error.", ], ), "int main(int argc, char *argv[])", "{", CodeGen( self.opt, 4 * " ", [ Conditional( self.opt, "", self.opt.undefined_crc_parameters, [ f"{self.sym.cfg_t} cfg = " + "{", Conditional( self.opt, 4 * " ", self.opt.width is None, [ "0, /* width */", ], ), Conditional( self.opt, 4 * " ", self.opt.poly is None, [ "0, /* poly */", ], ), Conditional( self.opt, 4 * " ", self.opt.reflect_in is None, [ "0, /* reflect_in */", ], ), Conditional( self.opt, 4 * " ", self.opt.xor_in is None, [ "0, /* xor_in */", ], ), Conditional( self.opt, 4 * " ", self.opt.reflect_out is None, [ "0, /* reflect_out */", ], ), Conditional( self.opt, 4 * " ", self.opt.xor_out is None, [ "0, /* xor_out */", ], ), Conditional( self.opt, 4 * " ", self.opt.width is None, [ "", "0, /* crc_mask */", "0, /* msb_mask */", "0, /* crc_shift */", ], ), "};", ], ), f"{self.sym.crc_t} crc;", "", Conditional2( self.opt, "", self.opt.undefined_crc_parameters, [ "get_config(argc, argv, &cfg);", ], [ "get_config(argc, argv);", ], ), Conditional( self.opt, "", _use_crc_table_gen(self.opt), [ f"{self.sym.crc_table_gen_function}(&cfg);", ], ), "crc = {0}({1});".format(self.sym.crc_init_function, "" if _use_constant_crc_init(self.sym) else "&cfg"), "crc = {0}({1}crc, (void *)str, strlen(str));".format( self.sym.crc_update_function, "" if _crc_update_without_cfg(self.opt) else "&cfg, " ), "crc = {0}({1}crc);".format( self.sym.crc_finalize_function, "" if _crc_finalize_without_cfg(self.opt) else "&cfg, " ), "", "if (verbose) {", CodeGen( self.opt, 4 * " ", [ "print_params({0});".format("&cfg" if self.opt.undefined_crc_parameters else ""), ], ), "}", Conditional2( self.opt, "", self.opt.c_std == "C89", [ 'printf("0x%lx\\n", (unsigned long int)crc);', ], [ 'printf("0x%llx\\n", (unsigned long long int)crc);', ], ), "return 0;", ], ), "}", ] return out def _getopt_template(self) -> CodeGen: """ Add option handler code. """ out: list[CodeGen | str] = [ Conditional( self.opt, "", self.opt.reflect_in is None or self.opt.reflect_out is None, [ "", "", f"static {self.sym.c_bool} atob(const char *str)", "{", CodeGen( self.opt, 4 * " ", [ "if (!str) {", CodeGen( self.opt, 4 * " ", [ "return 0;", ], ), "}", "if (isdigit(str[0])) {", CodeGen( self.opt, 4 * " ", [ f"return ({self.sym.c_bool})atoi(str);", ], ), "}", "if (tolower(str[0]) == 't') {", CodeGen( self.opt, 4 * " ", [ f"return {self.sym.c_true};", ], ), "}", f"return {self.sym.c_false};", ], ), "}", ], ), Conditional( self.opt, "", self.opt.poly is None or self.opt.xor_in is None or self.opt.xor_out is None, [ "", "", f"static {self.sym.crc_t} xtoi(const char *str)", "{", CodeGen( self.opt, 4 * " ", [ f"{self.sym.crc_t} ret = 0;", "", "if (!str) {", CodeGen( self.opt, 4 * " ", [ "return 0;", ], ), "}", "if (str[0] == '0' && tolower(str[1]) == 'x') {", CodeGen( self.opt, 4 * " ", [ "str += 2;", "while (*str) {", CodeGen( self.opt, 4 * " ", [ "if (isdigit(*str))", CodeGen( self.opt, 4 * " ", [ "ret = 16 * ret + *str - '0';", ], ), "else if (isxdigit(*str))", CodeGen( self.opt, 4 * " ", [ "ret = 16 * ret + tolower(*str) - 'a' + 10;", ], ), "else", CodeGen( self.opt, 4 * " ", [ "return ret;", ], ), "str++;", ], ), "}", ], ), "} else if (isdigit(*str)) {", CodeGen( self.opt, 4 * " ", [ "while (*str) {", CodeGen( self.opt, 4 * " ", [ "if (isdigit(*str))", CodeGen( self.opt, 4 * " ", [ "ret = 10 * ret + *str - '0';", ], ), "else", CodeGen( self.opt, 4 * " ", [ "return ret;", ], ), "str++;", ], ), "}", ], ), "}", "return ret;", ], ), "}", ], ), "", "", Conditional2( self.opt, "", self.opt.undefined_crc_parameters, [ f"static int get_config(int argc, char *argv[], {self.sym.cfg_t} *cfg)", ], [ "static int get_config(int argc, char *argv[])", ], ), "{", CodeGen( self.opt, 4 * " ", [ "int c;", "int option_index;", "static struct option long_options[] = {", CodeGen( self.opt, 4 * " ", [ Conditional( self.opt, "", self.opt.width is None, [ "{\"width\", 1, 0, 'w'},", ], ), Conditional( self.opt, "", self.opt.poly is None, [ "{\"poly\", 1, 0, 'p'},", ], ), Conditional( self.opt, "", self.opt.reflect_in is None, [ "{\"reflect-in\", 1, 0, 'n'},", ], ), Conditional( self.opt, "", self.opt.xor_in is None, [ "{\"xor-in\", 1, 0, 'i'},", ], ), Conditional( self.opt, "", self.opt.reflect_out is None, [ "{\"reflect-out\", 1, 0, 'u'},", ], ), Conditional( self.opt, "", self.opt.xor_out is None, [ "{\"xor-out\", 1, 0, 'o'},", ], ), "{\"verbose\", 0, 0, 'v'},", "{\"check-string\", 1, 0, 's'},", Conditional( self.opt, "", self.opt.width is None, [ "{\"table-idx-width\", 1, 0, 't'},", ], ), "{0, 0, 0, 0}", ], ), "};", "", "while (1) {", CodeGen( self.opt, 4 * " ", [ "option_index = 0;", "", 'c = getopt_long(argc, argv, "w:p:n:i:u:o:s:vt:", long_options, &option_index);', "if (c == -1)", CodeGen( self.opt, 4 * " ", [ "break;", ], ), "", "switch (c) {", CodeGen( self.opt, 4 * " ", [ "case 0:", CodeGen( self.opt, 4 * " ", [ 'printf("option %s", long_options[option_index].name);', "if (optarg)", CodeGen( self.opt, 4 * " ", [ 'printf(" with arg %s", optarg);', ], ), 'printf("\\n");', "break;", ], ), Conditional( self.opt, "", self.opt.width is None, [ "case 'w':", CodeGen( self.opt, 4 * " ", [ "cfg->width = atoi(optarg);", "break;", ], ), ], ), Conditional( self.opt, "", self.opt.poly is None, [ "case 'p':", CodeGen( self.opt, 4 * " ", [ "cfg->poly = xtoi(optarg);", "break;", ], ), ], ), Conditional( self.opt, "", self.opt.reflect_in is None, [ "case 'n':", CodeGen( self.opt, 4 * " ", [ "cfg->reflect_in = atob(optarg);", "break;", ], ), ], ), Conditional( self.opt, "", self.opt.xor_in is None, [ "case 'i':", CodeGen( self.opt, 4 * " ", [ "cfg->xor_in = xtoi(optarg);", "break;", ], ), ], ), Conditional( self.opt, "", self.opt.reflect_out is None, [ "case 'u':", CodeGen( self.opt, 4 * " ", [ "cfg->reflect_out = atob(optarg);", "break;", ], ), ], ), Conditional( self.opt, "", self.opt.xor_out is None, [ "case 'o':", CodeGen( self.opt, 4 * " ", [ "cfg->xor_out = xtoi(optarg);", "break;", ], ), ], ), "case 's':", CodeGen( self.opt, 4 * " ", [ "memcpy(str, optarg, strlen(optarg) < sizeof(str) ? strlen(optarg) + 1 : sizeof(str));", "str[sizeof(str) - 1] = '\\0';", "break;", ], ), "case 'v':", CodeGen( self.opt, 4 * " ", [ f"verbose = {self.sym.c_true};", "break;", ], ), Conditional( self.opt, "", self.opt.width is None, [ "case 't':", CodeGen( self.opt, 4 * " ", [ "/* ignore --table_idx_width option */", "break;", ], ), ], ), "case '?':", CodeGen( self.opt, 4 * " ", [ "return -1;", ], ), "case ':':", CodeGen( self.opt, 4 * " ", [ 'fprintf(stderr, "missing argument to option %c\\n", c);', "return -1;", ], ), "default:", CodeGen( self.opt, 4 * " ", [ 'fprintf(stderr, "unhandled option %c\\n", c);', "return -1;", ], ), ], ), "}", ], ), "}", Conditional( self.opt, "", self.opt.width is None, [ f"cfg->msb_mask = ({self.sym.crc_t})1u << (cfg->width - 1);", "cfg->crc_mask = (cfg->msb_mask - 1) | cfg->msb_mask;", "cfg->crc_shift = cfg->width < 8 ? 8 - cfg->width : 0;", ], ), "", Conditional( self.opt, "", self.opt.poly is None, [ f"cfg->poly &= {self.sym.cfg_mask};", ], ), Conditional( self.opt, "", self.opt.xor_in is None, [ f"cfg->xor_in &= {self.sym.cfg_mask};", ], ), Conditional( self.opt, "", self.opt.xor_out is None, [ f"cfg->xor_out &= {self.sym.cfg_mask};", ], ), "return 0;", ], ), "}", ] return CodeGen(self.opt, "", out) def _includes(opt: Options) -> list[str]: """ Return the #include directives for the user-defined list of include files. """ includes: list[str] = [] if opt.include_files is not None and len(opt.include_files) > 0: for include_file in opt.include_files: if include_file[0] == '"' or include_file[0] == "<": includes.append(f"#include {include_file}") else: includes.append(f'#include "{include_file}"') return includes def _crc_algo_define(opt: Options, sym: SymbolTable) -> str: """ Get the identifier of the algorithm for the header file. """ name = sym.crc_algorithm.upper().replace("-", "_") return "CRC_ALGO_" + name def _use_cfg(opt: Options) -> bool: """ Return True if a cfg_t structure is to be used. """ return opt.undefined_crc_parameters def _use_constant_crc_init(sym: SymbolTable) -> bool: """ Return True if the inintial value is constant. """ return sym.crc_init_value is not None def _use_reflect_func(opt: Options) -> bool: """ Return True if the reflect function is to be used. """ if opt.reflect_out is None or opt.reflect_in is None: return True if opt.algorithm == opt.algo_table_driven: if opt.reflect_in and opt.reflect_out: return True if opt.reflect_in != opt.reflect_out: return True if opt.algorithm == opt.algo_bit_by_bit: if opt.reflect_in: return True if opt.reflect_out: return True if opt.algorithm == opt.algo_bit_by_bit_fast: if opt.reflect_in: return True if opt.reflect_out: return True return False def _use_static_reflect_func(opt: Options) -> bool: """ Whether a static reflect function is to be used. """ if opt.algorithm == opt.algo_table_driven: return False return not (opt.reflect_out is not None and opt.algorithm == opt.algo_bit_by_bit_fast) def _use_crc_table_gen(opt: Options) -> bool: """ Return True if the table generator function is to be generated. """ if opt.algorithm == opt.algo_table_driven: return opt.width is None or opt.poly is None or opt.reflect_in is None else: return False def _crc_init_function_def(opt: Options, sym: SymbolTable) -> str: """ The definition for the init function. """ if _use_constant_crc_init(sym): return f"{sym.crc_t} {sym.crc_init_function}(void)" else: return f"{sym.crc_t} {sym.crc_init_function}(const {sym.cfg_t} *cfg)" def _crc_update_without_cfg(opt: Options) -> bool: """ Return True if the update function can be generated without the cfg_t parameter, because all required parameters are compile-time constants. """ return ( opt.algorithm in {opt.algo_bit_by_bit, opt.algo_bit_by_bit_fast} and opt.width is not None and opt.poly is not None and opt.reflect_in is not None ) or (opt.algorithm == opt.algo_table_driven and opt.width is not None and opt.reflect_in is not None) def _crc_update_function_def(opt: Options, sym: SymbolTable) -> str: """ The definition of the update function. """ if _crc_update_without_cfg(opt): return f"{sym.crc_t} {sym.crc_update_function}({sym.crc_t} crc, const void *data, size_t data_len)" else: return f"{sym.crc_t} {sym.crc_update_function}(const {sym.cfg_t} *cfg, {sym.crc_t} crc, const void *data, size_t data_len)" def _crc_finalize_without_cfg(opt: Options) -> bool: """ Return True if the finalize function can be generated without the cfg_t parameter, because all required parameters are compile-time constants. """ return ( ( opt.algorithm == opt.algo_bit_by_bit and opt.width is not None and opt.poly is not None and opt.reflect_out is not None and opt.xor_out is not None ) or ( opt.algorithm == opt.algo_bit_by_bit_fast and opt.width is not None and opt.reflect_out is not None and opt.xor_out is not None ) or ( opt.algorithm == opt.algo_table_driven and opt.width is not None and opt.reflect_in is not None and opt.reflect_out is not None and opt.xor_out is not None ) ) def _use_inline_crc_finalize(opt: Options) -> bool: """ Return True if the finalize function can be inlined. """ return ( opt.algorithm in {opt.algo_bit_by_bit_fast, opt.algo_table_driven} and opt.width is not None and opt.reflect_in is not None and opt.reflect_out is not None and opt.xor_out is not None ) def _use_constant_crc_table(opt: Options) -> bool: """ Return True if the CRC table is constant. """ return opt.width is not None and opt.poly is not None and opt.reflect_in is not None def _crc_finalize_function_def(opt: Options, sym: SymbolTable) -> str: """ The definition of the finalize function. """ if _crc_finalize_without_cfg(opt): return f"{sym.crc_t} {sym.crc_finalize_function}({sym.crc_t} crc)" else: return f"{sym.crc_t} {sym.crc_finalize_function}(const {sym.cfg_t} *cfg, {sym.crc_t} crc)" def _crc_final_value(opt: Options, sym: SymbolTable) -> expr.Expression: """ The return value for the finalize function. """ if opt.algorithm == opt.algo_table_driven: if opt.reflect_in == opt.reflect_out: return expr.Xor("crc", sym.crc_xor_out).simplify() else: reflect_fun = expr.FunctionCall(sym.crc_reflect_function, ["crc", sym.crc_width]) return expr.Xor(reflect_fun, sym.crc_xor_out).simplify() if opt.reflect_out: reflect_fun = expr.FunctionCall(sym.crc_reflect_function, ["crc", sym.crc_width]) return expr.Xor(reflect_fun, sym.crc_xor_out).simplify() return expr.Xor("crc", sym.crc_xor_out).simplify() def _crc_table(opt: Options, sym: SymbolTable) -> list[CodeGen | str]: """ Return the code for the CRC table or the generator function. """ if opt.algorithm != opt.algo_table_driven: return [] return [ "", "", Comment( opt, "", [ "Static table used for the table_driven implementation.", Conditional( opt, "", opt.undefined_crc_parameters, [ f"Must be initialised with the {sym.crc_table_gen_function} function.", ], ), ], ), Conditional2( opt, "", _use_constant_crc_table(opt), [ Conditional2( opt, "", opt.slice_by > 1, [ f"static const {sym.crc_t} crc_table[{sym.crc_slice_by}][{sym.crc_table_width}] = {sym.crc_table_init};", ], [ f"static const {sym.crc_t} crc_table[{sym.crc_table_width}] = {sym.crc_table_init};", ], ), ], [ f"static {sym.crc_t} crc_table[{sym.crc_table_width}];", ], ), ] def _crc_table_gen(opt: Options, sym: SymbolTable) -> list[CodeGen | str]: """ Return the code for the CRC table or the generator function. """ if opt.algorithm != opt.algo_table_driven or _use_constant_crc_table(opt): return [] return [ "", "", f"void {sym.crc_table_gen_function}(const {sym.cfg_t} *cfg)", "{", CodeGen( opt, 4 * " ", [ f"{sym.crc_t} crc;", "unsigned int i, j;", "", f"for (i = 0; i < {sym.cfg_table_width}; i++) " + "{", CodeGen( opt, 4 * " ", [ Conditional2( opt, "", opt.reflect_in is None, [ "if (cfg->reflect_in) {", CodeGen( opt, 4 * " ", [ f"crc = {sym.crc_reflect_function}(i, {sym.cfg_table_idx_width});", ], ), "} else {", CodeGen( opt, 4 * " ", [ "crc = i;", ], ), "}", ], [ Conditional2( opt, "", opt.reflect_in, [ f"crc = {sym.crc_reflect_function}(i, {sym.cfg_table_idx_width});", ], [ "crc = i;", ], ), ], ), f"crc <<= {expr.Parenthesis(expr.Add(expr.Sub(sym.cfg_width, sym.cfg_table_idx_width), sym.cfg_shift)).simplify()};", f"for (j = 0; j < {sym.cfg_table_idx_width}; j++) " + "{", CodeGen( opt, 4 * " ", [ f"if (crc & {sym.cfg_msb_mask_shifted}) " + "{", CodeGen( opt, 4 * " ", [ "crc = {0};".format( expr.Xor(expr.Parenthesis(expr.Shl("crc", 1)), sym.cfg_poly_shifted).simplify() ), ], ), "} else {", CodeGen( opt, 4 * " ", [ "crc = crc << 1;", ], ), "}", ], ), "}", Conditional( opt, "", opt.reflect_in is None, [ "if (cfg->reflect_in) {", Conditional2( opt, 4 * " ", sym.tbl_shift is None or sym.tbl_shift > 0, [ "crc = {0};".format( expr.Shl( expr.FunctionCall( sym.crc_reflect_function, [expr.Shr("crc", sym.cfg_shift), sym.cfg_width] ), sym.cfg_shift, ).simplify() ), ], [ f"crc = {sym.crc_reflect_function}(crc, {sym.cfg_width});", ], ), "}", ], ), Conditional( opt, "", opt.reflect_in, [ Conditional2( opt, "", sym.tbl_shift is None or sym.tbl_shift > 0, [ "crc = {0};".format( expr.Shl( expr.FunctionCall( sym.crc_reflect_function, [expr.Shr("crc", sym.cfg_shift), sym.cfg_width] ), sym.cfg_shift, ).simplify() ), ], [ f"crc = {sym.crc_reflect_function}(crc, {sym.cfg_width});", ], ), ], ), "crc_table[i] = {0};".format( expr.Shr(expr.Parenthesis(expr.And("crc", sym.cfg_mask_shifted)), sym.cfg_shift) ), ], ), "}", ], ), "}", ] def _crc_reflect_function_gen(opt: Options, sym: SymbolTable) -> list[CodeGen | str]: """ Return the code for the reflect functon. """ if not _use_reflect_func(opt): return [] if not (opt.reflect_in is None or opt.reflect_in or opt.reflect_out is None or opt.reflect_out): return [] return [ "", "", f"{sym.crc_t} {sym.crc_reflect_function}({sym.crc_t} data, size_t data_len)", "{", CodeGen( opt, 4 * " ", [ "unsigned int i;", f"{sym.crc_t} ret;", "", "ret = data & 0x01;", "for (i = 1; i < data_len; i++) {", CodeGen( opt, 4 * " ", [ "data >>= 1;", "ret = (ret << 1) | (data & 0x01);", ], ), "}", "return ret;", ], ), "}", ] def _crc_init_function_gen(opt: Options, sym: SymbolTable) -> list[CodeGen | str]: """ Return the code for the init function. """ if _use_constant_crc_init(sym): return [] out: list[CodeGen | str] = [ "", "", _crc_init_function_def(opt, sym), "{", CodeGen( opt, 4 * " ", [ Conditional( opt, "", opt.algorithm == opt.algo_bit_by_bit, [ "unsigned int i;", f"{sym.c_bool} bit;", f"{sym.crc_t} crc = {sym.cfg_xor_in};", f"for (i = 0; i < {sym.cfg_width}; i++) " + "{", CodeGen( opt, 4 * " ", [ "bit = crc & 0x01;", "if (bit) {", CodeGen( opt, 4 * " ", [ f"crc = ((crc ^ {sym.cfg_poly}) >> 1) | {sym.cfg_msb_mask};", ], ), "} else {", CodeGen( opt, 4 * " ", [ "crc >>= 1;", ], ), "}", ], ), "}", f"return crc & {sym.cfg_mask};", ], ), Conditional( opt, "", opt.algorithm == opt.algo_bit_by_bit_fast, [ f"return {sym.cfg_xor_in} & {sym.cfg_mask};", ], ), Conditional( opt, "", opt.algorithm == opt.algo_table_driven, [ Conditional2( opt, "", opt.reflect_in is None, [ f"if ({sym.cfg_reflect_in}) " + "{", CodeGen( opt, 4 * " ", [ f"return {sym.crc_reflect_function}({sym.cfg_xor_in} & {sym.cfg_mask}, {sym.cfg_width});", ], ), "} else {", CodeGen( opt, 4 * " ", [ f"return {sym.cfg_xor_in} & {sym.cfg_mask};", ], ), "}", ], [ Conditional2( opt, "", opt.reflect_in, [ f"return {sym.crc_reflect_function}({sym.cfg_xor_in} & {sym.cfg_mask}, {sym.cfg_width});", ], [ f"return {sym.cfg_xor_in} & {sym.cfg_mask};", ], ), ], ), ], ), ], ), "}", ] return out def _crc_update_function_gen(opt: Options, sym: SymbolTable) -> list[CodeGen | str]: """ Return the code for the update function. """ out: list[CodeGen | str] = [ "", "", _crc_update_function_def(opt, sym), "{", CodeGen(opt, 4 * " ", ["const unsigned char *d = (const unsigned char *)data;"]), ] if opt.algorithm == opt.algo_bit_by_bit: out += [ CodeGen( opt, 4 * " ", [ "unsigned int i;", f"{sym.c_bool} bit;", "unsigned char c;", "", "while (data_len--) {", Conditional2( opt, 4 * " ", opt.reflect_in is None, [ "if (" + sym.cfg_reflect_in + ") {", CodeGen( opt, 4 * " ", [ f"c = {sym.crc_reflect_function}(*d++, 8);", ], ), "} else {", CodeGen( opt, 4 * " ", [ "c = *d++;", ], ), "}", ], [ Conditional2( opt, "", opt.reflect_in, [ f"c = {sym.crc_reflect_function}(*d++, 8);", ], [ "c = *d++;", ], ), ], ), CodeGen( opt, 4 * " ", [ "for (i = 0; i < 8; i++) {", CodeGen( opt, 4 * " ", [ Conditional2( opt, "", opt.c_std == "C89", [ f"bit = !!(crc & {sym.cfg_msb_mask});", ], [ f"bit = crc & {sym.cfg_msb_mask};", ], ), "crc = (crc << 1) | ((c >> (7 - i)) & 0x01);", "if (bit) {", CodeGen( opt, 4 * " ", [ f"crc ^= {sym.cfg_poly};", ], ), "}", ], ), "}", f"crc &= {sym.cfg_mask};", ], ), "}", f"return crc & {sym.cfg_mask};", ], ), ] if opt.algorithm == opt.algo_bit_by_bit_fast: out += [ CodeGen( opt, 4 * " ", [ "unsigned int i;", f"{sym.crc_t} bit;", "unsigned char c;", "", "while (data_len--) {", CodeGen( opt, 4 * " ", [ Conditional2( opt, "", opt.reflect_in is None, [ "if (" + sym.cfg_reflect_in + ") {", CodeGen( opt, 4 * " ", [ f"c = {sym.crc_reflect_function}(*d++, 8);", ], ), "} else {", CodeGen( opt, 4 * " ", [ "c = *d++;", ], ), "}", ], [ "c = *d++;", ], ), Conditional2( opt, "", opt.reflect_in, [ "for (i = 0x01; i & 0xff; i <<= 1) {", ], [ "for (i = 0x80; i > 0; i >>= 1) {", ], ), CodeGen( opt, 4 * " ", [ "bit = ({0}) ^ ({1});".format( expr.And("crc", sym.cfg_msb_mask).simplify(), f"(c & i) ? {sym.cfg_msb_mask} : 0" ), "crc <<= 1;", "if (bit) {", CodeGen( opt, 4 * " ", [ f"crc ^= {sym.cfg_poly};", ], ), "}", ], ), "}", f"crc &= {sym.cfg_mask};", ], ), "}", "return {0};".format(expr.And("crc", sym.cfg_mask).simplify()), ], ), ] if opt.algorithm == opt.algo_table_driven: out += [ CodeGen( opt, 4 * " ", [ "unsigned int tbl_idx;", "", Conditional2( opt, "", opt.reflect_in is None, [ "if (cfg->reflect_in) {", CodeGen( opt, 4 * " ", [ "while (data_len--) {", CodeGen( opt, 4 * " ", [ _crc_table_core_algorithm_reflected(opt, sym), "d++;", ], ), "}", ], ), "} else {", CodeGen( opt, 4 * " ", [ "while (data_len--) {", CodeGen( opt, 4 * " ", [ _crc_table_core_algorithm_nonreflected(opt, sym), "d++;", ], ), "}", ], ), "}", ], [ Conditional( opt, "", opt.slice_by > 1, [ f"/* Align to a multiple of {sym.crc_slice_by} bytes */", f"while (data_len && (((uintptr_t)(const void *)d) % {sym.crc_slice_by} != 0))" + " {", CodeGen( opt, 4 * " ", [ _crc_table_core_algorithm(opt, sym), "data_len--;", ], ), "}", "", _crc_table_slice_by_algorithm(opt, sym), "/* Remaining bytes with the standard algorithm */", "d = (const unsigned char *)d32;", ], ), "while (data_len--) {", CodeGen( opt, 4 * " ", [ _crc_table_core_algorithm(opt, sym), ], ), "}", ], ), "return {0};".format(expr.And("crc", sym.cfg_mask).simplify()), ], ), ] out += [ "}", ] return out def _crc_finalize_function_gen(opt: Options, sym: SymbolTable) -> list[CodeGen | str]: """ Return the code for the finalize function. """ if _use_inline_crc_finalize(opt): return [] out: list[CodeGen | str] = [ "", "", _crc_finalize_function_def(opt, sym), "{", ] if opt.algorithm in {opt.algo_bit_by_bit, opt.algo_bit_by_bit_fast}: out += [ Conditional( opt, 4 * " ", opt.algorithm == opt.algo_bit_by_bit, [ "unsigned int i;", f"{sym.c_bool} bit;", "", "for (i = 0; i < " + sym.cfg_width + "; i++) {", CodeGen( opt, 4 * " ", [ Conditional2( opt, "", opt.c_std == "C89", [f"bit = !!(crc & {sym.cfg_msb_mask});"], [ f"bit = crc & {sym.cfg_msb_mask};", ], ), "crc <<= 1;", "if (bit) {", CodeGen( opt, 4 * " ", [ f"crc ^= {sym.cfg_poly};", ], ), "}", ], ), "}", Conditional( opt, "", opt.reflect_out is None, [ "if (" + sym.cfg_reflect_out + ") {", CodeGen( opt, 4 * " ", [ f"crc = {sym.crc_reflect_function}(crc, {sym.cfg_width});", ], ), "}", ], ), Conditional( opt, "", opt.reflect_out, [ f"crc = {sym.crc_reflect_function}(crc, {sym.cfg_width});", ], ), ], ), Conditional( opt, 4 * " ", opt.algorithm == opt.algo_bit_by_bit_fast, [ Conditional( opt, "", opt.reflect_out is None, [ "if (" + sym.cfg_reflect_out + ") {", CodeGen( opt, 4 * " ", [ f"crc = {sym.crc_reflect_function}(crc, {sym.cfg_width});", ], ), "}", ], ), Conditional( opt, "", opt.reflect_out, [ f"crc = {sym.crc_reflect_function}(crc, {sym.cfg_width});", ], ), ], ), ] if opt.algorithm == opt.algo_table_driven: if opt.reflect_in is None or opt.reflect_out is None: if opt.reflect_in is None and opt.reflect_out is None: cond = "cfg->reflect_in != cfg->reflect_out" elif opt.reflect_out is None: cond = ("!" if opt.reflect_in else "") + "cfg->reflect_out" else: cond = ("!" if opt.reflect_out else "") + "cfg->reflect_in" out += [ CodeGen( opt, 4 * " ", [ "if (" + cond + ") {", CodeGen( opt, 4 * " ", [ f"crc = {sym.crc_reflect_function}(crc, {sym.cfg_width});", ], ), "}", ], ), ] elif opt.reflect_in != opt.reflect_out: out += [ f"crc = {sym.crc_reflect_function}(crc, {sym.cfg_width});", ] out += [ CodeGen( opt, 4 * " ", [ "return {0};".format(expr.And(expr.Parenthesis(expr.Xor("crc", sym.cfg_xor_out)), sym.cfg_mask).simplify()), ], ), "}", ] return out def _crc_table_core_algorithm(opt: Options, sym: SymbolTable) -> CodeGen: """ Return the core of the table-driven algorithm. """ out: list[CodeGen | str] = [] out += [ Conditional2( opt, "", opt.reflect_in, [ _crc_table_core_algorithm_reflected(opt, sym), ], [ _crc_table_core_algorithm_nonreflected(opt, sym), ], ), "d++;", ] return CodeGen(opt, "", out) def _crc_table_core_algorithm_reflected(opt: Options, sym: SymbolTable) -> CodeGen: """ Return the core loop of the table-driven algorithm, reflected variant. """ out: list[CodeGen | str] = [] if opt.width is not None and opt.table_idx_width is not None and opt.width <= opt.table_idx_width: crc_xor_expr = "0" else: crc_xor_expr = f"(crc >> {sym.cfg_table_idx_width})" if opt.table_idx_width == 8: crc_lookup = "crc_table[0][tbl_idx]" if opt.slice_by > 1 else "crc_table[tbl_idx]" crc_exp = expr.And( expr.Parenthesis(expr.Xor(crc_lookup, expr.Parenthesis(expr.Shr("crc", sym.cfg_table_idx_width)))), sym.cfg_mask ).simplify() out += [ Conditional2( opt, "", opt.width is None or opt.width > 8, [ f"tbl_idx = (crc ^ *d) & {sym.crc_table_mask};", ], [ "tbl_idx = crc ^ *d;", ], ), f"crc = {crc_exp};", ] else: crc_lookup = f"crc_table[tbl_idx & {sym.crc_table_mask}]" for i in range(8 // opt.table_idx_width): idx = expr.Xor( "crc", expr.Parenthesis(expr.Shr("*d", expr.Parenthesis(expr.Mul(i, sym.cfg_table_idx_width)))) ).simplify() out += [f"tbl_idx = {idx};", f"crc = {expr.Xor(crc_lookup, crc_xor_expr).simplify()};"] return CodeGen(opt, "", out) def _crc_table_core_algorithm_nonreflected(opt: Options, sym: SymbolTable) -> CodeGen: """ Return the core loop of the table-driven algorithm, non-reflected variant. """ out: list[CodeGen | str] = [] if opt.width is None: crc_shifted_right = expr.Parenthesis( expr.Shr("crc", expr.Parenthesis(expr.Sub(sym.cfg_width, sym.cfg_table_idx_width))) ).simplify() elif opt.width < 8: shift_val = opt.width - opt.table_idx_width if shift_val < 0: crc_shifted_right = expr.Parenthesis(expr.Shl("crc", -shift_val)).simplify() else: crc_shifted_right = expr.Parenthesis(expr.Shr("crc", shift_val)).simplify() else: shift_val = opt.width - opt.table_idx_width crc_shifted_right = expr.Parenthesis(expr.Shr("crc", shift_val)).simplify() if opt.width is not None and opt.table_idx_width is not None and opt.width <= opt.table_idx_width: crc_xor_expr = "0" else: crc_xor_expr = f"(crc << {sym.cfg_table_idx_width})" if opt.table_idx_width == 8: crc_lookup = "crc_table[0][tbl_idx]" if opt.slice_by > 1 else "crc_table[tbl_idx]" out += [ Conditional2( opt, "", opt.width is None or opt.width > 8, [ "tbl_idx = {0};".format( expr.And(expr.Parenthesis(expr.Xor(crc_shifted_right, "*d")), sym.crc_table_mask).simplify() ) ], ["tbl_idx = {0};".format(expr.Xor(crc_shifted_right, "*d").simplify())], ), f"crc = {expr.And(expr.Parenthesis(expr.Xor(crc_lookup, crc_xor_expr)), sym.cfg_mask).simplify()};", ] else: crc_lookup = f"crc_table[tbl_idx & {sym.crc_table_mask}]" for i in range(8 // opt.table_idx_width): str_idx = f"{8 - (i + 1) * opt.table_idx_width:d}" out += [ "tbl_idx = {0};".format(expr.Xor(crc_shifted_right, expr.Parenthesis(expr.Shr("*d", str_idx)))), f"crc = {expr.Xor(crc_lookup, crc_xor_expr).simplify()};", ] return CodeGen(opt, "", out) def _crc_table_slice_by_algorithm(opt: Options, sym: SymbolTable) -> CodeGen: """ Return the body of the slice-by loop of the table-driven update function. The loop consumes opt.slice_by bytes at a time. It is emitted once for big-endian and once for little-endian hosts. """ update_be: list[str] = [] for i in range(opt.slice_by // 4): vard = f"d{opt.slice_by // 4 - i}" for j in range(4): idx1 = i * 4 + j idx2 = expr.And(expr.Parenthesis(expr.Shr(vard, j * 8)), expr.Terminal(255, "0xffu")).simplify() update_be.append("crc_table[{0}][{1}]{2}".format(idx1, idx2, " ^" if idx1 < opt.slice_by - 1 else ";")) update_le: list[str] = [] for i in range(opt.slice_by // 4): vard = f"d{opt.slice_by // 4 - i}" for j in range(4): idx1 = i * 4 + j idx2 = expr.And(expr.Parenthesis(expr.Shr(vard, 24 - j * 8)), expr.Terminal(255, "0xffu")).simplify() update_le.append("crc_table[{0}][{1}]{2}".format(idx1, idx2, " ^" if idx1 < opt.slice_by - 1 else ";")) out: list[CodeGen | str] = [ "const uint32_t *d32 = (const uint32_t *)d;", f"while (data_len >= {sym.crc_slice_by})", "{", CodeGen( opt, 4 * " ", [ CodeGen( opt, None, [ "#if __BYTE_ORDER == __BIG_ENDIAN", ], ), f"{sym.crc_t} d1 = *d32++ ^ le32toh(crc);", Conditional( opt, "", opt.slice_by >= 8, [ f"{sym.crc_t} d2 = *d32++;", ], ), Conditional( opt, "", opt.slice_by >= 16, [ f"{sym.crc_t} d3 = *d32++;", f"{sym.crc_t} d4 = *d32++;", ], ), "crc =", CodeGen(opt, 4 * " ", update_be), CodeGen( opt, None, [ "#else", ], ), f"{sym.crc_t} d1 = *d32++ ^ crc;", Conditional( opt, "", opt.slice_by >= 8, [ f"{sym.crc_t} d2 = *d32++;", ], ), Conditional( opt, "", opt.slice_by >= 16, [ f"{sym.crc_t} d3 = *d32++;", f"{sym.crc_t} d4 = *d32++;", ], ), "crc =", CodeGen(opt, 4 * " ", update_le), CodeGen( opt, None, [ "#endif", ], ), "", f"data_len -= {sym.crc_slice_by};", ], ), "}", "", ] return CodeGen(opt, "", out) pycrc-0.12.0/src/pycrc/expr.py0000644000000000000000000002742713615410400013105 0ustar00# pycrc -- parameterisable CRC calculation utility and C source code generator # # Copyright (c) 2017-2026 Thomas Pircher # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. """ This modules simplifies an expression. import pycrc.expr as exp my_expr = exp.Xor('var', exp.Parenthesis(exp.And('0x700', 4))) print('"{}" -> "{}"'.format(my_expr, my_expr.simplify())) """ from __future__ import annotations from collections.abc import Sequence from typing import Any def _classify(val: int | str | Expression) -> Expression: """ Check if the parameter is a string or a integer value, and return a Terminal object in this case. Otherwise return the parameter itself. """ if isinstance(val, int): return Terminal(val) if isinstance(val, str): if val.isdigit(): return Terminal(int(val), val) if val[:2].lower() == "0x": return Terminal(int(val, 16), val) return Terminal(val) return val class Expression: """ Base class for all expressions. """ val: Any def is_int(self, val: int | None = None) -> bool: """Dummy function, always returns False. This is overwritten by derived classes.""" return False def simplify(self) -> Expression: """ Return a simplified version of this sub-expression. """ return self class Terminal(Expression): """ A terminal object. """ def __init__(self, val: int | str, pretty: str | None = None) -> None: """ Construct a Terminal. The val variable is usually a string or an integer. Integers may also be passed as strings. The pretty-printer will use the string when formatting the expression. """ self.val: int | str = val self.pretty: str | None = pretty def __str__(self) -> str: """ Return the string representation of this object. """ if self.pretty is not None: return self.pretty return str(self.val) def simplify(self) -> Terminal: """ Return a simplified version of this sub-expression. """ return self def is_int(self, val: int | None = None) -> bool: """ Return True if the value of this Terminal is an integer. """ if isinstance(self.val, int): return val is None or self.val == val return False class FunctionCall(Expression): """ Represent a function call """ def __init__(self, name: int | str | Expression, args: Sequence[int | str | Expression]) -> None: """ Construct a function call object. """ self.name: Expression = _classify(name) self.args: list[Expression] = [_classify(arg) for arg in args] def __str__(self) -> str: """ Return the string expression of this object. """ return str(self.name) + "(" + ", ".join([str(arg) for arg in self.args]) + ")" def simplify(self) -> FunctionCall: """ Return a simplified version of this sub-expression. """ args = [arg.simplify() for arg in self.args] return FunctionCall(self.name, args) class Parenthesis(Expression): """ Represent a pair of round brackets. """ def __init__(self, val: int | str | Expression) -> None: """ Construct a parenthesis object. """ self.val: Expression = _classify(val) def simplify(self) -> Expression: """ Return a simplified version of this sub-expression. """ val = self.val.simplify() if isinstance(val, Terminal): return val return Parenthesis(val) def __str__(self) -> str: """ Return the string expression of this object. """ return "(" + str(self.val) + ")" class Add(Expression): """ Represent an addition of operands. """ def __init__(self, lhs: int | str | Expression, rhs: int | str | Expression) -> None: """ Construct an addition object. """ self.lhs: Expression = _classify(lhs) self.rhs: Expression = _classify(rhs) def simplify(self) -> Expression: """ Return a simplified version of this sub-expression. """ lhs = self.lhs.simplify() rhs = self.rhs.simplify() if lhs.is_int() and rhs.is_int(): return Terminal(lhs.val + rhs.val) if lhs.is_int(0): return rhs if rhs.is_int(0): return lhs return Add(lhs, rhs) def __str__(self) -> str: """ Return the string expression of this object. """ return str(self.lhs) + " + " + str(self.rhs) class Sub(Expression): """ Represent a subtraction of operands. """ def __init__(self, lhs: int | str | Expression, rhs: int | str | Expression) -> None: """ Construct subtraction object. """ self.lhs: Expression = _classify(lhs) self.rhs: Expression = _classify(rhs) def simplify(self) -> Expression: """ Return a simplified version of this sub-expression. """ lhs = self.lhs.simplify() rhs = self.rhs.simplify() if lhs.is_int() and rhs.is_int(): return Terminal(lhs.val - rhs.val) if rhs.is_int(0): return lhs return Sub(lhs, rhs) def __str__(self) -> str: """ Return the string expression of this object. """ return str(self.lhs) + " - " + str(self.rhs) class Mul(Expression): """ Represent the multiplication of operands. """ def __init__(self, lhs: int | str | Expression, rhs: int | str | Expression) -> None: """ Construct a multiplication object. """ self.lhs: Expression = _classify(lhs) self.rhs: Expression = _classify(rhs) def simplify(self) -> Expression: """ Return a simplified version of this sub-expression. """ lhs = self.lhs.simplify() rhs = self.rhs.simplify() if lhs.is_int() and rhs.is_int(): return Terminal(lhs.val * rhs.val) if lhs.is_int(0) or rhs.is_int(0): return Terminal(0) if lhs.is_int(1): return rhs if rhs.is_int(1): return lhs return Mul(lhs, rhs) def __str__(self) -> str: """ Return the string expression of this object. """ return str(self.lhs) + " * " + str(self.rhs) class Shl(Expression): """ Shift left operation. """ def __init__(self, lhs: int | str | Expression, rhs: int | str | Expression) -> None: """ Construct a shift left object. """ self.lhs: Expression = _classify(lhs) self.rhs: Expression = _classify(rhs) def simplify(self) -> Expression: """ Return a simplified version of this sub-expression. """ lhs = self.lhs.simplify() rhs = self.rhs.simplify() if lhs.is_int() and rhs.is_int(): return Terminal(lhs.val << rhs.val) if lhs.is_int(0): return Terminal(0) if rhs.is_int(0): return lhs return Shl(lhs, rhs) def __str__(self) -> str: """ Return the string expression of this object. """ return str(self.lhs) + " << " + str(self.rhs) class Shr(Expression): """ Shift right operation. """ def __init__(self, lhs: int | str | Expression, rhs: int | str | Expression) -> None: """ Construct a shift right object. """ self.lhs: Expression = _classify(lhs) self.rhs: Expression = _classify(rhs) def simplify(self) -> Expression: """ Return a simplified version of this sub-expression. """ lhs = self.lhs.simplify() rhs = self.rhs.simplify() if lhs.is_int() and rhs.is_int(): return Terminal(lhs.val >> rhs.val) if lhs.is_int(0): return Terminal(0) if rhs.is_int(0): return lhs return Shr(lhs, rhs) def __str__(self) -> str: """ Return the string expression of this object. """ return str(self.lhs) + " >> " + str(self.rhs) class Or(Expression): """ Logical or operation. """ def __init__(self, lhs: int | str | Expression, rhs: int | str | Expression) -> None: """ Construct a logical and object. """ self.lhs: Expression = _classify(lhs) self.rhs: Expression = _classify(rhs) def simplify(self) -> Expression: """ Return a simplified version of this sub-expression. """ lhs = self.lhs.simplify() rhs = self.rhs.simplify() if lhs.is_int() and rhs.is_int(): return Terminal(lhs.val | rhs.val) if lhs.is_int(0): return rhs if rhs.is_int(0): return lhs return Or(lhs, rhs) def __str__(self) -> str: """ Return the string expression of this object. """ return str(self.lhs) + " | " + str(self.rhs) class And(Expression): """ Logical and operation. """ def __init__(self, lhs: int | str | Expression, rhs: int | str | Expression) -> None: """ Construct a logical and object. """ self.lhs: Expression = _classify(lhs) self.rhs: Expression = _classify(rhs) def simplify(self) -> Expression: """ Return a simplified version of this sub-expression. """ lhs = self.lhs.simplify() rhs = self.rhs.simplify() if lhs.is_int() and rhs.is_int(): return Terminal(lhs.val & rhs.val) if lhs.is_int(0) or rhs.is_int(0): return Terminal(0) return And(lhs, rhs) def __str__(self) -> str: """ Return the string expression of this object. """ return str(self.lhs) + " & " + str(self.rhs) class Xor(Expression): """ Logical xor operation. """ def __init__(self, lhs: int | str | Expression, rhs: int | str | Expression) -> None: """ Construct a logical xor object. """ self.lhs: Expression = _classify(lhs) self.rhs: Expression = _classify(rhs) def simplify(self) -> Expression: """ Return a simplified version of this sub-expression. """ lhs = self.lhs.simplify() rhs = self.rhs.simplify() if lhs.is_int() and rhs.is_int(): return Terminal(lhs.val ^ rhs.val) if lhs.is_int(0): return rhs if rhs.is_int(0): return lhs return Xor(lhs, rhs) def __str__(self) -> str: """ Return the string expression of this object. """ return str(self.lhs) + " ^ " + str(self.rhs) pycrc-0.12.0/src/pycrc/main.py0000644000000000000000000001535513615410400013050 0ustar00#!/usr/bin/env python # pycrc -- parameterisable CRC calculation utility and C source code generator # # Copyright (c) 2006-2026 Thomas Pircher # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. """ pycrc is a fully parameterisable Cyclic Redundancy Check (CRC) calculation utility and C source code generator written in Python. It can: - generate the checksum of a string - generate the checksum of a file - generate the C header file and source of any of the algorithms below It supports the following CRC algorithms: - bit-by-bit the basic algorithm which operates bit by bit on the augmented message - bit-by-bit-fast a variation of the simple bit-by-bit algorithm - table-driven the standard table driven algorithm """ from __future__ import annotations import binascii import sys import pycrc.codegen as cg from pycrc import __version__ from pycrc.algorithms import Crc from pycrc.opt import Options progname = "pycrc" url = "https://pycrc.org" def print_parameters(opt: Options) -> str: """ Generate a string with the options pretty-printed (used in the --verbose mode). """ return str(cg.ParamBlock(opt, "")) def _get_crc(opt: Options) -> Crc: """ Return a Crc object built from the fully defined options. """ assert opt.width is not None assert opt.poly is not None assert opt.reflect_in is not None assert opt.xor_in is not None assert opt.reflect_out is not None assert opt.xor_out is not None return Crc( width=opt.width, poly=opt.poly, reflect_in=opt.reflect_in, xor_in=opt.xor_in, reflect_out=opt.reflect_out, xor_out=opt.xor_out, table_idx_width=opt.table_idx_width, ) def check_string(opt: Options) -> int | None: """ Return the calculated CRC sum of a string. """ error = False if opt.undefined_crc_parameters: sys.stderr.write(f"{progname:s}: error: undefined parameters\n") sys.exit(1) if opt.algorithm == 0: opt.algorithm = opt.algo_bit_by_bit | opt.algo_bit_by_bit_fast | opt.algo_table_driven alg = _get_crc(opt) crc: int | None = None if opt.algorithm & opt.algo_bit_by_bit: bbb_crc = alg.bit_by_bit(opt.check_string) error |= crc is not None and bbb_crc != crc crc = bbb_crc if opt.algorithm & opt.algo_bit_by_bit_fast: bbf_crc = alg.bit_by_bit_fast(opt.check_string) error |= crc is not None and bbf_crc != crc crc = bbf_crc if opt.algorithm & opt.algo_table_driven: tbl_crc = alg.table_driven(opt.check_string) error |= crc is not None and tbl_crc != crc crc = tbl_crc if error: sys.stderr.write(f"{progname:s}: error: different checksums!\n") if opt.algorithm & opt.algo_bit_by_bit: sys.stderr.write(f" bit-by-bit: {bbb_crc:#x}\n") if opt.algorithm & opt.algo_bit_by_bit_fast: sys.stderr.write(f" bit-by-bit-fast: {bbf_crc:#x}\n") if opt.algorithm & opt.algo_table_driven: sys.stderr.write(f" table_driven: {tbl_crc:#x}\n") sys.exit(1) return crc def check_hexstring(opt: Options) -> int | None: """ Return the calculated CRC sum of a hex string. """ if opt.undefined_crc_parameters: sys.stderr.write(f"{progname:s}: error: undefined parameters\n") sys.exit(1) hex_string = opt.check_string assert isinstance(hex_string, str) check_input = hex_string if len(hex_string) % 2 != 0: hex_string = "0" + hex_string try: check_str = bytearray(binascii.unhexlify(bytes(hex_string, "utf_8"))) except (binascii.Error, ValueError): sys.stderr.write(f"{progname:s}: error: invalid hex string {check_input:s}\n") sys.exit(1) opt.check_string = check_str return check_string(opt) def check_file(opt: Options) -> int: """ Calculate the CRC of a file. This algorithm uses the bit-by-bit-fast CRC algorithm. """ if opt.undefined_crc_parameters: sys.stderr.write(f"{progname:s}: error: undefined parameters\n") sys.exit(1) assert opt.check_file is not None alg = _get_crc(opt) try: with open(opt.check_file, "rb") as f: check_bytes = bytearray(f.read(4096)) while check_bytes != b"": alg.update(check_bytes) check_bytes = bytearray(f.read(4096)) except OSError: sys.stderr.write(f"{progname:s}: error: can't open file {opt.check_file:s}\n") sys.exit(1) return alg.digest() def write_file(filename: str, out_str: str) -> None: """ Write the content of out_str to filename. """ try: with open(filename, "w") as out_file: out_file.write(out_str) except OSError: sys.stderr.write(f"{progname:s}: error: cannot write to file {filename:s}\n") sys.exit(1) def main() -> int: """ Main function. """ opt = Options(progname, __version__, url) opt.parse(sys.argv[1:]) if opt.verbose: print(print_parameters(opt)) if opt.action == opt.action_check_str: crc = check_string(opt) print(f"{crc:#x}") if opt.action == opt.action_check_hex_str: crc = check_hexstring(opt) print(f"{crc:#x}") if opt.action == opt.action_check_file: crc = check_file(opt) print(f"{crc:#x}") if opt.action in {opt.action_generate_h, opt.action_generate_c, opt.action_generate_c_main, opt.action_generate_table}: out = str(cg.File(opt, "")) if opt.output_file is None: print(out) else: write_file(opt.output_file, out) return 0 if __name__ == "__main__": sys.exit(main()) pycrc-0.12.0/src/pycrc/models.py0000644000000000000000000003077213615410400013407 0ustar00# pycrc -- parameterisable CRC calculation utility and C source code generator # # Copyright (c) 2006-2026 Thomas Pircher # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. """ Collection of CRC models. This module contains the CRC models known to pycrc. To print the parameters of a particular model: import pycrc.models as cm models = cm.CrcModels() print(", ".join(models.names())) m = models.get_params("crc-32") if m is not None: print("Width: {width:d}".format(**m)) print("Poly: {poly:#x}".format(**m)) print("ReflectIn: {reflect_in}".format(**m)) print("XorIn: {xor_in:#x}".format(**m)) print("ReflectOut: {reflect_out}".format(**m)) print("XorOut: {xor_out:#x}".format(**m)) print("Check: {check:#x}".format(**m)) else: print("model not found.") """ from __future__ import annotations from collections.abc import Iterator, Mapping from dataclasses import dataclass, fields from types import MappingProxyType from typing import Any @dataclass(frozen=True) class CrcParams: """ The parameters of a CRC model (the Rocksoft model). Instances are immutable and may be used wherever a model's parameters are needed. For convenience they also support dictionary-style access, e.g. ``params["width"]`` and ``dict(params)``. """ name: str width: int poly: int reflect_in: bool xor_in: int reflect_out: bool xor_out: int check: int def keys(self) -> tuple[str, ...]: """Return the names of the model parameters.""" return tuple(field.name for field in fields(self)) def __contains__(self, key: object) -> bool: """Return True if the named model parameter exists.""" return isinstance(key, str) and key in self.keys() def __getitem__(self, key: str) -> Any: """Return the named model parameter, or raise KeyError.""" if key not in self.keys(): raise KeyError(key) return getattr(self, key) class CrcModels(Mapping[str, CrcParams]): """ CRC Models. All models are defined as constant class variables. The class behaves like a read-only mapping from model name to :class:`CrcParams`, so models can be looked up by name in constant time: models = CrcModels() params = models["crc-32"] if "crc-32" in models: ... """ _models: list[dict[str, Any]] = [] _models.append( { "name": "crc-5", "width": 5, "poly": 0x05, "reflect_in": True, "xor_in": 0x1F, "reflect_out": True, "xor_out": 0x1F, "check": 0x19, } ) _models.append( { "name": "crc-8", "width": 8, "poly": 0x07, "reflect_in": False, "xor_in": 0x0, "reflect_out": False, "xor_out": 0x0, "check": 0xF4, } ) _models.append( { "name": "dallas-1-wire", "width": 8, "poly": 0x31, "reflect_in": True, "xor_in": 0x0, "reflect_out": True, "xor_out": 0x0, "check": 0xA1, } ) _models.append( { "name": "crc-12-3gpp", "width": 12, "poly": 0x80F, "reflect_in": False, "xor_in": 0x0, "reflect_out": True, "xor_out": 0x0, "check": 0xDAF, } ) _models.append( { "name": "crc-15", "width": 15, "poly": 0x4599, "reflect_in": False, "xor_in": 0x0, "reflect_out": False, "xor_out": 0x0, "check": 0x59E, } ) _models.append( { "name": "crc-16", "width": 16, "poly": 0x8005, "reflect_in": True, "xor_in": 0x0, "reflect_out": True, "xor_out": 0x0, "check": 0xBB3D, } ) _models.append( { "name": "crc-16-usb", "width": 16, "poly": 0x8005, "reflect_in": True, "xor_in": 0xFFFF, "reflect_out": True, "xor_out": 0xFFFF, "check": 0xB4C8, } ) _models.append( { "name": "crc-16-modbus", "width": 16, "poly": 0x8005, "reflect_in": True, "xor_in": 0xFFFF, "reflect_out": True, "xor_out": 0x0, "check": 0x4B37, } ) _models.append( { "name": "crc-16-genibus", "width": 16, "poly": 0x1021, "reflect_in": False, "xor_in": 0xFFFF, "reflect_out": False, "xor_out": 0xFFFF, "check": 0xD64E, } ) _models.append( { "name": "crc-16-ccitt", "width": 16, "poly": 0x1021, "reflect_in": False, "xor_in": 0x1D0F, "reflect_out": False, "xor_out": 0x0, "check": 0xE5CC, } ) _models.append( { "name": "r-crc-16", "width": 16, "poly": 0x0589, "reflect_in": False, "xor_in": 0x0, "reflect_out": False, "xor_out": 0x0001, "check": 0x007E, } ) _models.append( { "name": "kermit", "width": 16, "poly": 0x1021, "reflect_in": True, "xor_in": 0x0, "reflect_out": True, "xor_out": 0x0, "check": 0x2189, } ) _models.append( { "name": "x-25", "width": 16, "poly": 0x1021, "reflect_in": True, "xor_in": 0xFFFF, "reflect_out": True, "xor_out": 0xFFFF, "check": 0x906E, } ) _models.append( { "name": "xmodem", "width": 16, "poly": 0x1021, "reflect_in": False, "xor_in": 0x0, "reflect_out": False, "xor_out": 0x0, "check": 0x31C3, } ) _models.append( { "name": "zmodem", "width": 16, "poly": 0x1021, "reflect_in": False, "xor_in": 0x0, "reflect_out": False, "xor_out": 0x0, "check": 0x31C3, } ) _models.append( { "name": "crc-24", "width": 24, "poly": 0x864CFB, "reflect_in": False, "xor_in": 0xB704CE, "reflect_out": False, "xor_out": 0x0, "check": 0x21CF02, } ) _models.append( { "name": "crc-32", "width": 32, "poly": 0x4C11DB7, "reflect_in": True, "xor_in": 0xFFFFFFFF, "reflect_out": True, "xor_out": 0xFFFFFFFF, "check": 0xCBF43926, } ) _models.append( { "name": "crc-32c", "width": 32, "poly": 0x1EDC6F41, "reflect_in": True, "xor_in": 0xFFFFFFFF, "reflect_out": True, "xor_out": 0xFFFFFFFF, "check": 0xE3069283, } ) _models.append( { "name": "crc-32-mpeg", "width": 32, "poly": 0x4C11DB7, "reflect_in": False, "xor_in": 0xFFFFFFFF, "reflect_out": False, "xor_out": 0x0, "check": 0x0376E6E7, } ) _models.append( { "name": "crc-32-bzip2", "width": 32, "poly": 0x04C11DB7, "reflect_in": False, "xor_in": 0xFFFFFFFF, "reflect_out": False, "xor_out": 0xFFFFFFFF, "check": 0xFC891918, } ) _models.append( { "name": "posix", "width": 32, "poly": 0x4C11DB7, "reflect_in": False, "xor_in": 0x0, "reflect_out": False, "xor_out": 0xFFFFFFFF, "check": 0x765E7680, } ) _models.append( { "name": "jam", "width": 32, "poly": 0x4C11DB7, "reflect_in": True, "xor_in": 0xFFFFFFFF, "reflect_out": True, "xor_out": 0x0, "check": 0x340BC6D9, } ) _models.append( { "name": "xfer", "width": 32, "poly": 0x000000AF, "reflect_in": False, "xor_in": 0x0, "reflect_out": False, "xor_out": 0x0, "check": 0xBD0BE338, } ) _models.append( { "name": "crc-64", "width": 64, "poly": 0x000000000000001B, "reflect_in": True, "xor_in": 0x0, "reflect_out": True, "xor_out": 0x0, "check": 0x46A5A9388A5BEFFE, } ) _models.append( { "name": "crc-64-jones", "width": 64, "poly": 0xAD93D23594C935A9, "reflect_in": True, "xor_in": 0xFFFFFFFFFFFFFFFF, "reflect_out": True, "xor_out": 0x0, "check": 0xCAA717168609F281, } ) _models.append( { "name": "crc-64-xz", "width": 64, "poly": 0x42F0E1EBA9EA3693, "reflect_in": True, "xor_in": 0xFFFFFFFFFFFFFFFF, "reflect_out": True, "xor_out": 0xFFFFFFFFFFFFFFFF, "check": 0x995DC9BBDF1939FA, } ) # Make the collection immutable so that callers cannot accidentally add # or remove model definitions. The lookup table is built once, so name # lookups are O(1). models: tuple[CrcParams, ...] = tuple(CrcParams(**definition) for definition in _models) _by_name: Mapping[str, CrcParams] = MappingProxyType({model.name: model for model in models}) def __getitem__(self, name: str) -> CrcParams: """Return the model with the given (case-insensitive) name.""" return self._by_name[name.lower()] def __iter__(self) -> Iterator[str]: """Iterate over the names of the supported models.""" return iter(self._by_name) def __len__(self) -> int: """Return the number of supported models.""" return len(self._by_name) def names(self) -> list[str]: """Return the list of supported CRC models.""" return list(self._by_name) def get_params(self, model: str) -> dict[str, Any] | None: """ Return a copy of the parameters of a given model, or None if it is unknown. This is the legacy interface: it returns a plain, mutable ``dict`` so that existing callers can keep modifying the result without affecting the (immutable) model definitions. New code should prefer the read-only mapping interface, i.e. ``models[model]`` or ``models.get(model)``, which return an immutable :class:`CrcParams`. The model name is matched case-insensitively. """ params = self._by_name.get(model.lower()) if params is None: return None return {field.name: getattr(params, field.name) for field in fields(params)} pycrc-0.12.0/src/pycrc/opt.py0000644000000000000000000005133313615410400012722 0ustar00# pycrc -- parameterisable CRC calculation utility and C source code generator # # Copyright (c) 2006-2026 Thomas Pircher # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. """ Option parsing library for pycrc. use as follows: from pycrc.opt import Options opt = Options() opt.parse(sys.argv[1:]) """ from __future__ import annotations import argparse import sys from collections.abc import Sequence from typing import Any, NoReturn from pycrc.models import CrcModels class Options: """ The options parsing and validating class. """ # Bitmap of the algorithms algo_none = 0x00 algo_bit_by_bit = 0x01 algo_bit_by_bit_fast = 0x02 algo_table_driven = 0x08 action_check_str = 0x01 action_check_hex_str = 0x02 action_check_file = 0x03 action_generate_h = 0x04 action_generate_c = 0x05 action_generate_c_main = 0x06 action_generate_table = 0x07 def __init__(self, progname: str = "pycrc", version: str = "unknown", url: str = "unknown") -> None: """ Create an option container. The CRC parameters are initially undefined; call parse() to fill them in from the command line. """ self.program_name: str = progname self.version: str = version self.version_str: str = f"{progname} v{version}" self.web_address: str = url self.width: int | None = None self.poly: int | None = None self.reflect_in: bool | None = None self.xor_in: int | None = None self.reflect_out: bool | None = None self.xor_out: int | None = None self.table_idx_width: int = 8 self.tbl_width: int = 1 << self.table_idx_width self.slice_by: int = 1 self.verbose: bool = False self.check_string: str | bytes | bytearray = "123456789" self.msb_mask: int | None = None self.mask: int | None = None self.algorithm: int = self.algo_none self.symbol_prefix: str = "crc_" self.crc_type: str | None = None self.include_files: list[str] = [] self.output_file: str | None = None self.action: int = self.action_check_str self.check_file: str | None = None self.c_std: str | None = None self.undefined_crc_parameters: bool = False @property def tbl_idx_width(self) -> int: """Deprecated alias for :attr:`table_idx_width`.""" return self.table_idx_width @tbl_idx_width.setter def tbl_idx_width(self, value: int) -> None: """Deprecated alias for :attr:`table_idx_width`.""" self.table_idx_width = value self.tbl_width = 1 << value def parse(self, argv: list[str] | None = None) -> None: """ Parses and validates the options given as arguments """ usage = """python %(prog)s [OPTIONS] To calculate the checksum of a string or hexadecimal data: python %(prog)s [model] --check-string "123456789" python %(prog)s [model] --check-hexstring "313233343536373839" To calculate the checksum of a file: python %(prog)s [model] --check-file filename To generate the C source code and write it to filename: python %(prog)s [model] --generate c -o filename The model can be defined either with the --model switch or by specifying each of the following parameters: --width --poly --reflect-in --xor-in --reflect-out --xor-out""" models = CrcModels() model_list = ", ".join(models.names()) parser = argparse.ArgumentParser(prog=self.program_name, usage=usage) parser.add_argument( "-v", "--verbose", action="store_true", default=False, help="be more verbose; print the value of the parameters and the chosen model to stdout", ) parser.add_argument( "--check-string", dest="check_string", help="calculate the checksum of a string (default: '123456789')", metavar="STRING", ) parser.add_argument( "--check-hexstring", dest="check_hexstring", help="calculate the checksum of a hexadecimal number string", metavar="STRING", ) parser.add_argument("--check-file", dest="check_file", help="calculate the checksum of a file", metavar="FILE") parser.add_argument( "--generate", dest="generate", default=None, help="generate C source code; choose the type from {h, c, c-main, table}", metavar="CODE", ) parser.add_argument( "--std", dest="c_std", default="C99", help="choose the C dialect of the generated code from {C89, ANSI, C99}", metavar="STD", ) parser.add_argument( "--algorithm", dest="algorithm", default="all", help="choose an algorithm from {bit-by-bit, bbb, bit-by-bit-fast, bbf, table-driven, tbl, all}", metavar="ALGO", ) parser.add_argument( "--model", action=_ModelAction, dest="model", default=None, help=f"choose a parameter set from {{{model_list}}}", metavar="MODEL", ) parser.add_argument("--width", type=_hex_type, dest="width", help="use NUM bits in the polynomial", metavar="NUM") parser.add_argument("--poly", type=_hex_type, dest="poly", help="use HEX as polynomial", metavar="HEX") parser.add_argument( "--reflect-in", type=_bool_type, dest="reflect_in", help="reflect the octets in the input message", metavar="BOOL" ) parser.add_argument("--xor-in", type=_hex_type, dest="xor_in", help="use HEX as initial value", metavar="HEX") parser.add_argument( "--reflect-out", type=_bool_type, dest="reflect_out", help="reflect the resulting checksum before applying the --xor-out value", metavar="BOOL", ) parser.add_argument( "--xor-out", type=_hex_type, dest="xor_out", help="xor the final CRC value with HEX", metavar="HEX" ) parser.add_argument( "--slice-by", type=int, dest="slice_by", help="read NUM bytes at a time from the input. NUM must be one of the values {4, 8, 16}", metavar="NUM", ) parser.add_argument( "--table-idx-width", type=int, dest="table_idx_width", help="use NUM bits to index the CRC table; NUM must be one of the values {1, 2, 4, 8}", metavar="NUM", ) parser.add_argument( "--force-poly", action="store_true", default=False, help="override any errors about possibly unsuitable polynoms" ) parser.add_argument( "--symbol-prefix", dest="symbol_prefix", help="when generating source code, use STRING as prefix to the exported C symbols", metavar="STRING", ) parser.add_argument( "--crc-type", dest="crc_type", help="when generating source code, use STRING as crc_t type", metavar="STRING" ) parser.add_argument( "--include-file", action="append", dest="include_files", help="when generating source code, include also FILE as header file; can be specified multiple times", metavar="FILE", ) parser.add_argument( "-o", "--output", dest="output_file", help="write the generated code to file instead to stdout", metavar="FILE" ) parser.add_argument("--version", action="version", version=self.version_str) options, args = parser.parse_known_args(argv) self._parse_c_std(options) undefined_params = self._parse_model_params(options) self._parse_table_idx_width(options) self._validate_bits(options) self._parse_slice_by(options) self._parse_algorithm(options) self._parse_output_options(options) self._resolve_action(options) self._validate_action(options, args, undefined_params) def _parse_c_std(self, options: argparse.Namespace) -> None: """ Validate and store the requested C standard. """ if options.c_std is not None: std = options.c_std.upper() if std == "ANSI" or std == "C89": self.c_std = "C89" elif std == "C99": self.c_std = std else: self.__error(f"unknown C standard {options.c_std}") def _parse_model_params(self, options: argparse.Namespace) -> list[str]: """ Store the individual CRC model parameters and return the list of the parameters that were not supplied. """ undefined_params = [] if options.width is not None: self.width = options.width else: undefined_params.append("--width") if options.poly is not None: self.poly = options.poly else: undefined_params.append("--poly") if options.reflect_in is not None: self.reflect_in = options.reflect_in else: undefined_params.append("--reflect-in") if options.xor_in is not None: self.xor_in = options.xor_in else: undefined_params.append("--xor-in") if options.reflect_out is not None: self.reflect_out = options.reflect_out else: undefined_params.append("--reflect-out") if options.xor_out is not None: self.xor_out = options.xor_out else: undefined_params.append("--xor-out") return undefined_params def _parse_table_idx_width(self, options: argparse.Namespace) -> None: """ Validate and store the table index width. """ if options.table_idx_width is not None: if options.table_idx_width in {1, 2, 4, 8}: self.table_idx_width = options.table_idx_width self.tbl_width = 1 << options.table_idx_width else: self.__error(f"unsupported table-idx-width {options.table_idx_width}") def _validate_bits(self, options: argparse.Namespace) -> None: """ Validate the width and polynomial and derive the bit masks. """ if self.poly is not None and self.poly % 2 == 0 and not options.force_poly: self.__error("even polinomials are not allowed by default. Use --force-poly to override this.") if self.width is not None: if self.width <= 0: self.__error("Width must be strictly positive") self.msb_mask = 0x1 << (self.width - 1) self.mask = ((self.msb_mask - 1) << 1) | 1 if self.poly is not None and self.poly >> (self.width + 1) != 0 and not options.force_poly: self.__error("the polynomial is wider than the supplied Width. Use --force-poly to override this.") if self.poly is not None: self.poly = self.poly & self.mask if self.xor_in is not None: self.xor_in = self.xor_in & self.mask if self.xor_out is not None: self.xor_out = self.xor_out & self.mask else: self.msb_mask = None self.mask = None self.undefined_crc_parameters = not ( self.width is not None and self.poly is not None and self.reflect_in is not None and self.xor_in is not None and self.reflect_out is not None and self.xor_out is not None ) def _parse_slice_by(self, options: argparse.Namespace) -> None: """ Validate and store the --slice-by value. """ if options.slice_by is not None: if options.slice_by in {4, 8, 16}: self.slice_by = options.slice_by else: self.__error(f"unsupported slice-by {options.slice_by}") if self.undefined_crc_parameters: self.__error("slice-by is only implemented for fully defined models") if self.table_idx_width != 8: self.__error("slice-by is only implemented for table-idx-width=8") # FIXME tp: Fix corner cases and disable the following tests assert self.width is not None if self.width < 16 or self.width > 32: self.__warning(f"disabling slice-by for width {self.width}") self.slice_by = 1 if not self.reflect_in: self.__warning("disabling slice-by for non-reflected algorithm") self.slice_by = 1 # FIXME tp: reintroduce this? # if self.width % 8 != 0: # self.__error("slice-by is only implemented for width multiples of 8") # if options.slice_by < self.width / 8: # self.__error("slice-by must be greater or equal width / 8") if self.c_std == "C89": self.__error("--slice-by not supported for C89") def _parse_algorithm(self, options: argparse.Namespace) -> None: """ Validate and store the selected algorithm(s). """ if options.algorithm is not None: alg = options.algorithm.lower() if alg in {"bit-by-bit", "bbb", "all"}: self.algorithm |= self.algo_bit_by_bit if alg in {"bit-by-bit-fast", "bbf", "all"}: self.algorithm |= self.algo_bit_by_bit_fast if alg in {"table-driven", "tbl", "all"}: self.algorithm |= self.algo_table_driven if self.algorithm == 0: self.__error(f"unknown algorithm {options.algorithm}") def _parse_output_options(self, options: argparse.Namespace) -> None: """ Store the options that affect the generated source code. """ if options.symbol_prefix is not None: self.symbol_prefix = options.symbol_prefix if options.include_files is not None: self.include_files = options.include_files if options.crc_type is not None: self.crc_type = options.crc_type if options.output_file is not None: self.output_file = options.output_file def _resolve_action(self, options: argparse.Namespace) -> int: """ Determine which action was requested and make sure exactly one was given. Return the number of actions. """ op_count = 0 if options.check_string is not None: self.action = self.action_check_str self.check_string = options.check_string op_count += 1 if options.check_hexstring is not None: self.action = self.action_check_hex_str self.check_string = options.check_hexstring op_count += 1 if options.check_file is not None: self.action = self.action_check_file self.check_file = options.check_file op_count += 1 if options.generate is not None: arg = options.generate.lower() if arg == "h": self.action = self.action_generate_h elif arg == "c": self.action = self.action_generate_c elif arg == "c-main": self.action = self.action_generate_c_main elif arg == "table": self.action = self.action_generate_table else: self.__error(f"don't know how to generate {options.generate}") op_count += 1 if self.action == self.action_generate_table: if self.algorithm & self.algo_table_driven == 0: self.__error("the --generate table option is incompatible with the --algorithm option") self.algorithm = self.algo_table_driven elif self.algorithm not in {self.algo_bit_by_bit, self.algo_bit_by_bit_fast, self.algo_table_driven}: self.__error("select an algorithm to be used in the generated file. (Hint: use the --algorithm option.)") else: if self.table_idx_width != 8: self.__warning("reverting to Table Index Width = 8 for internal CRC calculation") self.table_idx_width = 8 self.tbl_width = 1 << self.table_idx_width if op_count == 0: self.action = self.action_check_str if op_count > 1: self.__error("too many actions specified") return op_count def _validate_action(self, options: argparse.Namespace, args: list[str], undefined_params: list[str]) -> None: """ Perform the checks that depend on the resolved action. """ c_generation_actions = { self.action_generate_h, self.action_generate_c, self.action_generate_c_main, self.action_generate_table, } if self.width is not None and self.width > 64 and self.crc_type is None and self.action in c_generation_actions: self.__error( "width values greater than 64 bits cannot be represented " "by the type of the generated C code; use --crc-type to " "supply a wider integer type" ) if len(args) != 0: self.__error("unrecognized argument(s): {0:s}".format(" ".join(args))) def_params_acts = ( self.action_check_str, self.action_check_hex_str, self.action_check_file, self.action_generate_table, ) if self.undefined_crc_parameters and self.action in set(def_params_acts): undefined_params_str = ", ".join(undefined_params) self.__error(f"undefined parameters: Add {undefined_params_str} or use --model") self.verbose = options.verbose def __warning(self, message: str) -> None: """ Print a warning message to stderr. """ sys.stderr.write(f"{self.program_name}: warning: {message}\n") def __error(self, message: str) -> NoReturn: """ Print an error message to stderr and terminate the program. """ self.__warning(message) sys.exit(1) class _ModelAction(argparse.Action): """ Set the individual model parameters when the --model option is given. """ def __call__( self, parser: argparse.ArgumentParser, namespace: argparse.Namespace, values: str | Sequence[Any] | None, option_string: str | None = None, ) -> None: """ Set the individual model parameters from the selected model. """ assert isinstance(values, str) models = CrcModels() model = models.get_params(values.lower()) if model is None: model_list = ", ".join(models.names()) raise argparse.ArgumentError(self, f"unsupported model {values}. Supported models are: {model_list}.") for key in ("width", "poly", "reflect_in", "xor_in", "reflect_out", "xor_out"): setattr(namespace, key, model[key]) setattr(namespace, self.dest, values) def _hex_type(value: str) -> int: """ Convert a decimal or hexadecimal integer string to an integer. """ try: if value.lower().startswith("0x"): return int(value, 16) return int(value) except ValueError as exc: raise argparse.ArgumentTypeError(f"invalid integer or hexadecimal value: {value}.") from exc def _bool_type(value: str) -> bool: """ Convert 0/1, "true" or "false" to a boolean value. """ if value.isdigit(): return int(value, 10) != 0 if value.lower() == "false": return False if value.lower() == "true": return True raise argparse.ArgumentTypeError(f"invalid boolean value: {value}.") pycrc-0.12.0/src/pycrc/py.typed0000644000000000000000000000000013615410400013226 0ustar00pycrc-0.12.0/src/pycrc/symtable.py0000644000000000000000000003416713615410400013746 0ustar00# pycrc -- parameterisable CRC calculation utility and C source code generator # # Copyright (c) 2006-2026 Thomas Pircher # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. """ Symbol table for the macro processor used by pycrc. use as follows: import pycrc.opt as opt import pycrc.symtable as sym opt = opt.Options() sym = sym.SymbolTable(opt) print(sym.crc_width) print(f'width: {sym.crc_width}, poly: {sym.crc_poly}') """ from __future__ import annotations import os import time from typing import TYPE_CHECKING from pycrc.algorithms import Crc if TYPE_CHECKING: from pycrc.opt import Options class SymbolTable: """ The rendering context for the code generator. Despite the name this is not a mutable symbol table with name lookups: it is an immutable bag of pre-computed, pre-formatted symbols (the C type names, the reflect/init function names, the prefix strings, ...) that the code generator emits. It is kept for backwards compatibility; new code may refer to it as :data:`RenderContext`. """ def __init__(self, opt: Options) -> None: """ Create a symbol table for the given options. """ self._opt: Options = opt self.tbl_shift = _tbl_shift(opt) self.datetime = _get_datetime() self.program_version = self._opt.version_str self.program_url = self._opt.web_address self.filename = "pycrc_stdout" if self._opt.output_file is None else os.path.basename(self._opt.output_file) self.header_filename = _pretty_header_filename(self._opt.output_file) self.header_protection = _pretty_hdrprotection(self._opt) self.crc_algorithm = _pretty_algorithm(self._opt) self.crc_width = _pretty_str(self._opt.width) self.crc_poly = _pretty_hex(self._opt.poly, self._opt.width) self.crc_reflect_in = _pretty_bool(self._opt.reflect_in) self.crc_xor_in = _pretty_hex(self._opt.xor_in, self._opt.width) self.crc_reflect_out = _pretty_bool(self._opt.reflect_out) self.crc_xor_out = _pretty_hex(self._opt.xor_out, self._opt.width) self.crc_slice_by = _pretty_str(self._opt.slice_by) self.crc_table_idx_width = str(self._opt.table_idx_width) self.crc_table_width = _pretty_str(1 << self._opt.table_idx_width) self.crc_table_mask = _pretty_hex(self._opt.tbl_width - 1, 8) self.crc_mask = _pretty_hex(self._opt.mask, self._opt.width) self.crc_msb_mask = _pretty_hex(self._opt.msb_mask, self._opt.width) self.crc_shift = _pretty_str(self.tbl_shift) self.cfg_width = self.crc_width if self._opt.width is not None else "cfg->width" self.cfg_poly = self.crc_poly if self._opt.poly is not None else "cfg->poly" self.cfg_reflect_in = self.crc_reflect_in if self._opt.reflect_in is not None else "cfg->reflect_in" self.cfg_xor_in = self.crc_xor_in if self._opt.xor_in is not None else "cfg->xor_in" self.cfg_reflect_out = self.crc_reflect_out if self._opt.reflect_out is not None else "cfg->reflect_out" self.cfg_xor_out = self.crc_xor_out if self._opt.xor_out is not None else "cfg->xor_out" self.cfg_table_idx_width = ( self.crc_table_idx_width if self._opt.table_idx_width is not None else "cfg->table_idx_width" ) self.cfg_table_width = self.crc_table_width if self._opt.tbl_width is not None else "cfg->table_width" self.cfg_mask = self.crc_mask if self._opt.mask is not None else "cfg->crc_mask" self.cfg_msb_mask = self.crc_msb_mask if self._opt.msb_mask is not None else "cfg->msb_mask" self.cfg_shift = self.crc_shift if self.tbl_shift is not None else "cfg->crc_shift" self.cfg_poly_shifted = ( f"({self.cfg_poly} << {self.cfg_shift})" if self.tbl_shift is None or self.tbl_shift > 0 else self.cfg_poly ) self.cfg_mask_shifted = ( f"({self.cfg_mask} << {self.cfg_shift})" if self.tbl_shift is None or self.tbl_shift > 0 else self.cfg_mask ) self.cfg_msb_mask_shifted = ( f"({self.cfg_msb_mask} << {self.cfg_shift})" if self.tbl_shift is None or self.tbl_shift > 0 else self.cfg_msb_mask ) self.c_bool = "int" if self._opt.c_std == "C89" else "bool" self.c_true = "1" if self._opt.c_std == "C89" else "true" self.c_false = "0" if self._opt.c_std == "C89" else "false" self.underlying_crc_t = _get_underlying_crc_t(self._opt) self.crc_t = self._opt.symbol_prefix + "t" self.cfg_t = self._opt.symbol_prefix + "cfg_t" self.crc_reflect_function = self._opt.symbol_prefix + "reflect" self.crc_table_gen_function = self._opt.symbol_prefix + "table_gen" self.crc_init_function = self._opt.symbol_prefix + "init" self.crc_update_function = self._opt.symbol_prefix + "update" self.crc_finalize_function = self._opt.symbol_prefix + "finalize" self._crc_init_value: str | None = None self._crc_table_init: str | None = None @property def crc_init_value(self) -> str | None: """ The initial CRC value, or None if it must be taken from cfg at run time. """ if self._crc_init_value is None: self._crc_init_value = _get_init_value(self._opt) return self._crc_init_value @property def crc_table_init(self) -> str: """ The precalculated CRC table used by the table-driven algorithm. """ if self._crc_table_init is None: self._crc_table_init = _get_table_init(self._opt) return self._crc_table_init #: Preferred name for :class:`SymbolTable`, which better describes its role. RenderContext = SymbolTable def _pretty_str(value: int | str | None) -> str: """ Return a value of width bits as a pretty string. """ if value is None: return "Undefined" return str(value) def _pretty_hex(value: int | None, width: int | None = None) -> str: """ Return a value of width bits as a pretty hexadecimal formatted string. """ if value is None: return "Undefined" if width is None: return f"{value:#x}" width = (width + 3) // 4 hex_str = f"{{0:#0{width + 2:d}x}}" return hex_str.format(value) def _pretty_bool(value: bool | None) -> str: """ Return a boolen value of width bits as a pretty formatted string. """ if value is None: return "Undefined" return "True" if value else "False" def _get_datetime() -> str: """ Return the timestamp used in the generated file headers. If the SOURCE_DATE_EPOCH environment variable is set, the timestamp is derived from it so that generated files are reproducible. Otherwise the current local time is used. """ source_date_epoch = os.environ.get("SOURCE_DATE_EPOCH") if source_date_epoch is not None: try: return time.strftime("%a %b %d %H:%M:%S %Y", time.gmtime(int(source_date_epoch))) except (ValueError, OverflowError): pass return time.asctime() def _pretty_algorithm(opt: Options) -> str: """ Return the algorithm name. """ if opt.algorithm == opt.algo_bit_by_bit: return "bit-by-bit" elif opt.algorithm == opt.algo_bit_by_bit_fast: return "bit-by-bit-fast" elif opt.algorithm == opt.algo_table_driven: return "table-driven" else: return "UNDEFINED" def _pretty_header_filename(filename: str | None) -> str: """ Return the sanitized filename of a header file. """ if filename is None: return "pycrc_stdout.h" filename = os.path.basename(filename) if filename[-2:] == ".c": return filename[0:-1] + "h" else: return filename + ".h" def _pretty_hdrprotection(opt: Options) -> str: """ Return the name of a C header protection (e.g. CRC_IMPLEMENTATION_H). """ filename = "pycrc_stdout" if opt.output_file is None else os.path.basename(opt.output_file) out_str = "".join([s.upper() if s.isalnum() else "_" for s in filename]) if out_str and out_str[0].isdigit(): out_str = "_" + out_str return out_str def _get_underlying_crc_t(opt: Options) -> str: """ Return the C type of the crc_t typedef. """ if opt.crc_type is not None: return opt.crc_type if opt.c_std == "C89": if opt.width is None: return "unsigned long int" if opt.width <= 8: return "unsigned char" if opt.width <= 16: return "unsigned int" return "unsigned long int" else: # C99 if opt.width is None: return "unsigned long long int" if opt.width <= 8: return "uint_fast8_t" if opt.width <= 16: return "uint_fast16_t" if opt.width <= 32: return "uint_fast32_t" if opt.width <= 64: return "uint_fast64_t" if opt.width <= 128: return "uint_fast128_t" return "uintmax_t" def _get_init_value(opt: Options) -> str | None: """ Return the init value of a C implementation, according to the selected algorithm and to the given options. If no default option is given for a given parameter, value in the cfg_t structure must be used. The init value only depends on a subset of the model parameters (for example the bit-by-bit init value does not depend on the reflection flags). Crc() still requires a fully defined model, so the parameters the init value does not depend on are passed as neutral placeholders; they cannot influence the result. Parameters the init value does depend on are checked above and cause the value to be taken from cfg at run time. """ if opt.algorithm == opt.algo_bit_by_bit: if opt.xor_in is None or opt.width is None or opt.poly is None: return None # crc.nondirect_init depends only on width, poly and xor_in. crc = Crc( width=opt.width, poly=opt.poly, reflect_in=opt.reflect_in if opt.reflect_in is not None else False, xor_in=opt.xor_in, reflect_out=False, xor_out=0, table_idx_width=opt.tbl_idx_width, ) init = crc.nondirect_init elif opt.algorithm == opt.algo_bit_by_bit_fast: if opt.xor_in is None: return None init = opt.xor_in elif opt.algorithm == opt.algo_table_driven: if opt.reflect_in is None or opt.xor_in is None or opt.width is None: return None poly = 0 if opt.poly is None else opt.poly # The table-driven init value depends only on width, reflect_in and # xor_in. crc = Crc( width=opt.width, poly=poly, reflect_in=opt.reflect_in, xor_in=opt.xor_in, reflect_out=False, xor_out=0, table_idx_width=opt.tbl_idx_width, ) init = crc.reflect(crc.direct_init, opt.width) if opt.reflect_in else crc.direct_init else: init = 0 return _pretty_hex(init, opt.width) def _get_simple_table(opt: Options, crc_tbl: list[int], values_per_line: int, format_width: int, indent: int) -> str: """ Get one CRC table, formatted as string with appropriate indenting and line breaks. """ out = "" for i in range(opt.tbl_width): if i % values_per_line == 0: out += " " * indent tbl_val = _pretty_hex(crc_tbl[i], format_width) if i == (opt.tbl_width - 1): out += f"{tbl_val:s}" elif i % values_per_line == (values_per_line - 1): out += f"{tbl_val:s},\n" else: out += f"{tbl_val:s}, " return out def _get_table_init(opt: Options) -> str: # TODO: change to return a list """ Return the precalculated CRC table for the table_driven implementation. """ if opt.algorithm != opt.algo_table_driven: return "0" if opt.width is None or opt.poly is None or opt.reflect_in is None: return "0" crc = Crc( width=opt.width, poly=opt.poly, reflect_in=opt.reflect_in, xor_in=0, reflect_out=False, xor_out=0, # set unimportant variables to known values table_idx_width=opt.table_idx_width, slice_by=opt.slice_by, ) crc_tbl = crc.table if opt.width > 32: values_per_line = 4 elif opt.width >= 16: values_per_line = 8 else: values_per_line = 16 format_width = max(opt.width, 8) indent = 4 if opt.slice_by == 1 else 8 rows = [""] * opt.slice_by for i in range(opt.slice_by): rows[i] = _get_simple_table(opt, crc_tbl[i], values_per_line, format_width, indent) fixed_indent = " " * (indent - 4) out = f"{fixed_indent:s}{{\n" + f"\n{fixed_indent:s}}},\n{fixed_indent:s}{{\n".join(rows) + f"\n{fixed_indent:s}}}" if opt.slice_by == 1: return out return "{\n" + out + "\n}" def _tbl_shift(opt: Options) -> int | None: """ Return the table shift value """ if opt.algorithm == opt.algo_table_driven and (opt.width is None or opt.width < 8): if opt.width is None: return None else: return 8 - opt.width else: return 0 pycrc-0.12.0/test/__init__.py0000644000000000000000000000003413615410400012717 0ustar00"""Test suite for pycrc.""" pycrc-0.12.0/test/check_files.sh0000644000000000000000000000616213615410400013411 0ustar00#!/bin/sh set -e PYCRC=`dirname $0`/../src/pycrc.py outdir_old="/tmp/pycrc_out" outdir_new="/tmp/pycrc_new" tarfile="pycrc_files.tar.gz" usage() { echo >&2 "usage: $0 [OPTIONS]" echo >&2 "" echo >&2 "with OPTIONS in" echo >&2 " -c check the generated output" echo >&2 " -g generate the database" echo >&2 " -n no cleanup: don't delete the directories with the generated code" echo >&2 " -h this help message" } opt_check=off opt_no_cleanup=off opt_generate=off while getopts cgnh opt; do case "$opt" in c) opt_check=on;; g) opt_generate=on;; n) opt_no_cleanup=on;; h) usage exit 0 ;; \?) usage # unknown flag exit 1 ;; esac done shift `expr $OPTIND - 1` if [ -e "$outdir_old" ]; then echo >&2 "Output directory $outdir_old exists!" exit 1 fi if [ -e "$outdir_new" ]; then echo >&2 "Output directory $outdir_new exists!" exit 1 fi cleanup() { if [ "$opt_no_cleanup" = "on" ]; then echo "No cleanup. Please delete $outdir_old and $outdir_new when you're done" else rm -rf "$outdir_old" "$outdir_new" fi } trap cleanup 0 1 2 3 15 generate() { outfile="$1" shift $PYCRC "$@" -o "${outfile}" sed -i -e 's/Generated on ... ... .. ..:..:.. ..../Generated on XXX XXX XX XX:XX:XX XXXX/; s/by pycrc v[0-9.]*/by pycrc vXXX/;' "${outfile}" } populate() { outdir=$1 mkdir -p "$outdir" models=`PYTHONPATH=../src python3 -c 'import pycrc.models as m; print(" ".join(m.CrcModels().names()))'` for model in "undefined" $models; do for algo in "bbb" "bbf" "tbl"; do for cstd in c89 c99; do if [ "$model" = "undefined" ]; then mod_opt= else mod_opt="--model=${model}" fi generate "${outdir}/${model}_${algo}_${cstd}.h" --generate=h --algorithm=${algo} --std=${cstd} $mod_opt generate "${outdir}/${model}_${algo}_${cstd}.c" --generate=c --algorithm=${algo} --std=${cstd} $mod_opt done done done algo=tbl for model in crc-32; do for slice in 4 8 16; do # --slice-by is only supported for C99 cstd=c99 generate "${outdir}/${model}_${algo}_sb${slice}_${cstd}.h" --generate=h --algorithm=${algo} --model=${model} --slice-by ${slice} --std=${cstd} generate "${outdir}/${model}_${algo}_sb${slice}_${cstd}.c" --generate=c --algorithm=${algo} --model=${model} --slice-by ${slice} --std=${cstd} done done } do_check() { tar xzf "$tarfile" -C "`dirname $outdir_new`" populate "$outdir_new" diff -ru "$outdir_old" "$outdir_new" } if [ "$opt_check" = "on" ]; then if [ ! -f "$tarfile" ]; then echo >&2 "Can't find tarfile $tarfile" exit 1 fi do_check fi if [ "$opt_generate" = "on" ]; then populate "$outdir_old" dirname="`dirname $outdir_old`" basename="`basename $outdir_old`" tar czf "$tarfile" -C "$dirname" "$basename" fi pycrc-0.12.0/test/main.c0000644000000000000000000002041713615410400011705 0ustar00// Copyright (c) 2006-2026 Thomas Pircher // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #include "crc.h" #include #include #include #include #include #include static bool atob(const char *str); static crc_t xtoi(const char *str); static int get_config(int argc, char *argv[], crc_cfg_t *cfg); #if CRC_ALGO_BIT_BY_BIT static crc_t crc_verify(const crc_cfg_t *cfg, crc_t crc_pre_final, crc_t crc); static crc_t crc_reflect(crc_t data, size_t data_len); #endif static bool verbose = false; static unsigned char str[256] = "123456789"; bool atob(const char *str) { if (!str) { return 0; } if (isdigit(str[0])) { return (bool)atoi(str); } if (tolower(str[0]) == 't') { return true; } return false; } crc_t xtoi(const char *str) { crc_t ret = 0; if (!str) { return 0; } if (str[0] == '0' && tolower(str[1]) == 'x') { str += 2; while (*str) { if (isdigit(*str)) ret = 16 * ret + *str - '0'; else if (isxdigit(*str)) ret = 16 * ret + tolower(*str) - 'a' + 10; else return ret; str++; } } else if (isdigit(*str)) { while (*str) { if (isdigit(*str)) ret = 10 * ret + *str - '0'; else return ret; str++; } } return ret; } int get_config(int argc, char *argv[], crc_cfg_t *cfg) { int c; int option_index; static struct option long_options[] = { {"width", 1, 0, 'w'}, {"poly", 1, 0, 'p'}, {"reflect-in", 1, 0, 'n'}, {"xor-in", 1, 0, 'i'}, {"reflect-out", 1, 0, 'u'}, {"xor-out", 1, 0, 'o'}, {"verbose", 0, 0, 'v'}, {"check-string", 1, 0, 's'}, {"table-idx-with", 1, 0, 't'}, {0, 0, 0, 0} }; while (1) { option_index = 0; c = getopt_long(argc, argv, "w:p:ni:uo:v", long_options, &option_index); if (c == -1) break; switch (c) { case 0: printf("option %s", long_options[option_index].name); if (optarg) printf(" with arg %s", optarg); printf ("\n"); case 'w': cfg->width = atoi(optarg); break; case 'p': cfg->poly = xtoi(optarg); break; case 'n': cfg->reflect_in = atob(optarg); break; case 'i': cfg->xor_in = xtoi(optarg); break; case 'u': cfg->reflect_out = atob(optarg); break; case 'o': cfg->xor_out = xtoi(optarg); break; case 's': memcpy(str, optarg, strlen(optarg) < sizeof(str) ? strlen(optarg) + 1 : sizeof(str)); str[sizeof(str) - 1] = '\0'; break; case 'v': verbose = true; break; case 't': // ignore --table_idx_width option break; case '?': return -1; case ':': fprintf(stderr, "missing argument to option %c\n", c); return -1; default: fprintf(stderr, "unhandled option %c\n", c); return -1; } } cfg->msb_mask = (crc_t)1u << (cfg->width - 1); cfg->crc_mask = (cfg->msb_mask - 1) | cfg->msb_mask; cfg->crc_shift = cfg->width < 8 ? 8 - cfg->width : 0; cfg->poly &= cfg->crc_mask; cfg->xor_in &= cfg->crc_mask; cfg->xor_out &= cfg->crc_mask; return 0; } #if CRC_ALGO_BIT_BY_BIT crc_t crc_verify(const crc_cfg_t *cfg, crc_t crc_pre_final, crc_t crc) { crc_t result; unsigned char data; unsigned int i; // don't verify if the width is not a multiple of 8 if (cfg->width % 8) { return 0; } if (cfg->xor_out) { crc ^= cfg->xor_out; } if (cfg->reflect_out) { crc = crc_reflect(crc, cfg->width); } result = crc_pre_final; for (i = 0; i < cfg->width / 8; i++) { data = (crc >> (cfg->width - 8 * i - 8)) & 0xff; if (cfg->reflect_in) { data = crc_reflect(data, 8); } result = crc_update(cfg, result, &data, 1); } // no crc_finalize, because if the CRC calculation is correct, result == 0. // A crc_finalize would XOR-in again some ones into the solution. // In theory the finalize function of the bit-by-bit algorithm // would also loop over cfg->width zero bits, but since // a) result == 0, and // b) input data == 0 // the output would always be zero return result; } crc_t crc_reflect(crc_t data, size_t data_len) { unsigned int i; crc_t ret; ret = 0; for (i = 0; i < data_len; i++) { if (data & 0x01) { ret = (ret << 1) | 1; } else { ret = ret << 1; } data >>= 1; } return ret; } #endif // CRC_ALGO_BIT_BY_BIT int main(int argc, char *argv[]) { crc_cfg_t cfg = { 0, // width 0, // poly 0, // xor_in 0, // reflect_in 0, // xor_out 0, // reflect_out 0, // crc_mask 0, // msb_mask 0, // crc_shift }; crc_t crc; crc_t crc_test, crc_pre_final; char format[20]; int ret, i; ret = get_config(argc, argv, &cfg); if (ret == 0) { # if CRC_ALGO_TABLE_DRIVEN crc_table_gen(&cfg); # endif // CRC_ALGO_TABLE_DRIVEN crc = crc_init(&cfg); crc = crc_pre_final = crc_update(&cfg, crc, str, strlen((char *)str)); crc = crc_finalize(&cfg, crc); # if CRC_ALGO_BIT_BY_BIT if (crc_verify(&cfg, crc_pre_final, crc) != 0) { fprintf(stderr, "error: crc verification failed\n"); return 1; } # endif // calculate the checksum again, but this time loop over the input // bytes one-by-one. crc_test = crc_init(&cfg); for (i = 0; str[i]; i++) { crc_test = crc_update(&cfg, crc_test, str + i, 1); } crc_test = crc_finalize(&cfg, crc_test); if (crc_test != crc) { fprintf(stderr, "error: crc loop verification failed\n"); return 1; } if (verbose) { snprintf(format, sizeof(format), "%%-16s = 0x%%0%dx\n", (unsigned int)(cfg.width + 3) / 4); printf("%-16s = %d\n", "width", (unsigned int)cfg.width); printf(format, "poly", (unsigned int)cfg.poly); printf("%-16s = %s\n", "reflect_in", cfg.reflect_in ? "true": "false"); printf(format, "xor_in", cfg.xor_in); printf("%-16s = %s\n", "reflect_out", cfg.reflect_out ? "true": "false"); printf(format, "xor_out", (unsigned int)cfg.xor_out); printf(format, "crc_mask", (unsigned int)cfg.crc_mask); printf(format, "msb_mask", (unsigned int)cfg.msb_mask); } printf("0x%llx\n", (unsigned long long int)crc); } return !ret; } pycrc-0.12.0/test/performance.sh0000644000000000000000000001253213615410400013451 0ustar00#!/bin/sh set -e PYCRC=`dirname $0`/../src/pycrc.py tmpdir= cleanup() { if [ -d "$tmpdir" ]; then rm -rf "$tmpdir" fi } trap cleanup 0 1 2 3 15 tmpdir=`mktemp -t -d pycrc-perf-XXXXXXXX` model=crc-32 prefix=bbb $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate c -o $tmpdir/crc_$prefix.c --algo bit-by-bit $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate h -o $tmpdir/crc_$prefix.h --algo bit-by-bit prefix=bbf $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate h -o $tmpdir/crc_$prefix.h --algo bit-by-bit-fast $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate c -o $tmpdir/crc_$prefix.c --algo bit-by-bit-fast prefix=tbl $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate h -o $tmpdir/crc_$prefix.h --algo table-driven $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate c -o $tmpdir/crc_$prefix.c --algo table-driven prefix=tb4 $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate h -o $tmpdir/crc_$prefix.h --algo table-driven --table-idx-width 4 $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate c -o $tmpdir/crc_$prefix.c --algo table-driven --table-idx-width 4 prefix=sb4 $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate h -o $tmpdir/crc_$prefix.h --algo table-driven --slice-by 4 $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate c -o $tmpdir/crc_$prefix.c --algo table-driven --slice-by 4 prefix=sb16 $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate h -o $tmpdir/crc_$prefix.h --algo table-driven --slice-by 16 $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate c -o $tmpdir/crc_$prefix.c --algo table-driven --slice-by 16 print_main() { cat < #include #include #include #include #include #define NUM_RUNS (128*256*256) unsigned char buf[1024]; void test_bbb(unsigned char *buf, size_t buf_len, size_t num_runs, clock_t clock_per_sec); void test_bbf(unsigned char *buf, size_t buf_len, size_t num_runs, clock_t clock_per_sec); void test_tbl(unsigned char *buf, size_t buf_len, size_t num_runs, clock_t clock_per_sec); void test_tb4(unsigned char *buf, size_t buf_len, size_t num_runs, clock_t clock_per_sec); void test_sb4(unsigned char *buf, size_t buf_len, size_t num_runs, clock_t clock_per_sec); void test_sb16(unsigned char *buf, size_t buf_len, size_t num_runs, clock_t clock_per_sec); /** * Print results. * * \param dsc Description of the test * \param buflen Length of one buffer * \param num_runs Number of runs over that buffer * \param t_user user time * \param t_sys system time * \return void *****************************************************************************/ void show_times(const char *dsc, size_t buflen, size_t num_runs, double t_user) { double mbps = (((double)buflen) * num_runs)/(1024*1024*t_user); printf("%s of %ld bytes (%ld * %ld)\n", dsc, (long)buflen*num_runs, (long)buflen, (long)num_runs); printf("%13s: %7.3f s %13s: %7.3f MiB/s\n", "user time", t_user, "throughput", mbps); printf("\n"); } /** * C main function. * * \retval 0 on success * \retval 1 on failure *****************************************************************************/ int main(void) { unsigned int i; long int clock_per_sec; for (i = 0; i < sizeof(buf); i++) { buf[i] = (unsigned char)rand(); } clock_per_sec = sysconf(_SC_CLK_TCK); // bit-by-bit test_bbb(buf, sizeof(buf), NUM_RUNS / 8, clock_per_sec); // bit-by-bit-fast test_bbf(buf, sizeof(buf), NUM_RUNS / 8, clock_per_sec); // table-driven test_tbl(buf, sizeof(buf), NUM_RUNS, clock_per_sec); // table-driven idx4 test_tb4(buf, sizeof(buf), NUM_RUNS / 2, clock_per_sec); // table-driven slice-by 4 test_sb4(buf, sizeof(buf), NUM_RUNS, clock_per_sec); // table-driven slice-by 16 test_sb16(buf, sizeof(buf), NUM_RUNS, clock_per_sec); return 0; } EOF } print_routine() { algo=$1 prefix=$2 cat < $tmpdir/performance.c print_routine "bit-by-bit" bbb >> $tmpdir/performance.c print_routine "bit-by-bit-fast" bbf >> $tmpdir/performance.c print_routine "table-driven" tbl >> $tmpdir/performance.c print_routine "table-driven idx4" tb4 >> $tmpdir/performance.c print_routine "table-driven sb4" sb4 >> $tmpdir/performance.c print_routine "table-driven sb16" sb16 >> $tmpdir/performance.c cc -W -Wall -O3 -o $tmpdir/perf $tmpdir/crc_bbb.c $tmpdir/crc_bbf.c $tmpdir/crc_tbl.c $tmpdir/crc_tb4.c $tmpdir/crc_sb4.c $tmpdir/crc_sb16.c $tmpdir/performance.c $tmpdir/perf pycrc-0.12.0/test/test_algorithms.py0000644000000000000000000001335313615410400014400 0ustar00#!/usr/bin/env python3 """Tests for the new pure Python CRC API. The tests for the legacy API live in ``test_algorithms_legacy.py``. """ import binascii import zlib import pytest from pycrc.algorithms import Crc from pycrc.models import CrcModels def test_table_driven_index_widths() -> None: """table_driven() supports every index width that divides 8.""" for reflect in (False, True): for tbl_idx_width in (1, 2, 4, 8): algo = Crc( width=8, poly=0x07, reflect_in=reflect, xor_in=0, reflect_out=reflect, xor_out=0, table_idx_width=tbl_idx_width, ) assert algo.table_driven("123456789") == algo.bit_by_bit("123456789") def test_all_models_all_index_widths() -> None: """table_driven() must agree with bit_by_bit() for every index width.""" for m in CrcModels().models: for tbl_idx_width in (1, 2, 4, 8): algo = Crc( width=m.width, poly=m.poly, reflect_in=m.reflect_in, xor_in=m.xor_in, reflect_out=m.reflect_out, xor_out=m.xor_out, table_idx_width=tbl_idx_width, ) assert algo.table_driven("123456789") == algo.bit_by_bit("123456789") def test_table_driven_rejects_non_divisor_index_width() -> None: """An index width that does not divide 8 must be rejected.""" for tbl_idx_width in (3, 5, 6, 7, 16): with pytest.raises(ValueError): Crc( width=8, poly=0x07, reflect_in=False, xor_in=0, reflect_out=False, xor_out=0, table_idx_width=tbl_idx_width, ) def test_from_model() -> None: """Crc.from_model/from_params must build the named model correctly.""" params = CrcModels().get("crc-32") assert params is not None from_model = Crc.from_model("crc-32") from_params = Crc.from_params(params) for crc in (from_model, from_params): assert crc.bit_by_bit_fast("123456789") == params.check assert crc.bit_by_bit("123456789") == params.check def test_from_model_unknown() -> None: """An unknown model name must raise a clear error.""" with pytest.raises(ValueError): Crc.from_model("no-such-model") def test_model_mapping() -> None: """CrcModels behaves like a read-only mapping of name to CrcParams.""" models = CrcModels() assert "crc-32" in models assert "CRC-32" in models assert models["crc-32"] is models.get("crc-32") assert len(models) == len(models.names()) assert models.get("no-such-model") is None assert models.get_params("no-such-model") is None with pytest.raises(KeyError): models["no-such-model"] def test_legacy_get_params_returns_a_copy() -> None: """The legacy get_params() returns a mutable dict that does not modify the model.""" models = CrcModels() legacy = models.get_params("crc-32") assert legacy is not None legacy["xor_in"] = 0x12345678 assert models["crc-32"].xor_in != 0x12345678 def test_streaming_update_digest() -> None: """Streaming update()/digest() must match the one-shot bit_by_bit_fast().""" check_str = "123456789" for m in CrcModels().models: algo = Crc.from_params(m) for size in (1, 3, 7, len(check_str)): algo.reset() for i in range(0, len(check_str), size): algo.update(check_str[i : i + size]) assert algo.digest() == algo.bit_by_bit_fast(check_str) def test_digest_empty() -> None: """digest() without update() equals the CRC of the empty message.""" for m in CrcModels().models: algo = Crc.from_params(m) assert algo.digest() == algo.bit_by_bit_fast(b"") algo.update(b"") assert algo.digest() == algo.bit_by_bit_fast(b"") def test_explicit_encoding() -> None: """Strings are encoded with the requested encoding.""" algo = Crc.from_model("crc-32") text = "Grüße" assert algo.bit_by_bit_fast(text) == algo.bit_by_bit_fast(text.encode("utf-8")) assert algo.bit_by_bit_fast(text, encoding="latin-1") == algo.bit_by_bit_fast(text.encode("latin-1")) assert algo.table_driven(text, encoding="latin-1") == algo.bit_by_bit_fast(text.encode("latin-1")) def test_repr() -> None: """repr() must show the model parameters.""" text = repr(Crc.from_model("crc-32")) assert "width=32" in text assert "poly=0x4c11db7" in text assert "reflect_out=True" in text def test_table_property_and_alias() -> None: """The table is exposed read-only and tbl_idx_width remains an alias.""" algo = Crc.from_model("crc-32") assert algo.table is algo.tbl assert algo.gen_table() == algo.table assert algo.tbl_idx_width == algo.table_idx_width == 8 def test_reflect_is_static() -> None: """reflect() can be called without an instance.""" assert Crc.reflect(0x80, 8) == 0x01 assert Crc.from_model("crc-32").reflect(0x01, 8) == 0x80 def test_matches_zlib_crc32() -> None: """The crc-32 model must match zlib.crc32.""" algo = Crc.from_model("crc-32") for data in (b"", b"123456789", b"The quick brown fox jumps over the lazy dog"): assert algo.bit_by_bit_fast(data) == zlib.crc32(data) assert algo.table_driven(data) == zlib.crc32(data) def test_matches_binascii_crc_hqx() -> None: """The xmodem model must match binascii.crc_hqx.""" algo = Crc.from_model("xmodem") for data in (b"", b"123456789", b"The quick brown fox jumps over the lazy dog"): assert algo.bit_by_bit_fast(data) == binascii.crc_hqx(data, 0) assert algo.table_driven(data) == binascii.crc_hqx(data, 0) pycrc-0.12.0/test/test_algorithms_legacy.py0000644000000000000000000001054613615410400015725 0ustar00#!/usr/bin/env python3 """Regression tests for the legacy pure Python CRC API. These tests exercise the API that existed before the typed ``CrcParams`` model and the general table index width support were added. They are kept in a separate file so that the behaviour of the legacy API stays covered alongside the tests in ``test_algorithms.py``. """ import logging from pycrc.algorithms import Crc from pycrc.models import CrcModels LOGGER = logging.getLogger(__name__) def check_crc(algo: Crc, check_str: str | bytes, expected_crc: int | None = None) -> None: """Check that all three algorithms agree and optionally match expected_crc.""" res_bbb = algo.bit_by_bit(check_str) res_bbf = algo.bit_by_bit_fast(check_str) res_tbl = algo.table_driven(check_str) LOGGER.info( f"Crc(width={algo.width:#x}, poly={algo.poly:#x}, " f"reflect_in={algo.reflect_in:#x}, xor_in={algo.xor_in:#x}, " f"reflect_out={algo.reflect_out:#x}, xor_out={algo.xor_out:#x}), " f"expected_crc={expected_crc}, " f"bbb={res_bbb:#x}, bbf={res_bbf:#x}, tbl={res_tbl:#x}" ) if expected_crc is not None: assert res_bbb == res_bbf == res_tbl == expected_crc assert res_bbb == res_bbf == res_tbl def test_all_models_with_check_input() -> None: """ Test all models using the basic check sequence. """ check_str = "123456789" for m in CrcModels().models: algo = Crc( width=m["width"], poly=m["poly"], reflect_in=m["reflect_in"], xor_in=m["xor_in"], reflect_out=m["reflect_out"], xor_out=m["xor_out"], ) check_crc(algo, check_str, m["check"]) def test_all_models_with_cornercase_input() -> None: """ Use corner case input strings """ for m in CrcModels().models: algo = Crc( width=m["width"], poly=m["poly"], reflect_in=m["reflect_in"], xor_in=m["xor_in"], reflect_out=m["reflect_out"], xor_out=m["xor_out"], ) for check_str in "", b"", b"\0", b"\1", b"\0\0\0\0", b"\xff": check_crc(algo, check_str) def test_other_models() -> None: """ Test random parameters. """ check_str = "123456789" for width in [5, 8, 16, 32, 65, 513]: mask = (1 << width) - 1 for poly in [0x8005, 0x4C11DB7, 0xA5A5A5A5]: poly &= mask for reflect_in in [0, 1]: for reflect_out in [0, 1]: for xor_in in [0x0, 0x1, 0x5A5A5A5A]: xor_in &= mask for xor_out in [0x0, 0x1, 0x5A5A5A5A]: xor_out &= mask algo = Crc( width=width, poly=poly, reflect_in=reflect_in, xor_in=xor_in, reflect_out=reflect_out, xor_out=xor_out, ) check_crc(algo, check_str) def test_all_algorithms_mask_xor_out() -> None: """ All algorithms must return a value within the configured width, even if xor_out has bits set above the width. """ xor_out = 0x1FF for reflect in (False, True): algo = Crc(width=8, poly=0x07, reflect_in=reflect, xor_in=0, reflect_out=reflect, xor_out=xor_out) for res in (algo.bit_by_bit("123456789"), algo.bit_by_bit_fast("123456789"), algo.table_driven("123456789")): assert res == res & algo.mask def test_incremental_bit_by_bit_fast() -> None: """ Feeding the data in chunks through bit_by_bit_fast_update() must give the same result as passing it all at once. """ check_str = "123456789" for m in CrcModels().models: algo = Crc( width=m["width"], poly=m["poly"], reflect_in=m["reflect_in"], xor_in=m["xor_in"], reflect_out=m["reflect_out"], xor_out=m["xor_out"], ) reg = algo.direct_init for octet in check_str.encode("ascii"): reg = algo.bit_by_bit_fast_update(reg, bytes([octet])) if algo.reflect_out: reg = algo.reflect(reg, algo.width) assert (reg ^ algo.xor_out) & algo.mask == algo.bit_by_bit_fast(check_str) pycrc-0.12.0/test/test_cli.py0000644000000000000000000000540613615410400012776 0ustar00#!/usr/bin/env python3 """Tests for the pycrc command line interface.""" import logging import subprocess import sys import tempfile from collections.abc import Mapping from typing import Any from pycrc.models import CrcModels LOGGER = logging.getLogger(__name__) class TestCli: """End-to-end tests that invoke the pycrc CLI.""" def test_cli(self) -> None: """All models must yield their check value for every CLI input type.""" check_bytes = b"123456789" with tempfile.NamedTemporaryFile(prefix="pycrc-test.") as f: f.write(check_bytes) f.seek(0) for m in CrcModels().models: expected_crc = m["check"] args = args_from_model(m) check_crc(["--model", m["name"]], expected_crc) check_crc(args + ["--check-string", check_bytes.decode("utf-8")], expected_crc) check_crc(args + ["--check-hexstring", "".join([f"{i:02x}" for i in check_bytes])], expected_crc) check_crc(args + ["--check-file", f.name], expected_crc) def test_invalid_hexstring(self) -> None: """An invalid hex string must fail with a message, not a traceback.""" ret = subprocess.run( [sys.executable, "src/pycrc.py", "--model", "crc-32", "--check-hexstring", "zz"], capture_output=True, text=True ) assert ret.returncode != 0 assert "invalid hex string" in ret.stderr assert "Traceback" not in ret.stderr def run_cmd(cmd: list[str]) -> subprocess.CompletedProcess[bytes]: """Run a command, raising on failure, and return the completed process.""" LOGGER.info(" ".join(cmd)) ret = subprocess.run(cmd, check=True, capture_output=True) return ret def run_pycrc(args: list[str]) -> str: """Run pycrc with the given arguments and return its stripped stdout.""" ret = run_cmd([sys.executable, "src/pycrc.py"] + args) return ret.stdout.decode("utf-8").rstrip() def check_crc(args: list[str], expected_crc: int) -> None: """Run pycrc and assert that the printed checksum equals expected_crc.""" res = run_pycrc(args) assert res[:2] == "0x" assert int(res, 16) == expected_crc def args_from_model(m: Mapping[str, Any]) -> list[str]: """Return the CLI arguments that describe the given CRC model.""" args = [] if "width" in m: args += ["--width", f"{m['width']:d}"] if "poly" in m: args += ["--poly", f"{m['poly']:#x}"] if "xor_in" in m: args += ["--xor-in", f"{m['xor_in']:#x}"] if "reflect_in" in m: args += ["--reflect-in", f"{m['reflect_in']}"] if "xor_out" in m: args += ["--xor-out", f"{m['xor_out']:#x}"] if "reflect_out" in m: args += ["--reflect-out", f"{m['reflect_out']}"] return args pycrc-0.12.0/test/test_codegen.py0000644000000000000000000002756513615410400013645 0ustar00#!/usr/bin/env python3 """End-to-end tests that generate, compile and run the C code.""" import itertools import logging import os import subprocess import sys import tempfile from collections.abc import Mapping, Sequence from typing import Any import pytest from pycrc.algorithms import Crc from pycrc.models import CrcModels LOGGER = logging.getLogger(__name__) use_algo_bit_by_bit = True use_algo_bit_by_bit_fast = True use_algo_table_driven = True use_algo_table_slice = True use_c89 = True class TestCodeGen: """Compile and run the generated code for many option combinations.""" @pytest.mark.skipif(not use_algo_bit_by_bit, reason="bbb tests disabled") def test_models_bbb_c99(self) -> None: """Compile and run all models with the bit-by-bit algorithm (C99).""" compile_and_test_models("bbb", "c99") @pytest.mark.skipif(not use_c89, reason="c89 tests disabled") @pytest.mark.skipif(not use_algo_bit_by_bit, reason="bbb tests disabled") def test_models_bbb_c89(self) -> None: """Compile and run all models with the bit-by-bit algorithm (C89).""" compile_and_test_models("bbb", "c89") @pytest.mark.skipif(not use_algo_bit_by_bit_fast, reason="bbf tests disabled") def test_models_bbf_c99(self) -> None: """Compile and run all models with the bit-by-bit-fast algorithm (C99).""" compile_and_test_models("bbf", "c99") @pytest.mark.skipif(not use_c89, reason="c89 tests disabled") @pytest.mark.skipif(not use_algo_bit_by_bit_fast, reason="bbf tests disabled") def test_models_bbf_c89(self) -> None: """Compile and run all models with the bit-by-bit-fast algorithm (C89).""" compile_and_test_models("bbf", "c89") @pytest.mark.skipif(not use_algo_table_driven, reason="tbl tests disabled") def test_models_tbl_c99(self) -> None: """Compile and run all models with the table-driven algorithm (C99).""" compile_and_test_models("tbl", "c99") @pytest.mark.skipif(not use_c89, reason="c89 tests disabled") @pytest.mark.skipif(not use_algo_table_driven, reason="tbl tests disabled") def test_models_tbl_c89(self) -> None: """Compile and run all models with the table-driven algorithm (C89).""" compile_and_test_models("tbl", "c89") @pytest.mark.skipif(not use_algo_table_driven, reason="tbl tests disabled") @pytest.mark.skipif(not use_algo_table_slice, reason="tbl slice tests disabled") def test_models_tbl_sb_c99(self) -> None: """Compile and run all models with the table-driven slice-by algorithm.""" compile_and_test_models("tbl", "c99", ["--slice-by", "4"]) compile_and_test_models("tbl", "c99", ["--slice-by", "8"]) compile_and_test_models("tbl", "c99", ["--slice-by", "16"]) # --slice-by not supported with C89 @pytest.mark.skipif(not use_algo_table_driven, reason="tbl tests disabled") def test_incomplete_models_tbl_c99(self) -> None: """Table-driven generation with every subset of undefined parameters.""" params = ["width", "poly", "xor_in", "reflect_in", "xor_out", "reflect_out"] for n in range(len(params)): for c in itertools.combinations(params, n): compile_and_test_incomplete_models("c99", c) def test_special_cases(self) -> None: """Check table-driven generation with non-default table index widths.""" compile_and_run_special_cases() def test_variable_width(self) -> None: """Generated code must match the Python reference across widths.""" compile_and_run_variable_width("bbb", "c99") compile_and_run_variable_width("bbf", "c99") compile_and_run_variable_width("tbl", "c99") def test_incomplete_model_reflected_init(self) -> None: """Regression test for reflecting a non-palindromic xor_in.""" compile_and_run_incomplete_reflected_init() def test_symbol_prefix_incomplete_model(self) -> None: """Regression test for --symbol-prefix in the generated code.""" compile_and_run_symbol_prefix() def run_cmd(cmd: list[str]) -> subprocess.CompletedProcess[bytes]: """Run a command, raising on failure, and return the completed process.""" LOGGER.info(" ".join(cmd)) ret = subprocess.run(cmd, check=True, capture_output=True) return ret def run_pycrc(args: list[str]) -> str: """Run pycrc with the given arguments and return its stripped stdout.""" ret = run_cmd([sys.executable, "src/pycrc.py"] + args) return ret.stdout.decode("utf-8").rstrip() def gen_src(tmpdir: str, args: list[str], name: str) -> None: """Generate the header and C main file for the given options into tmpdir.""" src_h = os.path.join(tmpdir, f"{name}.h") gen = ["--generate", "h", "-o", src_h] run_pycrc(gen + args) src_c = os.path.join(tmpdir, f"{name}.c") gen = ["--generate", "c-main", "-o", src_c] run_pycrc(gen + args) def compile_and_run(tmpdir: str, compile_args: list[str], run_args: list[str], name: str, check: int) -> None: """Generate, compile and run a model and verify the printed checksum.""" gen_src(tmpdir, compile_args, name) binary = os.path.join(tmpdir, "a.out") compile_src(binary, os.path.join(tmpdir, name + ".c")) run_and_check_res([binary] + run_args, check) def compile_and_test_models(algo: str, cstd: str, opt_args: Sequence[str] = ()) -> None: """Compile and run every known model for one algorithm and C standard.""" with tempfile.TemporaryDirectory(prefix="pycrc-test.") as tmpdir: for m in CrcModels().models: # Don't test width > 32 for C89, as I don't know how to ask for a data type > 32 bits. if cstd == "c89" and m["width"] > 32: continue args = args_from_model(m) args += ["--algorithm", algo, "--std", cstd] args += opt_args compile_and_run(tmpdir, args, [], m["name"], m["check"]) def compile_and_test_incomplete_models(cstd: str, erase_params: Sequence[str] = ()) -> None: """Compile and run crc-32 with the named model parameters left undefined.""" if cstd == "c89": pytest.skip("C89 not supported") model = CrcModels().get_params("crc-32") assert model is not None with tempfile.TemporaryDirectory(prefix="pycrc-test.") as tmpdir: for algo in ["bbb", "bbf", "tbl"]: m = dict(model) for param in erase_params: del m[param] args = args_from_model(m) args += ["--algorithm", algo, "--std", cstd] run_args = args_from_model({param: model[param] for param in erase_params}) compile_and_run(tmpdir, args, run_args, f"{m['name']}_incomplete", m["check"]) def compile_and_run_special_cases() -> None: """Check a width-5 model with table index widths 8, 4 and 2.""" with tempfile.TemporaryDirectory(prefix="pycrc-test.") as tmpdir: crc_5_args = ["--model=crc-5", "--reflect-in=0", "--algorithm", "table-driven"] compile_and_run(tmpdir, crc_5_args + ["--table-idx-width=8"], [], "special", 0x01) compile_and_run(tmpdir, crc_5_args + ["--table-idx-width=4"], [], "special", 0x01) compile_and_run(tmpdir, crc_5_args + ["--table-idx-width=2"], [], "special", 0x01) def compile_and_run_incomplete_reflected_init() -> None: """ Regression test for the table-driven init function. When the model is only partially defined, the generated crc_init() must still reflect xor_in if reflect_in is set. The existing incomplete-model tests are based on crc-32, whose xor_in is palindromic, so they cannot detect a missing reflection. Use a non-palindromic xor_in instead. """ model = CrcModels().get_params("crc-32") assert model is not None model["xor_in"] = 0x12345678 model["xor_out"] = 0x0 reference = Crc( width=model["width"], poly=model["poly"], reflect_in=model["reflect_in"], xor_in=model["xor_in"], reflect_out=model["reflect_out"], xor_out=model["xor_out"], ) check = reference.bit_by_bit_fast("123456789") with tempfile.TemporaryDirectory(prefix="pycrc-test.") as tmpdir: for erased in (("width",), ("xor_in",)): m = dict(model) for param in erased: del m[param] args = args_from_model(m) + ["--algorithm", "tbl", "--std", "c99"] run_args = args_from_model({param: model[param] for param in erased}) compile_and_run(tmpdir, args, run_args, "incomplete_reflect_init", check) def compile_and_run_symbol_prefix() -> None: """ Regression test for --symbol-prefix. The generated getopt helpers and the cfg initialisation code must use the configured symbol prefix (e.g. my_t instead of crc_t). Otherwise the generated code does not compile. Use an undefined model so that all the option-handling helpers are emitted. """ model = CrcModels().get_params("crc-32") assert model is not None with tempfile.TemporaryDirectory(prefix="pycrc-test.") as tmpdir: args = ["--symbol-prefix", "my_", "--algorithm", "tbl", "--std", "c99"] compile_and_run(tmpdir, args, args_from_model(model), "symbol_prefix", model["check"]) def compile_and_run_variable_width(algo: str, cstd: str) -> None: """Compile and run the given algorithm for a range of model widths.""" check_str = "123456789" models = CrcModels() m = models.get_params("crc-64-jones") assert m is not None with tempfile.TemporaryDirectory(prefix="pycrc-test.") as tmpdir: for width in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 23, 24, 25, 31, 32, 33, 63, 64]: mask = (1 << width) - 1 mw = { "width": width, "poly": m["poly"] & mask, "reflect_in": m["reflect_in"], "xor_in": m["xor_in"] & mask, "reflect_out": m["reflect_out"], "xor_out": m["xor_out"] & mask, } args = [ "--width", "{:d}".format(mw["width"]), "--poly", "{:#x}".format(mw["poly"]), "--xor-in", "{:#x}".format(mw["xor_in"]), "--reflect-in", "{:d}".format(mw["reflect_in"]), "--xor-out", "{:#x}".format(mw["xor_out"]), "--reflect-out", "{:d}".format(mw["reflect_out"]), ] reference = Crc( width=mw["width"], poly=mw["poly"], reflect_in=mw["reflect_in"], xor_in=mw["xor_in"], reflect_out=mw["reflect_out"], xor_out=mw["xor_out"], ) check = reference.bit_by_bit_fast(check_str) compile_and_run(tmpdir, ["--algorithm", algo, "--std", cstd] + args, [], "var_width", check) def run_and_check_res(cmd: list[str], expected_crc: int) -> None: """Run a command and assert that its checksum output matches expected_crc.""" res = run_cmd(cmd).stdout.decode("utf-8").rstrip() assert res[:2] == "0x" assert int(res, 16) == expected_crc def compile_src(out_file: str, src_file: str, cstd: str = "c99") -> None: """Compile a C source file with all warnings treated as errors.""" run_cmd(["cc", "-W", "-Wall", "-pedantic", "-Werror", f"-std={cstd}", "-o", out_file, src_file]) def args_from_model(m: Mapping[str, Any]) -> list[str]: """Return the CLI arguments that describe the given CRC model.""" args = [] if "width" in m: args += ["--width", f"{m['width']:d}"] if "poly" in m: args += ["--poly", f"{m['poly']:#x}"] if "xor_in" in m: args += ["--xor-in", f"{m['xor_in']:#x}"] if "reflect_in" in m: args += ["--reflect-in", f"{m['reflect_in']}"] if "xor_out" in m: args += ["--xor-out", f"{m['xor_out']:#x}"] if "reflect_out" in m: args += ["--reflect-out", f"{m['reflect_out']}"] return args pycrc-0.12.0/test/test_expr.py0000644000000000000000000000176613615410400013212 0ustar00#!/usr/bin/env python3 """Tests for the C expression simplifier.""" from pycrc import expr def test_sub_simplify() -> None: """Subtraction must be folded safely, in particular 0 - x.""" # A subtraction of two integers is folded into a constant. assert str(expr.Sub(5, 3).simplify()) == "2" # Subtracting zero leaves the operand unchanged. assert str(expr.Sub("x", 0).simplify()) == "x" # 0 - x must NOT be simplified to x (this would change the value). assert str(expr.Sub(0, "x").simplify()) == "0 - x" def test_other_simplifications() -> None: """The neutral and absorbing elements of the operators must simplify.""" assert str(expr.Add("x", 0).simplify()) == "x" assert str(expr.Mul("x", 0).simplify()) == "0" assert str(expr.Mul("x", 1).simplify()) == "x" assert str(expr.Xor("x", 0).simplify()) == "x" assert str(expr.And("x", 0).simplify()) == "0" assert str(expr.Shl("x", 0).simplify()) == "x" assert str(expr.Shr("x", 0).simplify()) == "x" pycrc-0.12.0/test/test_package.py0000644000000000000000000000174113615410400013620 0ustar00#!/usr/bin/env python3 """Tests for the top-level pycrc package API.""" import pytest import pycrc from pycrc.models import CrcModels def test_public_api_exports() -> None: """The public names are re-exported at package level.""" assert pycrc.Crc is not None assert pycrc.CrcModels is not None assert pycrc.CrcParams is not None assert set(pycrc.__all__) >= {"Crc", "CrcModels", "CrcParams", "crc"} def test_crc_convenience() -> None: """pycrc.crc() must match the model's check value for every algorithm.""" for m in CrcModels().models: assert pycrc.crc(m.name, "123456789") == m.check assert pycrc.crc(m.name, b"123456789", algorithm="bit-by-bit") == m.check assert pycrc.crc(m.name, b"123456789", algorithm="table-driven") == m.check def test_crc_unknown_algorithm() -> None: """An unknown algorithm must raise a clear error.""" with pytest.raises(ValueError): pycrc.crc("crc-32", b"123456789", algorithm="nope") pycrc-0.12.0/test/test_symtable.py0000644000000000000000000000771313615410400014052 0ustar00#!/usr/bin/env python3 """Unit tests for the code generation symbol table helpers.""" from pycrc import codegen, symtable from pycrc.opt import Options def _opt(**kwargs: object) -> Options: """Return an Options object with the given attributes set.""" opt = Options() for key, value in kwargs.items(): setattr(opt, key, value) return opt def test_pretty_str() -> None: """A missing value is rendered as Undefined, others as plain strings.""" assert symtable._pretty_str(None) == "Undefined" assert symtable._pretty_str(8) == "8" def test_pretty_hex() -> None: """Hex values are zero-padded to the requested width.""" assert symtable._pretty_hex(None, 16) == "Undefined" assert symtable._pretty_hex(0x0F, 8) == "0x0f" assert symtable._pretty_hex(0x01, 32) == "0x00000001" assert symtable._pretty_hex(0x2A) == "0x2a" def test_pretty_bool() -> None: """Boolean values are rendered as True/False, None as Undefined.""" assert symtable._pretty_bool(None) == "Undefined" assert symtable._pretty_bool(True) == "True" assert symtable._pretty_bool(0) == "False" def test_pretty_algorithm() -> None: """Each algorithm bitmap maps to its canonical name.""" opt = Options() opt.algorithm = opt.algo_bit_by_bit assert symtable._pretty_algorithm(opt) == "bit-by-bit" opt.algorithm = opt.algo_bit_by_bit_fast assert symtable._pretty_algorithm(opt) == "bit-by-bit-fast" opt.algorithm = opt.algo_table_driven assert symtable._pretty_algorithm(opt) == "table-driven" opt.algorithm = opt.algo_none assert symtable._pretty_algorithm(opt) == "UNDEFINED" def test_pretty_header_filename() -> None: """The header file name is derived from the output file name.""" assert symtable._pretty_header_filename(None) == "pycrc_stdout.h" assert symtable._pretty_header_filename("foo.c") == "foo.h" assert symtable._pretty_header_filename("foo") == "foo.h" def test_header_protection_from_filename() -> None: """Non-alphanumeric characters in the file name become underscores.""" opt = Options() opt.output_file = "my-crc.c" assert symtable._pretty_hdrprotection(opt) == "MY_CRC_C" def test_header_protection_leading_digit() -> None: """A leading digit must be prefixed to keep the guard a valid identifier.""" # A header guard must be a valid C identifier, so a file name starting # with a digit must not produce one that starts with a digit. opt = Options() opt.output_file = "1weird-name.c" assert symtable._pretty_hdrprotection(opt) == "_1WEIRD_NAME_C" def test_underlying_crc_t_c99() -> None: """C99 widths map onto the matching stdint types.""" assert symtable._get_underlying_crc_t(_opt(c_std="C99", width=None)) == "unsigned long long int" assert symtable._get_underlying_crc_t(_opt(c_std="C99", width=8)) == "uint_fast8_t" assert symtable._get_underlying_crc_t(_opt(c_std="C99", width=16)) == "uint_fast16_t" assert symtable._get_underlying_crc_t(_opt(c_std="C99", width=32)) == "uint_fast32_t" assert symtable._get_underlying_crc_t(_opt(c_std="C99", width=64)) == "uint_fast64_t" assert symtable._get_underlying_crc_t(_opt(c_std="C99", width=100)) == "uint_fast128_t" def test_underlying_crc_t_c89() -> None: """C89 widths map onto the built-in integer types.""" assert symtable._get_underlying_crc_t(_opt(c_std="C89", width=None)) == "unsigned long int" assert symtable._get_underlying_crc_t(_opt(c_std="C89", width=8)) == "unsigned char" assert symtable._get_underlying_crc_t(_opt(c_std="C89", width=16)) == "unsigned int" assert symtable._get_underlying_crc_t(_opt(c_std="C89", width=32)) == "unsigned long int" def test_codegen_reuses_symbol_table() -> None: """CodeGen nodes for the same options share a single SymbolTable.""" opt = Options() first = codegen.CodeGen(opt, "") second = codegen.CodeGen(opt, "") assert first.sym is second.sym assert symtable.RenderContext is symtable.SymbolTable pycrc-0.12.0/.gitignore0000644000000000000000000000062113615410400011621 0ustar00# Byte-compiled / cache __pycache__/ *.py[cod] *$py.class # Distribution / packaging build/ dist/ *.egg-info/ .eggs/ # Test / coverage / lint caches .pytest_cache/ .ruff_cache/ .mypy_cache/ .coverage .coverage.* htmlcov/ # Virtual environments .venv/ venv/ env/ # Editors / OS .svn/ *.swp .DS_Store # Generated documentation /doc/pycrc.1 /doc/pycrc.html /doc/docbook.css /test/pycrc_files.tar.gz pycrc-0.12.0/LICENSE0000644000000000000000000000207113615410400010637 0ustar00Copyright (c) 2006-2026, Thomas Pircher Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pycrc-0.12.0/README.md0000644000000000000000000001243313615410400011114 0ustar00# pycrc [pycrc](https://pycrc.org) is a free, easy to use Cyclic Redundancy Check (CRC) calculator and C source code generator. ## System Requirements Python 3.10 or newer is required to run pycrc. The last version compatible with Python 2 is v0.9.x. ## Running pycrc The easiest way to run pycrc is to install it in a virtual environment, e.g.: python3 -m venv .venv . .venv/bin/activate python3 -m pip install pycrc Then the pycrc executable is included in the path in the virtual environment: pycrc [OPTIONS] or python3 -m pycrc [OPTIONS] ## Installation Install pycrc (if required) using pip: python3 -m pip install pycrc or apt (on Debian 13 and later, Ubuntu 23.10 and later, and derivatives): sudo apt install pycrc Either of these will install a `pycrc` binary in the path. Once installed you can also run it as a module: python3 -m pycrc [OPTIONS] ## Python API pycrc can also be used as a Python library: import pycrc # One-shot checksum for a named model. print(pycrc.crc("crc-32", b"123456789")) from pycrc import Crc # Or use a model directly, choosing one of the algorithms. crc = Crc.from_model("crc-16-usb") print(crc.bit_by_bit_fast("123456789")) print(crc.table_driven(b"123456789")) # Large inputs can be processed incrementally. crc = Crc.from_model("crc-32") with open("image.bin", "rb") as f: for chunk in iter(lambda: f.read(4096), b""): crc.update(chunk) print(crc.digest()) The known models are exposed as a read-only mapping: from pycrc import CrcModels models = CrcModels() print(", ".join(models.names())) params = models["crc-32"] print(params.width, hex(params.poly), params.check) The package ships inline type annotations (PEP 561), so editors and type checkers can use them without any extra stub packages. ### Backwards compatibility The Python API is additive: the API that existed before the typed models is still available. - ``CrcModels.get_params()`` still returns a mutable ``dict`` (a copy); the immutable ``CrcParams`` objects are available through the mapping interface (``models[name]`` / ``models.get(name)``). - ``Crc.tbl_idx_width`` and ``Crc.gen_table()`` remain available as aliases for ``Crc.table_idx_width`` and ``Crc.table``. ## Development Create a virtual environment and install the package in editable mode together with the development tools: python3 -m venv .venv . .venv/bin/activate python3 -m pip install -e ".[dev]" ### Running the tests Run the whole test suite with pytest: pytest The tests in `test/test_codegen.py` and `test/test_cli.py` generate C source code, compile it and run it, so a C compiler (`cc`) must be installed and available on the `PATH`. To run a single test file, class or function, pass it to pytest: pytest test/test_algorithms.py pytest test/test_codegen.py::TestCodeGen pytest test/test_algorithms.py::test_all_models_with_check_input To measure test coverage (and enforce the minimum coverage configured in `pyproject.toml`): pytest --cov=pycrc --cov-report=term-missing The code generation and CLI tests spawn subprocesses. To include the code that runs in those subprocesses in the coverage report, point `COVERAGE_PROCESS_START` at `pyproject.toml`, as the CI workflow does: COVERAGE_PROCESS_START="$PWD/pyproject.toml" pytest --cov=pycrc --cov-report=term-missing **Checking the code style and types** ruff check . mypy ### Building the documentation The HTML manual and the man page are generated from the DocBook source in `doc`. On Debian/Ubuntu the required tools are installed with: sudo apt-get install make xsltproc docbook-xsl-ns docbook5-xml Generate the documentation with: make -C doc This writes `doc/pycrc.html`, `doc/pycrc.1` and the stylesheet `doc/docbook.css`. These generated files are not checked in. Build the source and wheel distributions with: python3 -m build The sdist target regenerates the documentation and includes the generated files in the source tarball. ## Getting help If you are new to pycrc and want to generate C code, start with [the tutorial](https://pycrc.org/tutorial.html). The [pycrc manual page](https://pycrc.org/pycrc.html) explains the command line options in some detail and also gives some more examples how to use pycrc. If you have found a bug in pycrc or want to request a feature please take the time and submit it to the [issue tracker](https://github.com/tpircher/pycrc/issues). Thanks for your help. Also see the [frequently asked questions](https://pycrc.org/faq.html). ## Feedback If you like pycrc, let me know and drop me a note. If you don't like pycrc let me know what you don't like and why. If you want some idea how to say thanks for this software, please have a look [here](https://www.tty1.net/say-thanks_en.html). ## Copyright of the generated source code The MIT licence allows commercial use of the source and the output of the program. But since I got asked on occasion whether the generated code is encumbered by the copyright of the author, I give my view on this: The code generated by pycrc is not considered a substantial portion of the software, therefore the licence does not cover the generated code; the author of pycrc will not claim any copyright on the generated code. pycrc-0.12.0/hatch_build.py0000644000000000000000000000331313615410400012452 0ustar00"""Build hook that generates the documentation before the sdist is created. The generated pages are not checked in; they are built from the DocBook sources in ``doc`` and added to the source distribution as build artifacts. """ from __future__ import annotations import shutil import subprocess from pathlib import Path from typing import Any from hatchling.builders.hooks.plugin.interface import BuildHookInterface #: Files produced by ``make -C doc``. ``docbook.css`` is generated as a #: side effect of the HTML transformation and is referenced by pycrc.html. DOC_ARTIFACTS = ( "doc/pycrc.html", "doc/pycrc.1", "doc/docbook.css", ) class DocsBuildHook(BuildHookInterface): """Build the HTML and manual-page documentation for source distributions.""" def initialize(self, version: str, build_data: dict[str, Any]) -> None: if self.target_name != "sdist": return missing = [tool for tool in ("make", "xsltproc") if shutil.which(tool) is None] if missing: tools = ", ".join(missing) raise RuntimeError( f"Building the pycrc documentation requires {tools}. " "On Debian/Ubuntu install them with: " "apt-get install make xsltproc docbook-xsl-ns docbook5-xml" ) doc_dir = Path(self.root) / "doc" subprocess.run(["make", "-C", str(doc_dir), "clean", "all"], check=True) missing_artifacts = [path for path in DOC_ARTIFACTS if not (Path(self.root) / path).is_file()] if missing_artifacts: raise RuntimeError(f"Documentation build did not produce: {', '.join(missing_artifacts)}") build_data.setdefault("artifacts", []).extend(DOC_ARTIFACTS) pycrc-0.12.0/pyproject.toml0000644000000000000000000000451313615410400012551 0ustar00[build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "pycrc" description = "A free, easy to use Cyclic Redundancy Check source code generator for C/C++" readme = "README.md" license = "MIT" license-files = ["LICENSE"] authors = [ { name = "Thomas Pircher", email = "thp.oss@p5r.uk" }, ] requires-python = ">=3.10" keywords = ["crc", "checksum", "cyclic-redundancy-check", "code-generator", "c"] classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Operating System :: OS Independent", "Programming Language :: C", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Topic :: Software Development :: Code Generators", "Topic :: Software Development :: Libraries :: Python Modules", ] dynamic = ["version"] [project.optional-dependencies] dev = [ "mypy>=1.10", "pytest>=7.0", "pytest-cov>=4.0", "ruff>=0.5", ] [project.urls] Homepage = "https://pycrc.org" Documentation = "https://pycrc.org/pycrc.html" "Bug Tracker" = "https://github.com/tpircher/pycrc/issues" "Source Code" = "https://github.com/tpircher/pycrc" [project.scripts] pycrc = "pycrc.main:main" [tool.hatch.version] path = "src/pycrc/__init__.py" [tool.hatch.build.targets.wheel] packages = ["src/pycrc"] [tool.hatch.build.targets.sdist] include = [ "/src", "/test", "/doc", "/hatch_build.py", "/AUTHORS", "/CHANGELOG.md", "/LICENSE", "/README.md", "/pyproject.toml", ] [tool.hatch.build.targets.sdist.hooks.custom] [tool.pytest.ini_options] testpaths = ["test"] addopts = "-ra" [tool.ruff] line-length = 127 target-version = "py310" [tool.ruff.lint] select = ["E4", "E7", "E9", "F", "W", "I", "UP", "B", "SIM", "C4"] ignore = ["UP030"] [tool.mypy] python_version = "3.10" files = ["src/pycrc"] [tool.coverage.run] branch = true source = ["pycrc"] # Subprocesses (the generated-code and CLI tests) inherit the coverage # configuration through COVERAGE_PROCESS_START, so collect their data too. parallel = true [tool.coverage.report] show_missing = true fail_under = 80 pycrc-0.12.0/PKG-INFO0000644000000000000000000001530713615410400010735 0ustar00Metadata-Version: 2.5 Name: pycrc Version: 0.12.0 Summary: A free, easy to use Cyclic Redundancy Check source code generator for C/C++ Project-URL: Homepage, https://pycrc.org Project-URL: Documentation, https://pycrc.org/pycrc.html Project-URL: Bug Tracker, https://github.com/tpircher/pycrc/issues Project-URL: Source Code, https://github.com/tpircher/pycrc Author-email: Thomas Pircher License-Expression: MIT License-File: LICENSE Keywords: c,checksum,code-generator,crc,cyclic-redundancy-check Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Operating System :: OS Independent Classifier: Programming Language :: C Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Classifier: Programming Language :: Python :: 3.14 Classifier: Topic :: Software Development :: Code Generators Classifier: Topic :: Software Development :: Libraries :: Python Modules Requires-Python: >=3.10 Provides-Extra: dev Requires-Dist: mypy>=1.10; extra == 'dev' Requires-Dist: pytest-cov>=4.0; extra == 'dev' Requires-Dist: pytest>=7.0; extra == 'dev' Requires-Dist: ruff>=0.5; extra == 'dev' Description-Content-Type: text/markdown # pycrc [pycrc](https://pycrc.org) is a free, easy to use Cyclic Redundancy Check (CRC) calculator and C source code generator. ## System Requirements Python 3.10 or newer is required to run pycrc. The last version compatible with Python 2 is v0.9.x. ## Running pycrc The easiest way to run pycrc is to install it in a virtual environment, e.g.: python3 -m venv .venv . .venv/bin/activate python3 -m pip install pycrc Then the pycrc executable is included in the path in the virtual environment: pycrc [OPTIONS] or python3 -m pycrc [OPTIONS] ## Installation Install pycrc (if required) using pip: python3 -m pip install pycrc or apt (on Debian 13 and later, Ubuntu 23.10 and later, and derivatives): sudo apt install pycrc Either of these will install a `pycrc` binary in the path. Once installed you can also run it as a module: python3 -m pycrc [OPTIONS] ## Python API pycrc can also be used as a Python library: import pycrc # One-shot checksum for a named model. print(pycrc.crc("crc-32", b"123456789")) from pycrc import Crc # Or use a model directly, choosing one of the algorithms. crc = Crc.from_model("crc-16-usb") print(crc.bit_by_bit_fast("123456789")) print(crc.table_driven(b"123456789")) # Large inputs can be processed incrementally. crc = Crc.from_model("crc-32") with open("image.bin", "rb") as f: for chunk in iter(lambda: f.read(4096), b""): crc.update(chunk) print(crc.digest()) The known models are exposed as a read-only mapping: from pycrc import CrcModels models = CrcModels() print(", ".join(models.names())) params = models["crc-32"] print(params.width, hex(params.poly), params.check) The package ships inline type annotations (PEP 561), so editors and type checkers can use them without any extra stub packages. ### Backwards compatibility The Python API is additive: the API that existed before the typed models is still available. - ``CrcModels.get_params()`` still returns a mutable ``dict`` (a copy); the immutable ``CrcParams`` objects are available through the mapping interface (``models[name]`` / ``models.get(name)``). - ``Crc.tbl_idx_width`` and ``Crc.gen_table()`` remain available as aliases for ``Crc.table_idx_width`` and ``Crc.table``. ## Development Create a virtual environment and install the package in editable mode together with the development tools: python3 -m venv .venv . .venv/bin/activate python3 -m pip install -e ".[dev]" ### Running the tests Run the whole test suite with pytest: pytest The tests in `test/test_codegen.py` and `test/test_cli.py` generate C source code, compile it and run it, so a C compiler (`cc`) must be installed and available on the `PATH`. To run a single test file, class or function, pass it to pytest: pytest test/test_algorithms.py pytest test/test_codegen.py::TestCodeGen pytest test/test_algorithms.py::test_all_models_with_check_input To measure test coverage (and enforce the minimum coverage configured in `pyproject.toml`): pytest --cov=pycrc --cov-report=term-missing The code generation and CLI tests spawn subprocesses. To include the code that runs in those subprocesses in the coverage report, point `COVERAGE_PROCESS_START` at `pyproject.toml`, as the CI workflow does: COVERAGE_PROCESS_START="$PWD/pyproject.toml" pytest --cov=pycrc --cov-report=term-missing **Checking the code style and types** ruff check . mypy ### Building the documentation The HTML manual and the man page are generated from the DocBook source in `doc`. On Debian/Ubuntu the required tools are installed with: sudo apt-get install make xsltproc docbook-xsl-ns docbook5-xml Generate the documentation with: make -C doc This writes `doc/pycrc.html`, `doc/pycrc.1` and the stylesheet `doc/docbook.css`. These generated files are not checked in. Build the source and wheel distributions with: python3 -m build The sdist target regenerates the documentation and includes the generated files in the source tarball. ## Getting help If you are new to pycrc and want to generate C code, start with [the tutorial](https://pycrc.org/tutorial.html). The [pycrc manual page](https://pycrc.org/pycrc.html) explains the command line options in some detail and also gives some more examples how to use pycrc. If you have found a bug in pycrc or want to request a feature please take the time and submit it to the [issue tracker](https://github.com/tpircher/pycrc/issues). Thanks for your help. Also see the [frequently asked questions](https://pycrc.org/faq.html). ## Feedback If you like pycrc, let me know and drop me a note. If you don't like pycrc let me know what you don't like and why. If you want some idea how to say thanks for this software, please have a look [here](https://www.tty1.net/say-thanks_en.html). ## Copyright of the generated source code The MIT licence allows commercial use of the source and the output of the program. But since I got asked on occasion whether the generated code is encumbered by the copyright of the author, I give my view on this: The code generated by pycrc is not considered a substantial portion of the software, therefore the licence does not cover the generated code; the author of pycrc will not claim any copyright on the generated code.