├── test ├── __init__.py ├── test_cli.py ├── test_algorithms.py ├── check_files.sh ├── performance.sh ├── test_codegen.py └── main.c ├── .gitignore ├── doc ├── style-man.xsl ├── Makefile ├── style-html.xsl └── pycrc.xml ├── AUTHORS ├── src ├── pycrc │ ├── __init__.py │ ├── __main__.py │ ├── main.py │ ├── algorithms.py │ ├── models.py │ ├── expr.py │ ├── symtable.py │ └── opt.py └── pycrc.py ├── pyproject.toml ├── LICENSE ├── .github └── workflows │ └── github-actions.yml ├── README.md └── CHANGELOG.md /test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .svn 2 | .*.swp 3 | *.pyc 4 | *.pyo 5 | __pycache__ 6 | /src/pycrc.egg-info 7 | /dist 8 | /build 9 | /doc/pycrc.1 10 | /doc/pycrc.html 11 | /doc/docbook.css 12 | /test/pycrc_files.tar.gz 13 | -------------------------------------------------------------------------------- /doc/style-man.xsl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | HTML_PARAMS = style-html.xsl 2 | MAN_PARAMS = style-man.xsl 3 | 4 | source = pycrc.xml 5 | targets = $(source:.xml=.html) $(source:.xml=.1) 6 | 7 | all: $(targets) 8 | 9 | .PHONY: clean 10 | clean: 11 | $(RM) $(targets) 12 | 13 | .PHONY: check 14 | check: 15 | xmllint --valid --noout $(source) 16 | 17 | %.html: %.xml $(HTML_PARAMS) 18 | saxon-xslt -o $@ $^ 19 | 20 | %.1: %.xml $(MAN_PARAMS) 21 | saxon-xslt -o $@ $^ 22 | 23 | %.txt: %.html 24 | links -dump -no-numbering -no-references $< > $@ 25 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Thomas Pircher: main developer. 2 | 3 | Matthias Urlichs: removed the unused 'direct' parameter and added a fix not to 4 | recurse into main() when an unknown algorithm is selected. 5 | 6 | Marti Raudsepp: improved spacing for the table-driven algorithm. 7 | 8 | Stephan Brumme: his implementation of the slice-by-x algorithm was used as a 9 | basis for the implementation in pycrc. 10 | 11 | Danjel McGougan: whose Universal Crc project was highly influential in the 12 | implementation of widths < 8 in the table-driven algorithm. 13 | 14 | André Hartmann, ashelly and others for minor fixes. 15 | -------------------------------------------------------------------------------- /doc/style-html.xsl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/pycrc/__init__.py: -------------------------------------------------------------------------------- 1 | def get_version(): 2 | try: 3 | import importlib.metadata 4 | return importlib.metadata.version("pycrc") 5 | except: # noqa: E722 6 | pass 7 | try: 8 | import re 9 | import os 10 | with open(os.path.join('..', '..', 'pyproject.toml'), 'r') as file: 11 | text = file.read() 12 | pattern = re.compile(r"""^version *= *["']([^'"]*)['"]""", re.MULTILINE) 13 | m = re.search(pattern, text) 14 | if m: 15 | return m[1] 16 | except FileNotFoundError: 17 | pass 18 | return 'unknown' 19 | 20 | 21 | __version__ = get_version() 22 | __author__ = "Thomas Pircher" 23 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=61.0"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "pycrc" 7 | version = "0.11.0" 8 | authors = [ 9 | { name="Thomas Pircher", email="tehpeh-web@tty1.net" }, 10 | ] 11 | description = "A free, easy to use Cyclic Redundancy Check source code generator for C/C++" 12 | readme = "README.md" 13 | requires-python = ">=3.8" 14 | classifiers = [ 15 | "Programming Language :: Python :: 3", 16 | "License :: OSI Approved :: MIT License", 17 | "Operating System :: OS Independent", 18 | ] 19 | dependencies = [ 20 | "importlib-metadata >= 4.0", 21 | ] 22 | 23 | [project.urls] 24 | "Homepage" = "https://pycrc.org" 25 | "Bug Tracker" = "https://github.com/tpircher/pycrc/issues" 26 | 27 | [project.scripts] 28 | pycrc = "pycrc.main:main" 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2006-2015, Thomas Pircher 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /.github/workflows/github-actions.yml: -------------------------------------------------------------------------------- 1 | name: Test and Lint 2 | 3 | on: [pull_request, push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Set up Python ${{ matrix.python-version }} 16 | uses: actions/setup-python@v5 17 | with: 18 | python-version: ${{ matrix.python-version }} 19 | - name: Install dependencies 20 | run: | 21 | python -m pip install --upgrade pip 22 | pip install flake8 pytest 23 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 24 | - name: Lint with flake8 25 | run: | 26 | # stop the build if there are Python syntax errors or undefined names 27 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 28 | # exit-zero treats all errors as warnings. The GitHub editor is 200 chars wide 29 | flake8 . --count --exit-zero --max-complexity=11 --max-line-length=127 --statistics 30 | - name: Test with pytest 31 | run: | 32 | pytest 33 | -------------------------------------------------------------------------------- /src/pycrc/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # pycrc -- parameterisable CRC calculation utility and C source code generator 4 | # 5 | # Copyright (c) 2006-2017 Thomas Pircher 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to 9 | # deal in the Software without restriction, including without limitation the 10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 11 | # sell copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23 | # IN THE SOFTWARE. 24 | 25 | from pycrc.main import main 26 | import sys 27 | 28 | 29 | if __name__ == "__main__": 30 | sys.exit(main()) 31 | -------------------------------------------------------------------------------- /src/pycrc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # pycrc -- parameterisable CRC calculation utility and C source code generator 4 | # 5 | # Copyright (c) 2006-2017 Thomas Pircher 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to 9 | # deal in the Software without restriction, including without limitation the 10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 11 | # sell copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23 | # IN THE SOFTWARE. 24 | 25 | """This is a simple wrapper/executable for the pycrc library.""" 26 | 27 | import sys 28 | from pycrc.main import main 29 | 30 | 31 | if __name__ == "__main__": 32 | sys.exit(main()) 33 | -------------------------------------------------------------------------------- /test/test_cli.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import logging 4 | import tempfile 5 | import subprocess 6 | from src.pycrc.models import CrcModels 7 | 8 | LOGGER = logging.getLogger(__name__) 9 | 10 | 11 | class TestCli: 12 | def test_cli(self): 13 | check_bytes = b"123456789" 14 | with tempfile.NamedTemporaryFile(prefix="pycrc-test.") as f: 15 | f.write(check_bytes) 16 | f.seek(0) 17 | 18 | for m in CrcModels().models: 19 | expected_crc = m["check"] 20 | args = args_from_model(m) 21 | check_crc(["--model", m["name"]], expected_crc) 22 | check_crc(args + ["--check-string", check_bytes.decode("utf-8")], expected_crc) 23 | check_crc(args + ["--check-hexstring", ''.join([f"{i:02x}" for i in check_bytes])], expected_crc) 24 | check_crc(args + ["--check-file", f.name], expected_crc) 25 | 26 | 27 | def run_cmd(cmd): 28 | LOGGER.info(' '.join(cmd)) 29 | ret = subprocess.run(cmd, check=True, capture_output=True) 30 | return ret 31 | 32 | 33 | def run_pycrc(args): 34 | ret = run_cmd(['python3', 'src/pycrc.py'] + args) 35 | return ret.stdout.decode('utf-8').rstrip() 36 | 37 | 38 | def check_crc(args, expected_crc): 39 | res = run_pycrc(args) 40 | assert res[:2] == "0x" 41 | assert int(res, 16) == expected_crc 42 | 43 | 44 | def args_from_model(m): 45 | args = [] 46 | if 'width' in m: 47 | args += ["--width", f"{m['width']:d}"] 48 | if 'poly' in m: 49 | args += ["--poly", f"{m['poly']:#x}"] 50 | if 'xor_in' in m: 51 | args += ["--xor-in", f"{m['xor_in']:#x}"] 52 | if 'reflect_in' in m: 53 | args += ["--reflect-in", f"{m['reflect_in']}"] 54 | if 'xor_out' in m: 55 | args += ["--xor-out", f"{m['xor_out']:#x}"] 56 | if 'reflect_out' in m: 57 | args += ["--reflect-out", f"{m['reflect_out']}"] 58 | return args 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pycrc 2 | ===== 3 | 4 | [pycrc](https://pycrc.org) is a free, easy to use Cyclic Redundancy Check (CRC) 5 | calculator and C source code generator. 6 | 7 | 8 | System Requirements 9 | ------------------- 10 | 11 | Python 3.8 or newer is required to run pycrc. 12 | The last version compatible with Python 2 is v0.9.x. 13 | 14 | Running pycrc 15 | ------------- 16 | 17 | This program doesn't need to be installed to be run. The script can be 18 | executed from the source directory. 19 | Simply call the python interpreter with the script as parameter: 20 | 21 | python3 src/pycrc.py [options] 22 | 23 | Installation 24 | ------------ 25 | 26 | Install pycrc (if required) using pip: 27 | 28 | python3 -m pip install pycrc 29 | 30 | or apt (on Debian 13 and later, Ubuntu 23.10 and later, and 31 | derivatives): 32 | 33 | sudo apt install pycrc 34 | 35 | Either of these will install a `pycrc` binary in the path. 36 | 37 | 38 | Getting help 39 | ------------ 40 | 41 | If you are new to pycrc and want to generate C code, start with 42 | [the tutorial](https://pycrc.org/tutorial.html). 43 | 44 | The [pycrc manual page](https://pycrc.org/pycrc.html) explains the command line 45 | options in some detail and also gives some more examples how to use pycrc. 46 | 47 | If you have found a bug in pycrc or want to request a feature please take the 48 | time and submit it to the 49 | [issue tracker](https://github.com/tpircher/pycrc/issues). 50 | Thanks for your help. 51 | 52 | Also see the [frequently asked questions](https://pycrc.org/faq.html). 53 | 54 | 55 | Feedback 56 | -------- 57 | 58 | If you like pycrc, let me know and drop me a note. If you don't like pycrc let 59 | me know what you don't like and why. 60 | If you want some idea how to say thanks for this software, please have a look 61 | [here](https://www.tty1.net/say-thanks_en.html). 62 | 63 | 64 | Copyright of the generated source code 65 | -------------------------------------- 66 | 67 | The MIT licence allows commercial use of the source and the output of the 68 | program. But since I got asked on occasion whether the generated code is 69 | encumbered by the copyright of the author, I give my view on this: 70 | 71 | The code generated by pycrc is not considered a substantial portion of the 72 | software, therefore the licence does not cover the generated code; the 73 | author of pycrc will not claim any copyright on the generated code. 74 | -------------------------------------------------------------------------------- /test/test_algorithms.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import logging 4 | from src.pycrc.models import CrcModels 5 | from src.pycrc.algorithms import Crc 6 | 7 | LOGGER = logging.getLogger(__name__) 8 | 9 | 10 | def check_crc(algo, check_str, expected_crc=None): 11 | res_bbb = algo.bit_by_bit(check_str) 12 | res_bbf = algo.bit_by_bit_fast(check_str) 13 | res_tbl = algo.table_driven(check_str) 14 | LOGGER.info(f"Crc(width={algo.width:#x}, poly={algo.poly:#x}, " 15 | f"reflect_in={algo.reflect_in:#x}, xor_in={algo.xor_in:#x}, " 16 | f"reflect_out={algo.reflect_out:#x}, xor_out={algo.xor_out:#x}), " 17 | f"expected_crc={expected_crc}, " 18 | f"bbb={res_bbb:#x}, bbf={res_bbf:#x}, tbl={res_tbl:#x}") 19 | if expected_crc is not None: 20 | assert res_bbb == res_bbf == res_tbl == expected_crc 21 | assert res_bbb == res_bbf == res_tbl 22 | 23 | 24 | def test_all_models_with_check_input(): 25 | """ 26 | Test all models using the basic check sequence. 27 | """ 28 | check_str = '123456789' 29 | for m in CrcModels().models: 30 | algo = Crc(width=m['width'], poly=m['poly'], 31 | reflect_in=m['reflect_in'], xor_in=m['xor_in'], 32 | reflect_out=m['reflect_out'], xor_out=m['xor_out']) 33 | check_crc(algo, check_str, m['check']) 34 | 35 | 36 | def test_all_models_with_cornercase_input(): 37 | """ 38 | Use corner case input strings 39 | """ 40 | for m in CrcModels().models: 41 | algo = Crc(width=m['width'], poly=m['poly'], 42 | reflect_in=m['reflect_in'], xor_in=m['xor_in'], 43 | reflect_out=m['reflect_out'], xor_out=m['xor_out']) 44 | for check_str in "", b"", b"\0", b"\1", b"\0\0\0\0", b"\xff": 45 | check_crc(algo, check_str) 46 | 47 | 48 | def test_other_models(): 49 | """ 50 | Test random parameters. 51 | """ 52 | check_str = '123456789' 53 | for width in [5, 8, 16, 32, 65, 513]: 54 | mask = ((1 << width) - 1) 55 | for poly in [0x8005, 0x4c11db7, 0xa5a5a5a5]: 56 | poly &= mask 57 | for reflect_in in [0, 1]: 58 | for reflect_out in [0, 1]: 59 | for xor_in in [0x0, 0x1, 0x5a5a5a5a]: 60 | xor_in &= mask 61 | for xor_out in [0x0, 0x1, 0x5a5a5a5a]: 62 | xor_out &= mask 63 | algo = Crc(width=width, poly=poly, 64 | reflect_in=reflect_in, xor_in=xor_in, 65 | reflect_out=reflect_out, xor_out=xor_out) 66 | check_crc(algo, check_str) 67 | -------------------------------------------------------------------------------- /test/check_files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | PYCRC=`dirname $0`/../src/pycrc.py 5 | outdir_old="/tmp/pycrc_out" 6 | outdir_new="/tmp/pycrc_new" 7 | tarfile="pycrc_files.tar.gz" 8 | 9 | usage() { 10 | echo >&2 "usage: $0 [OPTIONS]" 11 | echo >&2 "" 12 | echo >&2 "with OPTIONS in" 13 | echo >&2 " -c check the generated output" 14 | echo >&2 " -g generate the database" 15 | echo >&2 " -n no cleanup: don't delete the directories with the generated code" 16 | echo >&2 " -h this help message" 17 | } 18 | 19 | 20 | opt_check=off 21 | opt_no_cleanup=off 22 | opt_generate=off 23 | 24 | while getopts cgnh opt; do 25 | case "$opt" in 26 | c) opt_check=on;; 27 | g) opt_generate=on;; 28 | n) opt_no_cleanup=on;; 29 | h) usage 30 | exit 0 31 | ;; 32 | \?) usage # unknown flag 33 | exit 1 34 | ;; 35 | esac 36 | done 37 | shift `expr $OPTIND - 1` 38 | 39 | if [ -e "$outdir_old" ]; then 40 | echo >&2 "Output directory $outdir_old exists!" 41 | exit 1 42 | fi 43 | if [ -e "$outdir_new" ]; then 44 | echo >&2 "Output directory $outdir_new exists!" 45 | exit 1 46 | fi 47 | 48 | 49 | cleanup() { 50 | if [ "$opt_no_cleanup" = "on" ]; then 51 | echo "No cleanup. Please delete $outdir_old and $outdir_new when you're done" 52 | else 53 | rm -rf "$outdir_old" "$outdir_new" 54 | fi 55 | } 56 | 57 | trap cleanup 0 1 2 3 15 58 | 59 | 60 | generate() { 61 | outfile="$1" 62 | shift 63 | $PYCRC "$@" -o "${outfile}" 64 | 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}" 65 | } 66 | 67 | populate() { 68 | outdir=$1 69 | mkdir -p "$outdir" 70 | models=`PYTHONPATH=.. python3 -c 'import src.pycrc.models as m; print(" ".join(m.CrcModels().names()))'` 71 | for model in "undefined" $models; do 72 | for algo in "bbb" "bbf" "tbl"; do 73 | for cstd in c98 c99; do 74 | if [ "$model" = "undefined" ]; then 75 | mod_opt= 76 | else 77 | mod_opt="--model=${model}" 78 | fi 79 | generate "${outdir}/${model}_${algo}_${cstd}.h" --generate=h --algorithm=${algo} $mod_opt 80 | generate "${outdir}/${model}_${algo}_${cstd}.c" --generate=c --algorithm=${algo} $mod_opt 81 | done 82 | done 83 | done 84 | 85 | algo=tbl 86 | for model in crc-32; do 87 | for slice in 4 8 16; do 88 | for cstd in c98 c99; do 89 | generate "${outdir}/${model}_${algo}_sb${slice}_${cstd}.h" --generate=h --algorithm=${algo} --model=${model} --slice-by ${slice} 90 | generate "${outdir}/${model}_${algo}_sb${slice}_${cstd}.c" --generate=c --algorithm=${algo} --model=${model} --slice-by ${slice} 91 | done 92 | done 93 | done 94 | } 95 | 96 | do_check() { 97 | tar xzf "$tarfile" -C "`dirname $outdir_new`" 98 | populate "$outdir_new" 99 | diff -ru "$outdir_old" "$outdir_new" 100 | } 101 | 102 | 103 | if [ "$opt_check" = "on" ]; then 104 | if [ ! -f "$tarfile" ]; then 105 | echo >&2 "Can't find tarfile $tarfile" 106 | exit 1 107 | fi 108 | do_check 109 | fi 110 | 111 | if [ "$opt_generate" = "on" ]; then 112 | populate "$outdir_old" 113 | dirname="`dirname $outdir_old`" 114 | basename="`basename $outdir_old`" 115 | tar czf "$tarfile" -C "$dirname" "$basename" 116 | fi 117 | -------------------------------------------------------------------------------- /test/performance.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | PYCRC=`dirname $0`/../src/pycrc.py 5 | tmpdir= 6 | 7 | cleanup() { 8 | if [ -d "$tmpdir" ]; then 9 | rm -rf "$tmpdir" 10 | fi 11 | } 12 | 13 | trap cleanup 0 1 2 3 15 14 | tmpdir=`mktemp -t -d pycrc-perf-XXXXXXXX` 15 | 16 | model=crc-32 17 | 18 | prefix=bbb 19 | $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate c -o $tmpdir/crc_$prefix.c --algo bit-by-bit 20 | $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate h -o $tmpdir/crc_$prefix.h --algo bit-by-bit 21 | prefix=bbf 22 | $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate h -o $tmpdir/crc_$prefix.h --algo bit-by-bit-fast 23 | $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate c -o $tmpdir/crc_$prefix.c --algo bit-by-bit-fast 24 | prefix=tbl 25 | $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate h -o $tmpdir/crc_$prefix.h --algo table-driven 26 | $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate c -o $tmpdir/crc_$prefix.c --algo table-driven 27 | prefix=tb4 28 | $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate h -o $tmpdir/crc_$prefix.h --algo table-driven --table-idx-width 4 29 | $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate c -o $tmpdir/crc_$prefix.c --algo table-driven --table-idx-width 4 30 | prefix=sb4 31 | $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate h -o $tmpdir/crc_$prefix.h --algo table-driven --slice-by 4 32 | $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate c -o $tmpdir/crc_$prefix.c --algo table-driven --slice-by 4 33 | prefix=sb16 34 | $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate h -o $tmpdir/crc_$prefix.h --algo table-driven --slice-by 16 35 | $PYCRC --model $model --symbol-prefix crc_${prefix}_ --generate c -o $tmpdir/crc_$prefix.c --algo table-driven --slice-by 16 36 | 37 | 38 | print_main() { 39 | cat < 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | 53 | #define NUM_RUNS (128*256*256) 54 | 55 | unsigned char buf[1024]; 56 | 57 | void test_bbb(unsigned char *buf, size_t buf_len, size_t num_runs, clock_t clock_per_sec); 58 | void test_bbf(unsigned char *buf, size_t buf_len, size_t num_runs, clock_t clock_per_sec); 59 | void test_tbl(unsigned char *buf, size_t buf_len, size_t num_runs, clock_t clock_per_sec); 60 | void test_tb4(unsigned char *buf, size_t buf_len, size_t num_runs, clock_t clock_per_sec); 61 | void test_sb4(unsigned char *buf, size_t buf_len, size_t num_runs, clock_t clock_per_sec); 62 | void test_sb16(unsigned char *buf, size_t buf_len, size_t num_runs, clock_t clock_per_sec); 63 | 64 | /** 65 | * Print results. 66 | * 67 | * \param dsc Description of the test 68 | * \param buflen Length of one buffer 69 | * \param num_runs Number of runs over that buffer 70 | * \param t_user user time 71 | * \param t_sys system time 72 | * \return void 73 | *****************************************************************************/ 74 | void show_times(const char *dsc, size_t buflen, size_t num_runs, double t_user) 75 | { 76 | double mbps = (((double)buflen) * num_runs)/(1024*1024*t_user); 77 | printf("%s of %ld bytes (%ld * %ld)\n", dsc, (long)buflen*num_runs, (long)buflen, (long)num_runs); 78 | printf("%13s: %7.3f s %13s: %7.3f MiB/s\n", "user time", t_user, "throughput", mbps); 79 | printf("\n"); 80 | } 81 | 82 | 83 | /** 84 | * C main function. 85 | * 86 | * \retval 0 on success 87 | * \retval 1 on failure 88 | *****************************************************************************/ 89 | int main(void) 90 | { 91 | unsigned int i; 92 | long int clock_per_sec; 93 | 94 | for (i = 0; i < sizeof(buf); i++) { 95 | buf[i] = (unsigned char)rand(); 96 | } 97 | clock_per_sec = sysconf(_SC_CLK_TCK); 98 | 99 | // bit-by-bit 100 | test_bbb(buf, sizeof(buf), NUM_RUNS / 8, clock_per_sec); 101 | 102 | // bit-by-bit-fast 103 | test_bbf(buf, sizeof(buf), NUM_RUNS / 8, clock_per_sec); 104 | 105 | // table-driven 106 | test_tbl(buf, sizeof(buf), NUM_RUNS, clock_per_sec); 107 | 108 | // table-driven idx4 109 | test_tb4(buf, sizeof(buf), NUM_RUNS / 2, clock_per_sec); 110 | 111 | // table-driven slice-by 4 112 | test_sb4(buf, sizeof(buf), NUM_RUNS, clock_per_sec); 113 | 114 | // table-driven slice-by 16 115 | test_sb16(buf, sizeof(buf), NUM_RUNS, clock_per_sec); 116 | 117 | return 0; 118 | } 119 | EOF 120 | } 121 | 122 | print_routine() { 123 | algo=$1 124 | prefix=$2 125 | cat < $tmpdir/performance.c 152 | print_routine "bit-by-bit" bbb >> $tmpdir/performance.c 153 | print_routine "bit-by-bit-fast" bbf >> $tmpdir/performance.c 154 | print_routine "table-driven" tbl >> $tmpdir/performance.c 155 | print_routine "table-driven idx4" tb4 >> $tmpdir/performance.c 156 | print_routine "table-driven sb4" sb4 >> $tmpdir/performance.c 157 | print_routine "table-driven sb16" sb16 >> $tmpdir/performance.c 158 | 159 | 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 160 | $tmpdir/perf 161 | -------------------------------------------------------------------------------- /test/test_codegen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import logging 4 | import os 5 | import tempfile 6 | import subprocess 7 | import itertools 8 | import pytest 9 | from src.pycrc.models import CrcModels 10 | from src.pycrc.algorithms import Crc 11 | 12 | LOGGER = logging.getLogger(__name__) 13 | 14 | use_algo_bit_by_bit = True 15 | use_algo_bit_by_bit_fast = True 16 | use_algo_table_driven = True 17 | use_algo_table_slice = True 18 | use_c89 = True 19 | 20 | 21 | class TestCodeGen: 22 | @pytest.mark.skipif(not use_algo_bit_by_bit, reason='bbb tests disabled') 23 | def test_models_bbb_c99(self): 24 | compile_and_test_models('bbb', 'c99') 25 | 26 | @pytest.mark.skipif(not use_c89, reason='c89 tests disabled') 27 | @pytest.mark.skipif(not use_algo_bit_by_bit, reason='bbb tests disabled') 28 | def test_models_bbb_c89(self): 29 | compile_and_test_models('bbb', 'c89') 30 | 31 | @pytest.mark.skipif(not use_algo_bit_by_bit_fast, reason='bbf tests disabled') 32 | def test_models_bbf_c99(self): 33 | compile_and_test_models('bbf', 'c99') 34 | 35 | @pytest.mark.skipif(not use_c89, reason='c89 tests disabled') 36 | @pytest.mark.skipif(not use_algo_bit_by_bit_fast, reason='bbf tests disabled') 37 | def test_models_bbf_c89(self): 38 | compile_and_test_models('bbf', 'c89') 39 | 40 | @pytest.mark.skipif(not use_algo_table_driven, reason='tbl tests disabled') 41 | def test_models_tbl_c99(self): 42 | compile_and_test_models('tbl', 'c99') 43 | 44 | @pytest.mark.skipif(not use_c89, reason='c89 tests disabled') 45 | @pytest.mark.skipif(not use_algo_table_driven, reason='tbl tests disabled') 46 | def test_models_tbl_c89(self): 47 | compile_and_test_models('tbl', 'c89') 48 | 49 | @pytest.mark.skipif(not use_algo_table_driven, reason='tbl tests disabled') 50 | @pytest.mark.skipif(not use_algo_table_slice, reason='tbl slice tests disabled') 51 | def test_models_tbl_sb_c99(self): 52 | compile_and_test_models('tbl', 'c99', ['--slice-by', '4']) 53 | compile_and_test_models('tbl', 'c99', ['--slice-by', '8']) 54 | compile_and_test_models('tbl', 'c99', ['--slice-by', '16']) 55 | 56 | # --slice-by not supported with C89 57 | 58 | @pytest.mark.skipif(not use_algo_table_driven, reason="tbl tests disabled") 59 | def test_incomplete_models_tbl_c99(self): 60 | params = ['width', 'poly', 'xor_in', 'reflect_in', 'xor_out', 'reflect_out'] 61 | for n in range(len(params)): 62 | for c in itertools.combinations(params, n): 63 | compile_and_test_incomplete_models('tbl', 'c99', c) 64 | 65 | def test_special_cases(self): 66 | compile_and_run_special_cases() 67 | 68 | def test_variable_width(self): 69 | compile_and_run_variable_width('bbb', 'c99') 70 | compile_and_run_variable_width('bbf', 'c99') 71 | compile_and_run_variable_width('tbl', 'c99') 72 | 73 | 74 | def run_cmd(cmd): 75 | LOGGER.info(' '.join(cmd)) 76 | ret = subprocess.run(cmd, check=True, capture_output=True) 77 | return ret 78 | 79 | 80 | def run_pycrc(args): 81 | ret = run_cmd(['python3', 'src/pycrc.py'] + args) 82 | return ret.stdout.decode('utf-8').rstrip() 83 | 84 | 85 | def gen_src(tmpdir, args, name): 86 | src_h = os.path.join(tmpdir, f'{name}.h') 87 | gen = ['--generate', 'h', '-o', src_h] 88 | run_pycrc(gen + args) 89 | 90 | src_c = os.path.join(tmpdir, f'{name}.c') 91 | gen = ['--generate', 'c-main', '-o', src_c] 92 | run_pycrc(gen + args) 93 | 94 | 95 | def compile_and_run(tmpdir, compile_args, run_args, name, check): 96 | gen_src(tmpdir, compile_args, name) 97 | binary = os.path.join(tmpdir, 'a.out') 98 | compile_src(binary, os.path.join(tmpdir, name + '.c')) 99 | run_and_check_res([binary] + run_args, check) 100 | 101 | 102 | def compile_and_test_models(algo, cstd, opt_args=[]): 103 | with tempfile.TemporaryDirectory(prefix='pycrc-test.') as tmpdir: 104 | for m in CrcModels().models: 105 | # Don't test width > 32 for C89, as I don't know how to ask for an data type > 32 bits. 106 | if cstd == 'c89' and m['width'] > 32: 107 | continue 108 | args = args_from_model(m) 109 | args += ['--algorithm', algo, '--std', cstd] 110 | args += opt_args 111 | compile_and_run(tmpdir, args, [], m['name'], m['check']) 112 | 113 | 114 | def compile_and_test_incomplete_models(algo, cstd, erase_params=[]): 115 | if cstd == 'c89': 116 | pytest.skip('C89 not supported') 117 | model = CrcModels().get_params('crc-32') 118 | with tempfile.TemporaryDirectory(prefix='pycrc-test.') as tmpdir: 119 | for algo in ['bbb', 'bbf', 'tbl']: 120 | m = dict(model) 121 | for param in erase_params: 122 | del m[param] 123 | args = args_from_model(m) 124 | args += ['--algorithm', algo, '--std', cstd] 125 | run_args = args_from_model({param: model[param] for param in erase_params}) 126 | compile_and_run(tmpdir, args, run_args, f'{m["name"]}_incomplete', m['check']) 127 | 128 | 129 | def compile_and_run_special_cases(): 130 | with tempfile.TemporaryDirectory(prefix='pycrc-test.') as tmpdir: 131 | crc_5_args = ['--model=crc-5', '--reflect-in=0', '--algorithm', 'table-driven'] 132 | compile_and_run(tmpdir, crc_5_args + ['--table-idx-width=8'], [], 'special', 0x01) 133 | compile_and_run(tmpdir, crc_5_args + ['--table-idx-width=4'], [], 'special', 0x01) 134 | compile_and_run(tmpdir, crc_5_args + ['--table-idx-width=2'], [], 'special', 0x01) 135 | 136 | 137 | def compile_and_run_variable_width(algo, cstd): 138 | check_str = "123456789" 139 | models = CrcModels() 140 | m = models.get_params('crc-64-jones') 141 | with tempfile.TemporaryDirectory(prefix='pycrc-test.') as tmpdir: 142 | 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]: 143 | mask = (1 << width) - 1 144 | mw = { 145 | 'width': width, 146 | 'poly': m['poly'] & mask, 147 | 'reflect_in': m['reflect_in'], 148 | 'xor_in': m['xor_in'] & mask, 149 | 'reflect_out': m['reflect_out'], 150 | 'xor_out': m['xor_out'] & mask, 151 | } 152 | args = [ 153 | '--width', '{:d}'.format(mw['width']), 154 | '--poly', '{:#x}'.format(mw['poly']), 155 | '--xor-in', '{:#x}'.format(mw['xor_in']), 156 | '--reflect-in', '{:d}'.format(mw['reflect_in']), 157 | '--xor-out', '{:#x}'.format(mw['xor_out']), 158 | '--reflect-out', '{:d}'.format(mw['reflect_out']), 159 | ] 160 | reference = Crc(width=mw['width'], poly=mw['poly'], 161 | reflect_in=mw['reflect_in'], xor_in=mw['xor_in'], 162 | reflect_out=mw['reflect_out'], xor_out=mw['xor_out']) 163 | check = reference.bit_by_bit_fast(check_str) 164 | compile_and_run(tmpdir, ['--algorithm', algo, '--std', cstd] + args, [], 'var_width', check) 165 | 166 | 167 | def run_and_check_res(cmd, expected_crc): 168 | res = run_cmd(cmd).stdout.decode('utf-8').rstrip() 169 | assert res[:2] == '0x' 170 | assert int(res, 16) == expected_crc 171 | 172 | 173 | def compile_src(out_file, src_file, cstd='c99'): 174 | run_cmd(['cc', '-W', '-Wall', '-pedantic', '-Werror', f'-std={cstd}', '-o', out_file, src_file]) 175 | 176 | 177 | def args_from_model(m): 178 | args = [] 179 | if 'width' in m: 180 | args += ['--width', f'{m["width"]:d}'] 181 | if 'poly' in m: 182 | args += ['--poly', f'{m["poly"]:#x}'] 183 | if 'xor_in' in m: 184 | args += ['--xor-in', f'{m["xor_in"]:#x}'] 185 | if 'reflect_in' in m: 186 | args += ['--reflect-in', f'{m["reflect_in"]}'] 187 | if 'xor_out' in m: 188 | args += ['--xor-out', f'{m["xor_out"]:#x}'] 189 | if 'reflect_out' in m: 190 | args += ['--reflect-out', f'{m["reflect_out"]}'] 191 | return args 192 | -------------------------------------------------------------------------------- /src/pycrc/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # pycrc -- parameterisable CRC calculation utility and C source code generator 4 | # 5 | # Copyright (c) 2006-2017 Thomas Pircher 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to 9 | # deal in the Software without restriction, including without limitation the 10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 11 | # sell copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23 | # IN THE SOFTWARE. 24 | 25 | 26 | """ 27 | pycrc is a fully parameterisable Cyclic Redundancy Check (CRC) calculation 28 | utility and C source code generator written in Python. 29 | 30 | It can: 31 | - generate the checksum of a string 32 | - generate the checksum of a file 33 | - generate the C header file and source of any of the algorithms below 34 | 35 | It supports the following CRC algorithms: 36 | - bit-by-bit the basic algorithm which operates bit by bit on the 37 | augmented message 38 | - bit-by-bit-fast a variation of the simple bit-by-bit algorithm 39 | - table-driven the standard table driven algorithm 40 | """ 41 | 42 | from __future__ import print_function 43 | from pycrc import __version__ 44 | from pycrc.opt import Options 45 | from pycrc.algorithms import Crc 46 | import pycrc.codegen as cg 47 | import binascii 48 | import sys 49 | 50 | progname = "pycrc" 51 | url = 'https://pycrc.org' 52 | 53 | 54 | def print_parameters(opt): 55 | """ 56 | Generate a string with the options pretty-printed (used in the --verbose mode). 57 | """ 58 | return str(cg.ParamBlock(opt, '')) 59 | 60 | 61 | def check_string(opt): 62 | """ 63 | Return the calculated CRC sum of a string. 64 | """ 65 | error = False 66 | if opt.undefined_crc_parameters: 67 | sys.stderr.write(f"{progname:s}: error: undefined parameters\n") 68 | sys.exit(1) 69 | if opt.algorithm == 0: 70 | opt.algorithm = opt.algo_bit_by_bit | opt.algo_bit_by_bit_fast | opt.algo_table_driven 71 | 72 | alg = Crc( 73 | width=opt.width, poly=opt.poly, 74 | reflect_in=opt.reflect_in, xor_in=opt.xor_in, 75 | reflect_out=opt.reflect_out, xor_out=opt.xor_out, 76 | table_idx_width=opt.tbl_idx_width) 77 | 78 | crc = None 79 | if opt.algorithm & opt.algo_bit_by_bit: 80 | bbb_crc = alg.bit_by_bit(opt.check_string) 81 | error |= crc is not None and bbb_crc != crc 82 | crc = bbb_crc 83 | if opt.algorithm & opt.algo_bit_by_bit_fast: 84 | bbf_crc = alg.bit_by_bit_fast(opt.check_string) 85 | error |= crc is not None and bbf_crc != crc 86 | crc = bbf_crc 87 | if opt.algorithm & opt.algo_table_driven: 88 | # no point making the python implementation slower by using less than 8 bits as index. 89 | opt.tbl_idx_width = 8 90 | tbl_crc = alg.table_driven(opt.check_string) 91 | error |= crc is not None and tbl_crc != crc 92 | crc = tbl_crc 93 | 94 | if error: 95 | sys.stderr.write(f"{progname:s}: error: different checksums!\n") 96 | if opt.algorithm & opt.algo_bit_by_bit: 97 | sys.stderr.write(f" bit-by-bit: {bbb_crc:#x}\n") 98 | if opt.algorithm & opt.algo_bit_by_bit_fast: 99 | sys.stderr.write(f" bit-by-bit-fast: {bbf_crc:#x}\n") 100 | if opt.algorithm & opt.algo_table_driven: 101 | sys.stderr.write(f" table_driven: {tbl_crc:#x}\n") 102 | sys.exit(1) 103 | return crc 104 | 105 | 106 | def check_hexstring(opt): 107 | """ 108 | Return the calculated CRC sum of a hex string. 109 | """ 110 | if opt.undefined_crc_parameters: 111 | sys.stderr.write("{0:s}: error: undefined parameters\n".format(progname)) 112 | sys.exit(1) 113 | if len(opt.check_string) % 2 != 0: 114 | opt.check_string = "0" + opt.check_string 115 | opt.check_string = bytes(opt.check_string, 'utf_8') 116 | try: 117 | check_str = bytearray(binascii.unhexlify(opt.check_string)) 118 | except TypeError: 119 | sys.stderr.write( 120 | "{0:s}: error: invalid hex string {1:s}\n".format(progname, opt.check_string)) 121 | sys.exit(1) 122 | 123 | opt.check_string = check_str 124 | return check_string(opt) 125 | 126 | 127 | def crc_file_update(alg, register, check_bytes): 128 | """ 129 | Update the CRC using the bit-by-bit-fast CRC algorithm. 130 | """ 131 | # If the input data is a string, convert to bytes. 132 | if isinstance(check_bytes, str): 133 | check_bytes = bytearray(check_bytes, 'utf_8') 134 | 135 | for octet in check_bytes: 136 | if alg.reflect_in: 137 | octet = alg.reflect(octet, 8) 138 | for j in range(8): 139 | bit = register & alg.msb_mask 140 | register <<= 1 141 | if octet & (0x80 >> j): 142 | bit ^= alg.msb_mask 143 | if bit: 144 | register ^= alg.poly 145 | register &= alg.mask 146 | return register 147 | 148 | 149 | def check_file(opt): 150 | """ 151 | Calculate the CRC of a file. 152 | This algorithm uses the table_driven CRC algorithm. 153 | """ 154 | if opt.undefined_crc_parameters: 155 | sys.stderr.write("{0:s}: error: undefined parameters\n".format(progname)) 156 | sys.exit(1) 157 | alg = Crc( 158 | width=opt.width, poly=opt.poly, 159 | reflect_in=opt.reflect_in, xor_in=opt.xor_in, 160 | reflect_out=opt.reflect_out, xor_out=opt.xor_out, 161 | table_idx_width=opt.tbl_idx_width) 162 | 163 | # Always use the xor_in value unreflected 164 | # As in the rocksoft reference implementation 165 | register = opt.xor_in 166 | 167 | try: 168 | with open(opt.check_file, 'rb') as f: 169 | check_bytes = bytearray(f.read(4096)) 170 | while check_bytes != b"": 171 | register = crc_file_update(alg, register, check_bytes) 172 | check_bytes = bytearray(f.read(4096)) 173 | except IOError: 174 | sys.stderr.write( 175 | "{0:s}: error: can't open file {1:s}\n".format(progname, opt.check_file)) 176 | sys.exit(1) 177 | 178 | if opt.reflect_out: 179 | register = alg.reflect(register, opt.width) 180 | register = register ^ opt.xor_out 181 | return register 182 | 183 | 184 | def write_file(filename, out_str): 185 | """ 186 | Write the content of out_str to filename. 187 | """ 188 | try: 189 | out_file = open(filename, "w") 190 | out_file.write(out_str) 191 | out_file.close() 192 | except IOError: 193 | sys.stderr.write("{0:s}: error: cannot write to file {1:s}\n".format(progname, filename)) 194 | sys.exit(1) 195 | 196 | 197 | def main(): 198 | """ 199 | Main function. 200 | """ 201 | opt = Options(progname, __version__, url) 202 | opt.parse(sys.argv[1:]) 203 | if opt.verbose: 204 | print(print_parameters(opt)) 205 | if opt.action == opt.action_check_str: 206 | crc = check_string(opt) 207 | print("{0:#x}".format(crc)) 208 | if opt.action == opt.action_check_hex_str: 209 | crc = check_hexstring(opt) 210 | print("{0:#x}".format(crc)) 211 | if opt.action == opt.action_check_file: 212 | crc = check_file(opt) 213 | print("{0:#x}".format(crc)) 214 | if opt.action in set([ 215 | opt.action_generate_h, opt.action_generate_c, opt.action_generate_c_main, 216 | opt.action_generate_table]): 217 | out = str(cg.File(opt, '')) 218 | if opt.output_file is None: 219 | print(out) 220 | else: 221 | write_file(opt.output_file, out) 222 | return 0 223 | 224 | 225 | if __name__ == "__main__": 226 | sys.exit(main()) 227 | -------------------------------------------------------------------------------- /src/pycrc/algorithms.py: -------------------------------------------------------------------------------- 1 | # pycrc -- parameterisable CRC calculation utility and C source code generator 2 | # 3 | # Copyright (c) 2006-2017 Thomas Pircher 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to 7 | # deal in the Software without restriction, including without limitation the 8 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | # sell copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | # IN THE SOFTWARE. 22 | 23 | 24 | """ 25 | CRC algorithms implemented in Python. 26 | If you want to study the Python implementation of the CRC routines, then this 27 | is a good place to start from. 28 | 29 | The algorithms Bit by Bit, Bit by Bit Fast and Table-Driven are implemented. 30 | 31 | This module can also be used as a library from within Python. 32 | 33 | Examples 34 | ======== 35 | 36 | This is an example use of the different algorithms: 37 | 38 | from pycrc.algorithms import Crc 39 | 40 | crc = Crc(width = 16, poly = 0x8005, 41 | reflect_in = True, xor_in = 0x0000, 42 | reflect_out = True, xor_out = 0x0000) 43 | print("{0:#x}".format(crc.bit_by_bit("123456789"))) 44 | print("{0:#x}".format(crc.bit_by_bit_fast("123456789"))) 45 | print("{0:#x}".format(crc.table_driven("123456789"))) 46 | """ 47 | 48 | 49 | class Crc(): 50 | """ 51 | A base class for CRC routines. 52 | """ 53 | # pylint: disable=too-many-instance-attributes 54 | 55 | def __init__(self, width, poly, reflect_in, xor_in, reflect_out, xor_out, 56 | table_idx_width=None, slice_by=1): 57 | """ 58 | Create a CRC object, using the Rocksoft model. 59 | 60 | The parameters are as follows: 61 | width 62 | poly 63 | reflect_in 64 | xor_in 65 | reflect_out 66 | xor_out 67 | """ 68 | # pylint: disable=too-many-arguments 69 | 70 | self.width = width 71 | self.poly = poly 72 | self.reflect_in = reflect_in 73 | self.xor_in = xor_in 74 | self.reflect_out = reflect_out 75 | self.xor_out = xor_out 76 | self.tbl_idx_width = table_idx_width 77 | self.slice_by = slice_by 78 | 79 | self.msb_mask = 0x1 << (self.width - 1) 80 | self.mask = ((self.msb_mask - 1) << 1) | 1 81 | if self.tbl_idx_width is not None: 82 | self.tbl_width = 1 << self.tbl_idx_width 83 | else: 84 | self.tbl_idx_width = 8 85 | self.tbl_width = 1 << self.tbl_idx_width 86 | 87 | self.direct_init = self.xor_in 88 | self.nondirect_init = self.__get_nondirect_init(self.xor_in) 89 | if self.width < 8: 90 | self.crc_shift = 8 - self.width 91 | else: 92 | self.crc_shift = 0 93 | 94 | self.tbl = self.gen_table() 95 | 96 | def __get_nondirect_init(self, init): 97 | """ 98 | return the non-direct init if the direct algorithm has been selected. 99 | """ 100 | crc = init 101 | for dummy_i in range(self.width): 102 | bit = crc & 0x01 103 | if bit: 104 | crc ^= self.poly 105 | crc >>= 1 106 | if bit: 107 | crc |= self.msb_mask 108 | return crc & self.mask 109 | 110 | def reflect(self, data, width): 111 | """ 112 | reflect a data word, i.e. reverts the bit order. 113 | """ 114 | # pylint: disable=no-self-use 115 | 116 | res = data & 0x01 117 | for dummy_i in range(width - 1): 118 | data >>= 1 119 | res = (res << 1) | (data & 0x01) 120 | return res 121 | 122 | def bit_by_bit(self, in_data): 123 | """ 124 | Classic simple and slow CRC implementation. This function iterates bit 125 | by bit over the augmented input message and returns the calculated CRC 126 | value at the end. 127 | """ 128 | # If the input data is a string, convert to bytes. 129 | if isinstance(in_data, str): 130 | in_data = bytearray(in_data, 'utf-8') 131 | 132 | reg = self.nondirect_init 133 | for octet in in_data: 134 | if self.reflect_in: 135 | octet = self.reflect(octet, 8) 136 | for i in range(8): 137 | topbit = reg & self.msb_mask 138 | reg = ((reg << 1) & self.mask) | ((octet >> (7 - i)) & 0x01) 139 | if topbit: 140 | reg ^= self.poly 141 | 142 | for i in range(self.width): 143 | topbit = reg & self.msb_mask 144 | reg = ((reg << 1) & self.mask) 145 | if topbit: 146 | reg ^= self.poly 147 | 148 | if self.reflect_out: 149 | reg = self.reflect(reg, self.width) 150 | return (reg ^ self.xor_out) & self.mask 151 | 152 | def bit_by_bit_fast(self, in_data): 153 | """ 154 | This is a slightly modified version of the bit-by-bit algorithm: it 155 | does not need to loop over the augmented bits, i.e. the Width 0-bits 156 | wich are appended to the input message in the bit-by-bit algorithm. 157 | """ 158 | # If the input data is a string, convert to bytes. 159 | if isinstance(in_data, str): 160 | in_data = bytearray(in_data, 'utf-8') 161 | 162 | reg = self.direct_init 163 | for octet in in_data: 164 | if self.reflect_in: 165 | octet = self.reflect(octet, 8) 166 | for i in range(8): 167 | topbit = reg & self.msb_mask 168 | if octet & (0x80 >> i): 169 | topbit ^= self.msb_mask 170 | reg <<= 1 171 | if topbit: 172 | reg ^= self.poly 173 | reg &= self.mask 174 | if self.reflect_out: 175 | reg = self.reflect(reg, self.width) 176 | return reg ^ self.xor_out 177 | 178 | def gen_table(self): 179 | """ 180 | This function generates the CRC table used for the table_driven CRC 181 | algorithm. The Python version cannot handle tables of an index width 182 | other than 8. See the generated C code for tables with different sizes 183 | instead. 184 | """ 185 | table_length = 1 << self.tbl_idx_width 186 | tbl = [[0 for i in range(table_length)] for j in range(self.slice_by)] 187 | for i in range(table_length): 188 | reg = i 189 | if self.reflect_in: 190 | reg = self.reflect(reg, self.tbl_idx_width) 191 | reg = reg << (self.width - self.tbl_idx_width + self.crc_shift) 192 | for dummy_j in range(self.tbl_idx_width): 193 | if reg & (self.msb_mask << self.crc_shift) != 0: 194 | reg = (reg << 1) ^ (self.poly << self.crc_shift) 195 | else: 196 | reg = (reg << 1) 197 | if self.reflect_in: 198 | reg = self.reflect(reg >> self.crc_shift, self.width) << self.crc_shift 199 | tbl[0][i] = (reg >> self.crc_shift) & self.mask 200 | 201 | for j in range(1, self.slice_by): 202 | for i in range(table_length): 203 | tbl[j][i] = (tbl[j - 1][i] >> 8) ^ tbl[0][tbl[j - 1][i] & 0xff] 204 | return tbl 205 | 206 | def table_driven(self, in_data): 207 | """ 208 | The Standard table_driven CRC algorithm. 209 | """ 210 | # pylint: disable = line-too-long 211 | 212 | # If the input data is a string, convert to bytes. 213 | if isinstance(in_data, str): 214 | in_data = bytearray(in_data, 'utf-8') 215 | 216 | if not self.reflect_in: 217 | reg = self.direct_init << self.crc_shift 218 | for octet in in_data: 219 | tblidx = ((reg >> (self.width - self.tbl_idx_width + self.crc_shift)) ^ octet) & 0xff 220 | reg = ((reg << (self.tbl_idx_width - self.crc_shift)) ^ 221 | (self.tbl[0][tblidx] << self.crc_shift)) & (self.mask << self.crc_shift) 222 | reg = reg >> self.crc_shift 223 | else: 224 | reg = self.reflect(self.direct_init, self.width) 225 | for octet in in_data: 226 | tblidx = (reg ^ octet) & 0xff 227 | reg = ((reg >> self.tbl_idx_width) ^ self.tbl[0][tblidx]) & self.mask 228 | reg = self.reflect(reg, self.width) & self.mask 229 | 230 | if self.reflect_out: 231 | reg = self.reflect(reg, self.width) 232 | return reg ^ self.xor_out 233 | -------------------------------------------------------------------------------- /test/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2006-2013 Thomas Pircher 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #include "crc.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | static bool atob(const char *str); 30 | static crc_t xtoi(const char *str); 31 | static int get_config(int argc, char *argv[], crc_cfg_t *cfg); 32 | #if CRC_ALGO_BIT_BY_BIT 33 | static crc_t crc_verify(const crc_cfg_t *cfg, crc_t crc_pre_final, crc_t crc); 34 | static crc_t crc_reflect(crc_t data, size_t data_len); 35 | #endif 36 | 37 | 38 | 39 | static bool verbose = false; 40 | static unsigned char str[256] = "123456789"; 41 | 42 | bool atob(const char *str) 43 | { 44 | if (!str) { 45 | return 0; 46 | } 47 | if (isdigit(str[0])) { 48 | return (bool)atoi(str); 49 | } 50 | if (tolower(str[0]) == 't') { 51 | return true; 52 | } 53 | return false; 54 | } 55 | 56 | crc_t xtoi(const char *str) 57 | { 58 | crc_t ret = 0; 59 | 60 | if (!str) { 61 | return 0; 62 | } 63 | if (str[0] == '0' && tolower(str[1]) == 'x') { 64 | str += 2; 65 | while (*str) { 66 | if (isdigit(*str)) 67 | ret = 16 * ret + *str - '0'; 68 | else if (isxdigit(*str)) 69 | ret = 16 * ret + tolower(*str) - 'a' + 10; 70 | else 71 | return ret; 72 | str++; 73 | } 74 | } else if (isdigit(*str)) { 75 | while (*str) { 76 | if (isdigit(*str)) 77 | ret = 10 * ret + *str - '0'; 78 | else 79 | return ret; 80 | str++; 81 | } 82 | } 83 | return ret; 84 | } 85 | 86 | 87 | int get_config(int argc, char *argv[], crc_cfg_t *cfg) 88 | { 89 | int c; 90 | int option_index; 91 | static struct option long_options[] = { 92 | {"width", 1, 0, 'w'}, 93 | {"poly", 1, 0, 'p'}, 94 | {"reflect-in", 1, 0, 'n'}, 95 | {"xor-in", 1, 0, 'i'}, 96 | {"reflect-out", 1, 0, 'u'}, 97 | {"xor-out", 1, 0, 'o'}, 98 | {"verbose", 0, 0, 'v'}, 99 | {"check-string", 1, 0, 's'}, 100 | {"table-idx-with", 1, 0, 't'}, 101 | {0, 0, 0, 0} 102 | }; 103 | 104 | while (1) { 105 | option_index = 0; 106 | 107 | c = getopt_long(argc, argv, "w:p:ni:uo:v", long_options, &option_index); 108 | if (c == -1) 109 | break; 110 | 111 | switch (c) { 112 | case 0: 113 | printf("option %s", long_options[option_index].name); 114 | if (optarg) 115 | printf(" with arg %s", optarg); 116 | printf ("\n"); 117 | case 'w': 118 | cfg->width = atoi(optarg); 119 | break; 120 | case 'p': 121 | cfg->poly = xtoi(optarg); 122 | break; 123 | case 'n': 124 | cfg->reflect_in = atob(optarg); 125 | break; 126 | case 'i': 127 | cfg->xor_in = xtoi(optarg); 128 | break; 129 | case 'u': 130 | cfg->reflect_out = atob(optarg); 131 | break; 132 | case 'o': 133 | cfg->xor_out = xtoi(optarg); 134 | break; 135 | case 's': 136 | memcpy(str, optarg, strlen(optarg) < sizeof(str) ? strlen(optarg) + 1 : sizeof(str)); 137 | str[sizeof(str) - 1] = '\0'; 138 | break; 139 | case 'v': 140 | verbose = true; 141 | break; 142 | case 't': 143 | // ignore --table_idx_width option 144 | break; 145 | case '?': 146 | return -1; 147 | case ':': 148 | fprintf(stderr, "missing argument to option %c\n", c); 149 | return -1; 150 | default: 151 | fprintf(stderr, "unhandled option %c\n", c); 152 | return -1; 153 | } 154 | } 155 | cfg->msb_mask = (crc_t)1u << (cfg->width - 1); 156 | cfg->crc_mask = (cfg->msb_mask - 1) | cfg->msb_mask; 157 | cfg->crc_shift = cfg->width < 8 ? 8 - cfg->width : 0; 158 | 159 | cfg->poly &= cfg->crc_mask; 160 | cfg->xor_in &= cfg->crc_mask; 161 | cfg->xor_out &= cfg->crc_mask; 162 | return 0; 163 | } 164 | 165 | 166 | #if CRC_ALGO_BIT_BY_BIT 167 | crc_t crc_verify(const crc_cfg_t *cfg, crc_t crc_pre_final, crc_t crc) 168 | { 169 | crc_t result; 170 | unsigned char data; 171 | unsigned int i; 172 | 173 | // don't verify if the width is not a multiple of 8 174 | if (cfg->width % 8) { 175 | return 0; 176 | } 177 | if (cfg->xor_out) { 178 | crc ^= cfg->xor_out; 179 | } 180 | if (cfg->reflect_out) { 181 | crc = crc_reflect(crc, cfg->width); 182 | } 183 | result = crc_pre_final; 184 | for (i = 0; i < cfg->width / 8; i++) { 185 | data = (crc >> (cfg->width - 8 * i - 8)) & 0xff; 186 | if (cfg->reflect_in) { 187 | data = crc_reflect(data, 8); 188 | } 189 | result = crc_update(cfg, result, &data, 1); 190 | } 191 | // no crc_finalize, because if the CRC calculation is correct, result == 0. 192 | // A crc_finalize would XOR-in again some ones into the solution. 193 | // In theory the finalize function of the bit-by-bit algorithm 194 | // would also loop over cfg->width zero bits, but since 195 | // a) result == 0, and 196 | // b) input data == 0 197 | // the output would always be zero 198 | return result; 199 | } 200 | 201 | crc_t crc_reflect(crc_t data, size_t data_len) 202 | { 203 | unsigned int i; 204 | crc_t ret; 205 | 206 | ret = 0; 207 | for (i = 0; i < data_len; i++) 208 | { 209 | if (data & 0x01) { 210 | ret = (ret << 1) | 1; 211 | } else { 212 | ret = ret << 1; 213 | } 214 | data >>= 1; 215 | } 216 | return ret; 217 | } 218 | #endif // CRC_ALGO_BIT_BY_BIT 219 | 220 | 221 | int main(int argc, char *argv[]) 222 | { 223 | crc_cfg_t cfg = { 224 | 0, // width 225 | 0, // poly 226 | 0, // xor_in 227 | 0, // reflect_in 228 | 0, // xor_out 229 | 0, // reflect_out 230 | 231 | 0, // crc_mask 232 | 0, // msb_mask 233 | 0, // crc_shift 234 | }; 235 | crc_t crc; 236 | crc_t crc_test, crc_pre_final; 237 | char format[20]; 238 | int ret, i; 239 | 240 | ret = get_config(argc, argv, &cfg); 241 | if (ret == 0) { 242 | # if CRC_ALGO_TABLE_DRIVEN 243 | crc_table_gen(&cfg); 244 | # endif // CRC_ALGO_TABLE_DRIVEN 245 | crc = crc_init(&cfg); 246 | crc = crc_pre_final = crc_update(&cfg, crc, str, strlen((char *)str)); 247 | crc = crc_finalize(&cfg, crc); 248 | 249 | # if CRC_ALGO_BIT_BY_BIT 250 | if (crc_verify(&cfg, crc_pre_final, crc) != 0) { 251 | fprintf(stderr, "error: crc verification failed\n"); 252 | return 1; 253 | } 254 | # endif 255 | 256 | // calculate the checksum again, but this time loop over the input 257 | // bytes one-by-one. 258 | crc_test = crc_init(&cfg); 259 | for (i = 0; str[i]; i++) 260 | { 261 | crc_test = crc_update(&cfg, crc_test, str + i, 1); 262 | } 263 | crc_test = crc_finalize(&cfg, crc_test); 264 | if (crc_test != crc) { 265 | fprintf(stderr, "error: crc loop verification failed\n"); 266 | return 1; 267 | } 268 | 269 | if (verbose) { 270 | snprintf(format, sizeof(format), "%%-16s = 0x%%0%dx\n", (unsigned int)(cfg.width + 3) / 4); 271 | printf("%-16s = %d\n", "width", (unsigned int)cfg.width); 272 | printf(format, "poly", (unsigned int)cfg.poly); 273 | printf("%-16s = %s\n", "reflect_in", cfg.reflect_in ? "true": "false"); 274 | printf(format, "xor_in", cfg.xor_in); 275 | printf("%-16s = %s\n", "reflect_out", cfg.reflect_out ? "true": "false"); 276 | printf(format, "xor_out", (unsigned int)cfg.xor_out); 277 | printf(format, "crc_mask", (unsigned int)cfg.crc_mask); 278 | printf(format, "msb_mask", (unsigned int)cfg.msb_mask); 279 | } 280 | printf("0x%llx\n", (unsigned long long int)crc); 281 | } 282 | return !ret; 283 | } 284 | -------------------------------------------------------------------------------- /src/pycrc/models.py: -------------------------------------------------------------------------------- 1 | # pycrc -- parameterisable CRC calculation utility and C source code generator 2 | # 3 | # Copyright (c) 2006-2017 Thomas Pircher 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to 7 | # deal in the Software without restriction, including without limitation the 8 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | # sell copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | # IN THE SOFTWARE. 22 | 23 | 24 | """ 25 | Collection of CRC models. This module contains the CRC models known to pycrc. 26 | 27 | To print the parameters of a particular model: 28 | 29 | import pycrc.models as cm 30 | 31 | models = cm.CrcModels() 32 | print(", ".join(models.names())) 33 | m = models.get_params("crc-32") 34 | if m != None: 35 | print("Width: {width:d}".format(**m)) 36 | print("Poly: {poly:#x}".format(**m)) 37 | print("ReflectIn: {reflect_in}".format(**m)) 38 | print("XorIn: {xor_in:#x}".format(**m)) 39 | print("ReflectOut: {reflect_out}".format(**m)) 40 | print("XorOut: {xor_out:#x}".format(**m)) 41 | print("Check: {check:#x}".format(**m)) 42 | else: 43 | print("model not found.") 44 | """ 45 | 46 | 47 | class CrcModels(): 48 | """ 49 | CRC Models. 50 | 51 | All models are defined as constant class variables. 52 | """ 53 | 54 | models = [] 55 | 56 | models.append({ 57 | 'name': 'crc-5', 58 | 'width': 5, 59 | 'poly': 0x05, 60 | 'reflect_in': True, 61 | 'xor_in': 0x1f, 62 | 'reflect_out': True, 63 | 'xor_out': 0x1f, 64 | 'check': 0x19, 65 | }) 66 | models.append({ 67 | 'name': 'crc-8', 68 | 'width': 8, 69 | 'poly': 0x07, 70 | 'reflect_in': False, 71 | 'xor_in': 0x0, 72 | 'reflect_out': False, 73 | 'xor_out': 0x0, 74 | 'check': 0xf4, 75 | }) 76 | models.append({ 77 | 'name': 'dallas-1-wire', 78 | 'width': 8, 79 | 'poly': 0x31, 80 | 'reflect_in': True, 81 | 'xor_in': 0x0, 82 | 'reflect_out': True, 83 | 'xor_out': 0x0, 84 | 'check': 0xa1, 85 | }) 86 | models.append({ 87 | 'name': 'crc-12-3gpp', 88 | 'width': 12, 89 | 'poly': 0x80f, 90 | 'reflect_in': False, 91 | 'xor_in': 0x0, 92 | 'reflect_out': True, 93 | 'xor_out': 0x0, 94 | 'check': 0xdaf, 95 | }) 96 | models.append({ 97 | 'name': 'crc-15', 98 | 'width': 15, 99 | 'poly': 0x4599, 100 | 'reflect_in': False, 101 | 'xor_in': 0x0, 102 | 'reflect_out': False, 103 | 'xor_out': 0x0, 104 | 'check': 0x59e, 105 | }) 106 | models.append({ 107 | 'name': 'crc-16', 108 | 'width': 16, 109 | 'poly': 0x8005, 110 | 'reflect_in': True, 111 | 'xor_in': 0x0, 112 | 'reflect_out': True, 113 | 'xor_out': 0x0, 114 | 'check': 0xbb3d, 115 | }) 116 | models.append({ 117 | 'name': 'crc-16-usb', 118 | 'width': 16, 119 | 'poly': 0x8005, 120 | 'reflect_in': True, 121 | 'xor_in': 0xffff, 122 | 'reflect_out': True, 123 | 'xor_out': 0xffff, 124 | 'check': 0xb4c8, 125 | }) 126 | models.append({ 127 | 'name': 'crc-16-modbus', 128 | 'width': 16, 129 | 'poly': 0x8005, 130 | 'reflect_in': True, 131 | 'xor_in': 0xffff, 132 | 'reflect_out': True, 133 | 'xor_out': 0x0, 134 | 'check': 0x4b37, 135 | }) 136 | models.append({ 137 | 'name': 'crc-16-genibus', 138 | 'width': 16, 139 | 'poly': 0x1021, 140 | 'reflect_in': False, 141 | 'xor_in': 0xffff, 142 | 'reflect_out': False, 143 | 'xor_out': 0xffff, 144 | 'check': 0xd64e, 145 | }) 146 | models.append({ 147 | 'name': 'crc-16-ccitt', 148 | 'width': 16, 149 | 'poly': 0x1021, 150 | 'reflect_in': False, 151 | 'xor_in': 0x1d0f, 152 | 'reflect_out': False, 153 | 'xor_out': 0x0, 154 | 'check': 0xe5cc, 155 | }) 156 | models.append({ 157 | 'name': 'r-crc-16', 158 | 'width': 16, 159 | 'poly': 0x0589, 160 | 'reflect_in': False, 161 | 'xor_in': 0x0, 162 | 'reflect_out': False, 163 | 'xor_out': 0x0001, 164 | 'check': 0x007e, 165 | }) 166 | models.append({ 167 | 'name': 'kermit', 168 | 'width': 16, 169 | 'poly': 0x1021, 170 | 'reflect_in': True, 171 | 'xor_in': 0x0, 172 | 'reflect_out': True, 173 | 'xor_out': 0x0, 174 | 'check': 0x2189, 175 | }) 176 | models.append({ 177 | 'name': 'x-25', 178 | 'width': 16, 179 | 'poly': 0x1021, 180 | 'reflect_in': True, 181 | 'xor_in': 0xffff, 182 | 'reflect_out': True, 183 | 'xor_out': 0xffff, 184 | 'check': 0x906e, 185 | }) 186 | models.append({ 187 | 'name': 'xmodem', 188 | 'width': 16, 189 | 'poly': 0x1021, 190 | 'reflect_in': False, 191 | 'xor_in': 0x0, 192 | 'reflect_out': False, 193 | 'xor_out': 0x0, 194 | 'check': 0x31c3, 195 | }) 196 | models.append({ 197 | 'name': 'zmodem', 198 | 'width': 16, 199 | 'poly': 0x1021, 200 | 'reflect_in': False, 201 | 'xor_in': 0x0, 202 | 'reflect_out': False, 203 | 'xor_out': 0x0, 204 | 'check': 0x31c3, 205 | }) 206 | models.append({ 207 | 'name': 'crc-24', 208 | 'width': 24, 209 | 'poly': 0x864cfb, 210 | 'reflect_in': False, 211 | 'xor_in': 0xb704ce, 212 | 'reflect_out': False, 213 | 'xor_out': 0x0, 214 | 'check': 0x21cf02, 215 | }) 216 | models.append({ 217 | 'name': 'crc-32', 218 | 'width': 32, 219 | 'poly': 0x4c11db7, 220 | 'reflect_in': True, 221 | 'xor_in': 0xffffffff, 222 | 'reflect_out': True, 223 | 'xor_out': 0xffffffff, 224 | 'check': 0xcbf43926, 225 | }) 226 | models.append({ 227 | 'name': 'crc-32c', 228 | 'width': 32, 229 | 'poly': 0x1edc6f41, 230 | 'reflect_in': True, 231 | 'xor_in': 0xffffffff, 232 | 'reflect_out': True, 233 | 'xor_out': 0xffffffff, 234 | 'check': 0xe3069283, 235 | }) 236 | models.append({ 237 | 'name': 'crc-32-mpeg', 238 | 'width': 32, 239 | 'poly': 0x4c11db7, 240 | 'reflect_in': False, 241 | 'xor_in': 0xffffffff, 242 | 'reflect_out': False, 243 | 'xor_out': 0x0, 244 | 'check': 0x0376e6e7, 245 | }) 246 | models.append({ 247 | 'name': 'crc-32-bzip2', 248 | 'width': 32, 249 | 'poly': 0x04c11db7, 250 | 'reflect_in': False, 251 | 'xor_in': 0xffffffff, 252 | 'reflect_out': False, 253 | 'xor_out': 0xffffffff, 254 | 'check': 0xfc891918, 255 | }) 256 | models.append({ 257 | 'name': 'posix', 258 | 'width': 32, 259 | 'poly': 0x4c11db7, 260 | 'reflect_in': False, 261 | 'xor_in': 0x0, 262 | 'reflect_out': False, 263 | 'xor_out': 0xffffffff, 264 | 'check': 0x765e7680, 265 | }) 266 | models.append({ 267 | 'name': 'jam', 268 | 'width': 32, 269 | 'poly': 0x4c11db7, 270 | 'reflect_in': True, 271 | 'xor_in': 0xffffffff, 272 | 'reflect_out': True, 273 | 'xor_out': 0x0, 274 | 'check': 0x340bc6d9, 275 | }) 276 | models.append({ 277 | 'name': 'xfer', 278 | 'width': 32, 279 | 'poly': 0x000000af, 280 | 'reflect_in': False, 281 | 'xor_in': 0x0, 282 | 'reflect_out': False, 283 | 'xor_out': 0x0, 284 | 'check': 0xbd0be338, 285 | }) 286 | models.append({ 287 | 'name': 'crc-64', 288 | 'width': 64, 289 | 'poly': 0x000000000000001b, 290 | 'reflect_in': True, 291 | 'xor_in': 0x0, 292 | 'reflect_out': True, 293 | 'xor_out': 0x0, 294 | 'check': 0x46a5a9388a5beffe, 295 | }) 296 | models.append({ 297 | 'name': 'crc-64-jones', 298 | 'width': 64, 299 | 'poly': 0xad93d23594c935a9, 300 | 'reflect_in': True, 301 | 'xor_in': 0xffffffffffffffff, 302 | 'reflect_out': True, 303 | 'xor_out': 0x0, 304 | 'check': 0xcaa717168609f281, 305 | }) 306 | models.append({ 307 | 'name': 'crc-64-xz', 308 | 'width': 64, 309 | 'poly': 0x42f0e1eba9ea3693, 310 | 'reflect_in': True, 311 | 'xor_in': 0xffffffffffffffff, 312 | 'reflect_out': True, 313 | 'xor_out': 0xffffffffffffffff, 314 | 'check': 0x995dc9bbdf1939fa, 315 | }) 316 | 317 | def names(self): 318 | """ 319 | This function returns the list of supported CRC models. 320 | """ 321 | return [model['name'] for model in self.models] 322 | 323 | def get_params(self, model): 324 | """ 325 | This function returns the parameters of a given model. 326 | """ 327 | model = model.lower() 328 | for i in self.models: 329 | if i['name'] == model: 330 | return i 331 | return None 332 | -------------------------------------------------------------------------------- /src/pycrc/expr.py: -------------------------------------------------------------------------------- 1 | # pycrc -- parameterisable CRC calculation utility and C source code generator 2 | # 3 | # Copyright (c) 2017 Thomas Pircher 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to 7 | # deal in the Software without restriction, including without limitation the 8 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | # sell copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | # IN THE SOFTWARE. 22 | 23 | 24 | """ 25 | This modules simplifies an expression. 26 | 27 | import pycrc.expr as exp 28 | 29 | my_expr = exp.Xor('var', exp.Parenthesis(exp.And('0x700', 4))) 30 | print('"{}" -> "{}"'.format(my_expr, my_expr.simplify())) 31 | """ 32 | 33 | 34 | def _classify(val): 35 | """ 36 | Creates a Terminal object if the parameter is a string or an integer. 37 | """ 38 | if isinstance(val, int): 39 | return Terminal(val) 40 | if isinstance(val, str): 41 | if val.isdigit(): 42 | return Terminal(int(val), val) 43 | if val[:2].lower() == '0x': 44 | return Terminal(int(val, 16), val) 45 | return Terminal(val) 46 | return val 47 | 48 | 49 | class Expression(): 50 | """ 51 | Base class for all expressions. 52 | """ 53 | def is_int(self, val=None): 54 | """Dummy function, always returns False. This is overwritten bu derived classes.""" 55 | return False 56 | 57 | 58 | class Terminal(Expression): 59 | """ 60 | A terminal object. 61 | """ 62 | def __init__(self, val, pretty=None): 63 | """ 64 | Construct a Terminal. 65 | The val variable is usually a string or an integer. Integers may also 66 | be passed as strings. The pretty-printer will use the string when 67 | formatting the expression. 68 | """ 69 | self.val = val 70 | self.pretty = pretty 71 | 72 | def __str__(self): 73 | """ 74 | Return the string expression of this object. 75 | """ 76 | if self.pretty is None: 77 | return str(self.val) 78 | return self.pretty 79 | 80 | def simplify(self): 81 | """ 82 | Return a simplified version of this sub-expression. 83 | """ 84 | return self 85 | 86 | def is_int(self, val=None): 87 | """ 88 | Return True if the value of this Terminal is an integer. 89 | """ 90 | if isinstance(self.val, int): 91 | return val is None or self.val == val 92 | return False 93 | 94 | 95 | class FunctionCall(Expression): 96 | """ 97 | Represent a function call 98 | """ 99 | def __init__(self, name, args): 100 | """ 101 | Construct a function call object. 102 | """ 103 | self.name = _classify(name) 104 | self.args = [_classify(arg) for arg in args] 105 | 106 | def __str__(self): 107 | """ 108 | Return the string expression of this object. 109 | """ 110 | return str(self.name) + '(' + ', '.join([str(arg) for arg in self.args]) + ')' 111 | 112 | def simplify(self): 113 | """ 114 | Return a simplified version of this sub-expression. 115 | """ 116 | args = [arg.simplify() for arg in self.args] 117 | return FunctionCall(self.name, args) 118 | 119 | 120 | class Parenthesis(Expression): 121 | """ 122 | Represent a pair of round brackets. 123 | """ 124 | def __init__(self, val): 125 | """ 126 | Construct a parenthesis object. 127 | """ 128 | self.val = _classify(val) 129 | 130 | def simplify(self): 131 | """ 132 | Return a simplified version of this sub-expression. 133 | """ 134 | val = self.val.simplify() 135 | if isinstance(val, Terminal): 136 | return val 137 | return Parenthesis(val) 138 | 139 | def __str__(self): 140 | """ 141 | Return the string expression of this object. 142 | """ 143 | return '(' + str(self.val) + ')' 144 | 145 | 146 | class Add(Expression): 147 | """ 148 | Represent an addition of operands. 149 | """ 150 | def __init__(self, lhs, rhs): 151 | """ 152 | Construct an addition object. 153 | """ 154 | self.lhs = _classify(lhs) 155 | self.rhs = _classify(rhs) 156 | 157 | def simplify(self): 158 | """ 159 | Return a simplified version of this sub-expression. 160 | """ 161 | lhs = self.lhs.simplify() 162 | rhs = self.rhs.simplify() 163 | if lhs.is_int() and rhs.is_int(): 164 | return Terminal(lhs.val + rhs.val) 165 | if lhs.is_int(0): 166 | return rhs 167 | if rhs.is_int(0): 168 | return lhs 169 | return Add(lhs, rhs) 170 | 171 | def __str__(self): 172 | """ 173 | Return the string expression of this object. 174 | """ 175 | return str(self.lhs) + ' + ' + str(self.rhs) 176 | 177 | 178 | class Sub(Expression): 179 | """ 180 | Represent a subtraction of operands. 181 | """ 182 | def __init__(self, lhs, rhs): 183 | """ 184 | Construct subtraction object. 185 | """ 186 | self.lhs = _classify(lhs) 187 | self.rhs = _classify(rhs) 188 | 189 | def simplify(self): 190 | """ 191 | Return a simplified version of this sub-expression. 192 | """ 193 | lhs = self.lhs.simplify() 194 | rhs = self.rhs.simplify() 195 | if lhs.is_int() and rhs.is_int(): 196 | return Terminal(lhs.val - rhs.val) 197 | if lhs.is_int(0): 198 | return rhs 199 | if rhs.is_int(0): 200 | return lhs 201 | return Sub(lhs, rhs) 202 | 203 | def __str__(self): 204 | """ 205 | Return the string expression of this object. 206 | """ 207 | return str(self.lhs) + ' - ' + str(self.rhs) 208 | 209 | 210 | class Mul(Expression): 211 | """ 212 | Represent the multiplication of operands. 213 | """ 214 | def __init__(self, lhs, rhs): 215 | """ 216 | Construct a multiplication object. 217 | """ 218 | self.lhs = _classify(lhs) 219 | self.rhs = _classify(rhs) 220 | 221 | def simplify(self): 222 | """ 223 | Return a simplified version of this sub-expression. 224 | """ 225 | lhs = self.lhs.simplify() 226 | rhs = self.rhs.simplify() 227 | if lhs.is_int() and rhs.is_int(): 228 | return Terminal(lhs.val * rhs.val) 229 | if lhs.is_int(0) or rhs.is_int(0): 230 | return Terminal(0) 231 | if lhs.is_int(1): 232 | return rhs 233 | if rhs.is_int(1): 234 | return lhs 235 | return Mul(lhs, rhs) 236 | 237 | def __str__(self): 238 | """ 239 | Return the string expression of this object. 240 | """ 241 | return str(self.lhs) + ' * ' + str(self.rhs) 242 | 243 | 244 | class Shl(Expression): 245 | """ 246 | Shift left operation. 247 | """ 248 | def __init__(self, lhs, rhs): 249 | """ 250 | Construct a shift left object. 251 | """ 252 | self.lhs = _classify(lhs) 253 | self.rhs = _classify(rhs) 254 | 255 | def simplify(self): 256 | """ 257 | Return a simplified version of this sub-expression. 258 | """ 259 | lhs = self.lhs.simplify() 260 | rhs = self.rhs.simplify() 261 | if lhs.is_int() and rhs.is_int(): 262 | return Terminal(lhs.val << rhs.val) 263 | if lhs.is_int(0): 264 | return Terminal(0) 265 | if rhs.is_int(0): 266 | return lhs 267 | return Shl(lhs, rhs) 268 | 269 | def __str__(self): 270 | """ 271 | Return the string expression of this object. 272 | """ 273 | return str(self.lhs) + ' << ' + str(self.rhs) 274 | 275 | 276 | class Shr(Expression): 277 | """ 278 | Shift right operation. 279 | """ 280 | def __init__(self, lhs, rhs): 281 | """ 282 | Construct a shift right object. 283 | """ 284 | self.lhs = _classify(lhs) 285 | self.rhs = _classify(rhs) 286 | 287 | def simplify(self): 288 | """ 289 | Return a simplified version of this sub-expression. 290 | """ 291 | lhs = self.lhs.simplify() 292 | rhs = self.rhs.simplify() 293 | if lhs.is_int() and rhs.is_int(): 294 | return Terminal(lhs.val >> rhs.val) 295 | if lhs.is_int(0): 296 | return Terminal(0) 297 | if rhs.is_int(0): 298 | return lhs 299 | return Shr(lhs, rhs) 300 | 301 | def __str__(self): 302 | """ 303 | Return the string expression of this object. 304 | """ 305 | return str(self.lhs) + ' >> ' + str(self.rhs) 306 | 307 | 308 | class Or(Expression): 309 | """ 310 | Logical or operation. 311 | """ 312 | def __init__(self, lhs, rhs): 313 | """ 314 | Construct a logical and object. 315 | """ 316 | self.lhs = _classify(lhs) 317 | self.rhs = _classify(rhs) 318 | 319 | def simplify(self): 320 | """ 321 | Return a simplified version of this sub-expression. 322 | """ 323 | lhs = self.lhs.simplify() 324 | rhs = self.rhs.simplify() 325 | if lhs.is_int() and rhs.is_int(): 326 | return Terminal(lhs.val | rhs.val) 327 | if lhs.is_int(0): 328 | return rhs 329 | if rhs.is_int(0): 330 | return lhs 331 | return Or(lhs, rhs) 332 | 333 | def __str__(self): 334 | """ 335 | Return the string expression of this object. 336 | """ 337 | return str(self.lhs) + ' | ' + str(self.rhs) 338 | 339 | 340 | class And(Expression): 341 | """ 342 | Logical and operation. 343 | """ 344 | def __init__(self, lhs, rhs): 345 | """ 346 | Construct a logical and object. 347 | """ 348 | self.lhs = _classify(lhs) 349 | self.rhs = _classify(rhs) 350 | 351 | def simplify(self): 352 | """ 353 | Return a simplified version of this sub-expression. 354 | """ 355 | lhs = self.lhs.simplify() 356 | rhs = self.rhs.simplify() 357 | if lhs.is_int() and rhs.is_int(): 358 | return Terminal(lhs.val & rhs.val) 359 | if lhs.is_int(0) or rhs.is_int(0): 360 | return Terminal(0) 361 | return And(lhs, rhs) 362 | 363 | def __str__(self): 364 | """ 365 | Return the string expression of this object. 366 | """ 367 | return str(self.lhs) + ' & ' + str(self.rhs) 368 | 369 | 370 | class Xor(Expression): 371 | """ 372 | Logical xor operation. 373 | """ 374 | def __init__(self, lhs, rhs): 375 | """ 376 | Construct a logical xor object. 377 | """ 378 | self.lhs = _classify(lhs) 379 | self.rhs = _classify(rhs) 380 | 381 | def simplify(self): 382 | """ 383 | Return a simplified version of this sub-expression. 384 | """ 385 | lhs = self.lhs.simplify() 386 | rhs = self.rhs.simplify() 387 | if lhs.is_int() and rhs.is_int(): 388 | return Terminal(lhs.val ^ rhs.val) 389 | if lhs.is_int(0): 390 | return rhs 391 | if rhs.is_int(0): 392 | return lhs 393 | return Xor(lhs, rhs) 394 | 395 | def __str__(self): 396 | """ 397 | Return the string expression of this object. 398 | """ 399 | return str(self.lhs) + ' ^ ' + str(self.rhs) 400 | -------------------------------------------------------------------------------- /src/pycrc/symtable.py: -------------------------------------------------------------------------------- 1 | # pycrc -- parameterisable CRC calculation utility and C source code generator 2 | # 3 | # Copyright (c) 2006-2017 Thomas Pircher 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to 7 | # deal in the Software without restriction, including without limitation the 8 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | # sell copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | # IN THE SOFTWARE. 22 | 23 | 24 | """ 25 | Symbol table for the macro processor used by pycrc. 26 | use as follows: 27 | 28 | import pycrc.opt as opt 29 | import pycrc.symtable as sym 30 | 31 | opt = opt.Options() 32 | sym = sym.SymbolTable(opt) 33 | 34 | print(sym.crc_width) 35 | print(f'width: {sym.crc_width}, poly: {sym.crc_poly}') 36 | """ 37 | 38 | from pycrc.algorithms import Crc 39 | import time 40 | import os 41 | 42 | 43 | class SymbolTable: 44 | """ 45 | A class with the symbols as public members. 46 | """ 47 | 48 | def __init__(self, opt): 49 | self._opt = opt 50 | self.tbl_shift = _tbl_shift(opt) 51 | 52 | self.datetime = time.asctime() 53 | self.program_version = self._opt.version_str 54 | self.program_url = self._opt.web_address 55 | self.filename = 'pycrc_stdout' if self._opt.output_file is None else os.path.basename(self._opt.output_file) 56 | self.header_filename = _pretty_header_filename(self._opt.output_file) 57 | self.header_protection = _pretty_hdrprotection(self._opt) 58 | 59 | self.crc_algorithm = _pretty_algorithm(self._opt) 60 | self.crc_width = _pretty_str(self._opt.width) 61 | self.crc_poly = _pretty_hex(self._opt.poly, self._opt.width) 62 | self.crc_reflect_in = _pretty_bool(self._opt.reflect_in) 63 | self.crc_xor_in = _pretty_hex(self._opt.xor_in, self._opt.width) 64 | self.crc_reflect_out = _pretty_bool(self._opt.reflect_out) 65 | self.crc_xor_out = _pretty_hex(self._opt.xor_out, self._opt.width) 66 | self.crc_slice_by = _pretty_str(self._opt.slice_by) 67 | self.crc_table_idx_width = str(self._opt.tbl_idx_width) 68 | self.crc_table_width = _pretty_str(1 << self._opt.tbl_idx_width) 69 | self.crc_table_mask = _pretty_hex(self._opt.tbl_width - 1, 8) 70 | self.crc_mask = _pretty_hex(self._opt.mask, self._opt.width) 71 | self.crc_msb_mask = _pretty_hex(self._opt.msb_mask, self._opt.width) 72 | self.crc_shift = _pretty_str(self.tbl_shift) 73 | 74 | self.cfg_width = self.crc_width if self._opt.width is not None else 'cfg->width' 75 | self.cfg_poly = self.crc_poly if self._opt.poly is not None else 'cfg->poly' 76 | self.cfg_reflect_in = self.crc_reflect_in if self._opt.reflect_in is not None else 'cfg->reflect_in' 77 | self.cfg_xor_in = self.crc_xor_in if self._opt.xor_in is not None else 'cfg->xor_in' 78 | self.cfg_reflect_out = self.crc_reflect_out if self._opt.reflect_out is not None else 'cfg->reflect_out' 79 | self.cfg_xor_out = self.crc_xor_out if self._opt.xor_out is not None else 'cfg->xor_out' 80 | self.cfg_table_idx_width = self.crc_table_idx_width if self._opt.tbl_idx_width is not None else 'cfg->table_idx_width' 81 | self.cfg_table_width = self.crc_table_width if self._opt.tbl_width is not None else 'cfg->table_width' 82 | self.cfg_mask = self.crc_mask if self._opt.mask is not None else 'cfg->crc_mask' 83 | self.cfg_msb_mask = self.crc_msb_mask if self._opt.msb_mask is not None else 'cfg->msb_mask' 84 | self.cfg_shift = self.crc_shift if self.tbl_shift is not None else 'cfg->crc_shift' 85 | 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 86 | 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 87 | 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 88 | 89 | self.c_bool = 'int' if self._opt.c_std == 'C89' else 'bool' 90 | self.c_true = '1' if self._opt.c_std == 'C89' else 'true' 91 | self.c_false = '0' if self._opt.c_std == 'C89' else 'false' 92 | 93 | self.underlying_crc_t = _get_underlying_crc_t(self._opt) 94 | self.crc_t = self._opt.symbol_prefix + 't' 95 | self.cfg_t = self._opt.symbol_prefix + 'cfg_t' 96 | self.crc_reflect_function = self._opt.symbol_prefix + 'reflect' 97 | self.crc_table_gen_function = self._opt.symbol_prefix + 'table_gen' 98 | self.crc_init_function = self._opt.symbol_prefix + 'init' 99 | self.crc_update_function = self._opt.symbol_prefix + 'update' 100 | self.crc_finalize_function = self._opt.symbol_prefix + 'finalize' 101 | 102 | self.crc_init_value = _get_init_value(self._opt) 103 | self._crc_table_init = None 104 | 105 | @property 106 | def crc_table_init(self): 107 | if self._crc_table_init is None: 108 | self._crc_table_init = _get_table_init(self._opt) 109 | return self._crc_table_init 110 | 111 | 112 | def _pretty_str(value): 113 | """ 114 | Return a value of width bits as a pretty string. 115 | """ 116 | if value is None: 117 | return 'Undefined' 118 | return str(value) 119 | 120 | 121 | def _pretty_hex(value, width=None): 122 | """ 123 | Return a value of width bits as a pretty hexadecimal formatted string. 124 | """ 125 | if value is None: 126 | return 'Undefined' 127 | if width is None: 128 | return '{0:#x}'.format(value) 129 | width = (width + 3) // 4 130 | hex_str = "{{0:#0{0:d}x}}".format(width + 2) 131 | return hex_str.format(value) 132 | 133 | 134 | def _pretty_bool(value): 135 | """ 136 | Return a boolen value of width bits as a pretty formatted string. 137 | """ 138 | if value is None: 139 | return 'Undefined' 140 | return 'True' if value else 'False' 141 | 142 | 143 | def _pretty_algorithm(opt): 144 | """ 145 | Return the algorithm name. 146 | """ 147 | if opt.algorithm == opt.algo_bit_by_bit: 148 | return 'bit-by-bit' 149 | elif opt.algorithm == opt.algo_bit_by_bit_fast: 150 | return 'bit-by-bit-fast' 151 | elif opt.algorithm == opt.algo_table_driven: 152 | return 'table-driven' 153 | else: 154 | return 'UNDEFINED' 155 | 156 | 157 | def _pretty_header_filename(filename): 158 | """ 159 | Return the sanitized filename of a header file. 160 | """ 161 | if filename is None: 162 | return 'pycrc_stdout.h' 163 | filename = os.path.basename(filename) 164 | if filename[-2:] == '.c': 165 | return filename[0:-1] + 'h' 166 | else: 167 | return filename + '.h' 168 | 169 | 170 | def _pretty_hdrprotection(opt): 171 | """ 172 | Return the name of a C header protection (e.g. CRC_IMPLEMENTATION_H). 173 | """ 174 | if opt.output_file is None: 175 | filename = 'pycrc_stdout' 176 | else: 177 | filename = os.path.basename(opt.output_file) 178 | out_str = ''.join([s.upper() if s.isalnum() else '_' for s in filename]) 179 | return out_str 180 | 181 | 182 | def _get_underlying_crc_t(opt): # noqa: C901 183 | # pylint: disable=too-many-return-statements, too-many-branches 184 | """ 185 | Return the C type of the crc_t typedef. 186 | """ 187 | 188 | if opt.crc_type is not None: 189 | return opt.crc_type 190 | if opt.c_std == 'C89': 191 | if opt.width is None: 192 | return 'unsigned long int' 193 | if opt.width <= 8: 194 | return 'unsigned char' 195 | if opt.width <= 16: 196 | return 'unsigned int' 197 | return 'unsigned long int' 198 | else: # C99 199 | if opt.width is None: 200 | return 'unsigned long long int' 201 | if opt.width <= 8: 202 | return 'uint_fast8_t' 203 | if opt.width <= 16: 204 | return 'uint_fast16_t' 205 | if opt.width <= 32: 206 | return 'uint_fast32_t' 207 | if opt.width <= 64: 208 | return 'uint_fast64_t' 209 | if opt.width <= 128: 210 | return 'uint_fast128_t' 211 | return 'uintmax_t' 212 | 213 | 214 | def _get_init_value(opt): 215 | """ 216 | Return the init value of a C implementation, according to the selected 217 | algorithm and to the given options. 218 | If no default option is given for a given parameter, value in the cfg_t 219 | structure must be used. 220 | """ 221 | if opt.algorithm == opt.algo_bit_by_bit: 222 | if opt.xor_in is None or opt.width is None or opt.poly is None: 223 | return None 224 | crc = Crc( 225 | width=opt.width, poly=opt.poly, 226 | reflect_in=opt.reflect_in, xor_in=opt.xor_in, 227 | reflect_out=opt.reflect_out, xor_out=opt.xor_out, 228 | table_idx_width=opt.tbl_idx_width) 229 | init = crc.nondirect_init 230 | elif opt.algorithm == opt.algo_bit_by_bit_fast: 231 | if opt.xor_in is None: 232 | return None 233 | init = opt.xor_in 234 | elif opt.algorithm == opt.algo_table_driven: 235 | if opt.reflect_in is None or opt.xor_in is None or opt.width is None: 236 | return None 237 | if opt.poly is None: 238 | poly = 0 239 | else: 240 | poly = opt.poly 241 | crc = Crc( 242 | width=opt.width, poly=poly, 243 | reflect_in=opt.reflect_in, xor_in=opt.xor_in, 244 | reflect_out=opt.reflect_out, xor_out=opt.xor_out, 245 | table_idx_width=opt.tbl_idx_width) 246 | if opt.reflect_in: 247 | init = crc.reflect(crc.direct_init, opt.width) 248 | else: 249 | init = crc.direct_init 250 | else: 251 | init = 0 252 | return _pretty_hex(init, opt.width) 253 | 254 | 255 | def _get_simple_table(opt, crc_tbl, values_per_line, format_width, indent): 256 | """ 257 | Get one CRC table, formatted as string with appropriate indenting and 258 | line breaks. 259 | """ 260 | out = "" 261 | for i in range(opt.tbl_width): 262 | if i % values_per_line == 0: 263 | out += " " * indent 264 | tbl_val = _pretty_hex(crc_tbl[i], format_width) 265 | if i == (opt.tbl_width - 1): 266 | out += "{0:s}".format(tbl_val) 267 | elif i % values_per_line == (values_per_line - 1): 268 | out += "{0:s},\n".format(tbl_val) 269 | else: 270 | out += "{0:s}, ".format(tbl_val) 271 | return out 272 | 273 | 274 | def _get_table_init(opt): # TODO: change to return a list 275 | """ 276 | Return the precalculated CRC table for the table_driven implementation. 277 | """ 278 | if opt.algorithm != opt.algo_table_driven: 279 | return "0" 280 | if opt.width is None or opt.poly is None or opt.reflect_in is None: 281 | return "0" 282 | crc = Crc( 283 | width=opt.width, poly=opt.poly, 284 | reflect_in=opt.reflect_in, 285 | xor_in=0, reflect_out=False, xor_out=0, # set unimportant variables to known values 286 | table_idx_width=opt.tbl_idx_width, 287 | slice_by=opt.slice_by) 288 | crc_tbl = crc.gen_table() 289 | if opt.width > 32: 290 | values_per_line = 4 291 | elif opt.width >= 16: 292 | values_per_line = 8 293 | else: 294 | values_per_line = 16 295 | format_width = max(opt.width, 8) 296 | if opt.slice_by == 1: 297 | indent = 4 298 | else: 299 | indent = 8 300 | 301 | out = [''] * opt.slice_by 302 | for i in range(opt.slice_by): 303 | out[i] = _get_simple_table(opt, crc_tbl[i], values_per_line, format_width, indent) 304 | fixed_indent = ' ' * (indent - 4) 305 | out = '{0:s}{{\n'.format(fixed_indent) + \ 306 | '\n{0:s}}},\n{0:s}{{\n'.format(fixed_indent).join(out) + \ 307 | '\n{0:s}}}'.format(fixed_indent) 308 | if opt.slice_by == 1: 309 | return out 310 | return '{\n' + out + '\n}' 311 | 312 | 313 | def _tbl_shift(opt): 314 | """ 315 | Return the table shift value 316 | """ 317 | if opt.algorithm == opt.algo_table_driven and (opt.width is None or opt.width < 8): 318 | if opt.width is None: 319 | return None 320 | else: 321 | return 8 - opt.width 322 | else: 323 | return 0 324 | -------------------------------------------------------------------------------- /src/pycrc/opt.py: -------------------------------------------------------------------------------- 1 | # pycrc -- parameterisable CRC calculation utility and C source code generator 2 | # 3 | # Copyright (c) 2006-2017 Thomas Pircher 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to 7 | # deal in the Software without restriction, including without limitation the 8 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | # sell copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | # IN THE SOFTWARE. 22 | 23 | 24 | """ 25 | Option parsing library for pycrc. 26 | use as follows: 27 | 28 | from pycrc.opt import Options 29 | 30 | opt = Options() 31 | opt.parse(sys.argv[1:]) 32 | """ 33 | 34 | from optparse import OptionParser, Option, OptionValueError 35 | from copy import copy 36 | import sys 37 | from pycrc.models import CrcModels 38 | 39 | 40 | class Options(object): 41 | """ 42 | The options parsing and validating class. 43 | """ 44 | # pylint: disable=too-many-instance-attributes, too-few-public-methods 45 | 46 | # Bitmap of the algorithms 47 | algo_none = 0x00 48 | algo_bit_by_bit = 0x01 49 | algo_bit_by_bit_fast = 0x02 50 | algo_table_driven = 0x08 51 | 52 | action_check_str = 0x01 53 | action_check_hex_str = 0x02 54 | action_check_file = 0x03 55 | action_generate_h = 0x04 56 | action_generate_c = 0x05 57 | action_generate_c_main = 0x06 58 | action_generate_table = 0x07 59 | 60 | def __init__(self, progname='pycrc', version='unknown', url='unknown'): 61 | self.program_name = progname 62 | self.version = version 63 | self.version_str = f"{progname} v{version}" 64 | self.web_address = url 65 | 66 | self.width = None 67 | self.poly = None 68 | self.reflect_in = None 69 | self.xor_in = None 70 | self.reflect_out = None 71 | self.xor_out = None 72 | self.tbl_idx_width = 8 73 | self.tbl_width = 1 << self.tbl_idx_width 74 | self.slice_by = 1 75 | self.verbose = False 76 | self.check_string = "123456789" 77 | self.msb_mask = None 78 | self.mask = None 79 | 80 | self.algorithm = self.algo_none 81 | self.symbol_prefix = "crc_" 82 | self.crc_type = None 83 | self.include_files = [] 84 | self.output_file = None 85 | self.action = self.action_check_str 86 | self.check_file = None 87 | self.c_std = None 88 | self.undefined_crc_parameters = False 89 | 90 | def parse(self, argv=None): # noqa: C901 91 | """ 92 | Parses and validates the options given as arguments 93 | """ 94 | # pylint: disable=too-many-branches, too-many-statements 95 | 96 | usage = """python %prog [OPTIONS] 97 | 98 | To calculate the checksum of a string or hexadecimal data: 99 | python %prog [model] --check-string "123456789" 100 | python %prog [model] --check-hexstring "313233343536373839" 101 | 102 | To calculate the checksum of a file: 103 | python %prog [model] --check-file filename 104 | 105 | To generate the C source code and write it to filename: 106 | python %prog [model] --generate c -o filename 107 | 108 | The model can be defined either with the --model switch or by specifying each 109 | of the following parameters: 110 | --width --poly --reflect-in --xor-in --reflect-out --xor-out""" 111 | 112 | models = CrcModels() 113 | model_list = ", ".join(models.names()) 114 | parser = OptionParser(option_class=MyOption, usage=usage, version=self.version_str) 115 | parser.add_option( 116 | "-v", "--verbose", 117 | action="store_true", dest="verbose", default=False, 118 | help="be more verbose; print the value of the parameters " 119 | "and the chosen model to stdout") 120 | parser.add_option( 121 | "--check-string", 122 | action="store", type="string", dest="check_string", 123 | help="calculate the checksum of a string (default: '123456789')", 124 | metavar="STRING") 125 | parser.add_option( 126 | "--check-hexstring", 127 | action="store", type="string", dest="check_hexstring", 128 | help="calculate the checksum of a hexadecimal number string", 129 | metavar="STRING") 130 | parser.add_option( 131 | "--check-file", 132 | action="store", type="string", dest="check_file", 133 | help="calculate the checksum of a file", 134 | metavar="FILE") 135 | parser.add_option( 136 | "--generate", 137 | action="store", type="string", dest="generate", default=None, 138 | help="generate C source code; choose the type from {h, c, c-main, table}", 139 | metavar="CODE") 140 | parser.add_option( 141 | "--std", 142 | action="store", type="string", dest="c_std", default="C99", 143 | help="choose the C dialect of the generated code from {C89, ANSI, C99}", 144 | metavar="STD") 145 | parser.add_option( 146 | "--algorithm", 147 | action="store", type="string", dest="algorithm", default="all", 148 | help="choose an algorithm from " 149 | "{bit-by-bit, bbb, bit-by-bit-fast, bbf, table-driven, tbl, all}", 150 | metavar="ALGO") 151 | parser.add_option( 152 | "--model", 153 | action="callback", callback=_model_cb, type="string", dest="model", default=None, 154 | help=f"choose a parameter set from {{{model_list}}}", 155 | metavar="MODEL") 156 | parser.add_option( 157 | "--width", 158 | action="store", type="hex", dest="width", 159 | help="use NUM bits in the polynomial", 160 | metavar="NUM") 161 | parser.add_option( 162 | "--poly", 163 | action="store", type="hex", dest="poly", 164 | help="use HEX as polynomial", 165 | metavar="HEX") 166 | parser.add_option( 167 | "--reflect-in", 168 | action="store", type="bool", dest="reflect_in", 169 | help="reflect the octets in the input message", 170 | metavar="BOOL") 171 | parser.add_option( 172 | "--xor-in", 173 | action="store", type="hex", dest="xor_in", 174 | help="use HEX as initial value", 175 | metavar="HEX") 176 | parser.add_option( 177 | "--reflect-out", 178 | action="store", type="bool", dest="reflect_out", 179 | help="reflect the resulting checksum before applying the --xor-out value", 180 | metavar="BOOL") 181 | parser.add_option( 182 | "--xor-out", 183 | action="store", type="hex", dest="xor_out", 184 | help="xor the final CRC value with HEX", 185 | metavar="HEX") 186 | parser.add_option( 187 | "--slice-by", 188 | action="store", type="int", dest="slice_by", 189 | help="read NUM bytes at a time from the input. NUM must be one of the values {4, 8, 16}", 190 | metavar="NUM") 191 | parser.add_option( 192 | "--table-idx-width", 193 | action="store", type="int", dest="table_idx_width", 194 | help="use NUM bits to index the CRC table; NUM must be one of the values {1, 2, 4, 8}", 195 | metavar="NUM") 196 | parser.add_option( 197 | "--force-poly", 198 | action="store_true", dest="force_poly", default=False, 199 | help="override any errors about possibly unsuitable polynoms") 200 | parser.add_option( 201 | "--symbol-prefix", 202 | action="store", type="string", dest="symbol_prefix", 203 | help="when generating source code, use STRING as prefix to the exported C symbols", 204 | metavar="STRING") 205 | parser.add_option( 206 | "--crc-type", 207 | action="store", type="string", dest="crc_type", 208 | help="when generating source code, use STRING as crc_t type", 209 | metavar="STRING") 210 | parser.add_option( 211 | "--include-file", 212 | action="append", type="string", dest="include_files", 213 | help="when generating source code, include also FILE as header file; " 214 | "can be specified multiple times", 215 | metavar="FILE") 216 | parser.add_option( 217 | "-o", "--output", 218 | action="store", type="string", dest="output_file", 219 | help="write the generated code to file instead to stdout", 220 | metavar="FILE") 221 | 222 | options, args = parser.parse_args(argv) 223 | 224 | if options.c_std is not None: 225 | std = options.c_std.upper() 226 | if std == "ANSI" or std == "C89": 227 | self.c_std = "C89" 228 | elif std == "C99": 229 | self.c_std = std 230 | else: 231 | self.__error(f"unknown C standard {options.c_std}") 232 | 233 | undefined_params = [] 234 | if options.width is not None: 235 | self.width = options.width 236 | else: 237 | undefined_params.append("--width") 238 | if options.poly is not None: 239 | self.poly = options.poly 240 | else: 241 | undefined_params.append("--poly") 242 | if options.reflect_in is not None: 243 | self.reflect_in = options.reflect_in 244 | else: 245 | undefined_params.append("--reflect-in") 246 | if options.xor_in is not None: 247 | self.xor_in = options.xor_in 248 | else: 249 | undefined_params.append("--xor-in") 250 | if options.reflect_out is not None: 251 | self.reflect_out = options.reflect_out 252 | else: 253 | undefined_params.append("--reflect-out") 254 | if options.xor_out is not None: 255 | self.xor_out = options.xor_out 256 | else: 257 | undefined_params.append("--xor-out") 258 | 259 | if options.table_idx_width is not None: 260 | if options.table_idx_width in set((1, 2, 4, 8)): 261 | self.tbl_idx_width = options.table_idx_width 262 | self.tbl_width = 1 << options.table_idx_width 263 | else: 264 | self.__error(f"unsupported table-idx-width {options.table_idx_width}") 265 | 266 | if self.poly is not None and self.poly % 2 == 0 and not options.force_poly: 267 | self.__error("even polinomials are not allowed by default. Use --force-poly to override this.") 268 | 269 | if self.width is not None: 270 | if self.width <= 0: 271 | self.__error("Width must be strictly positive") 272 | self.msb_mask = 0x1 << (self.width - 1) 273 | self.mask = ((self.msb_mask - 1) << 1) | 1 274 | if self.poly is not None and self.poly >> (self.width + 1) != 0 and not options.force_poly: 275 | self.__error("the polynomial is wider than the supplied Width. Use --force-poly to override this.") 276 | if self.poly is not None: 277 | self.poly = self.poly & self.mask 278 | if self.xor_in is not None: 279 | self.xor_in = self.xor_in & self.mask 280 | if self.xor_out is not None: 281 | self.xor_out = self.xor_out & self.mask 282 | else: 283 | self.msb_mask = None 284 | self.mask = None 285 | 286 | if self.width is None or \ 287 | self.poly is None or \ 288 | self.reflect_in is None or \ 289 | self.xor_in is None or \ 290 | self.reflect_out is None or \ 291 | self.xor_out is None: 292 | self.undefined_crc_parameters = True 293 | else: 294 | self.undefined_crc_parameters = False 295 | 296 | if options.slice_by is not None: 297 | if options.slice_by in set((4, 8, 16)): 298 | self.slice_by = options.slice_by 299 | else: 300 | self.__error(f"unsupported slice-by {options.slice_by}") 301 | if self.undefined_crc_parameters: 302 | self.__error("slice-by is only implemented for fully defined models") 303 | if self.tbl_idx_width != 8: 304 | self.__error("slice-by is only implemented for table-idx-width=8") 305 | # FIXME tp: Fix corner cases and disable the following tests 306 | if self.width < 8: 307 | self.__warning(f"disabling slice-by for width {self.width}") 308 | self.slice_by = 1 309 | if self.width < 16: 310 | self.__warning(f"disabling slice-by for width {self.width}") 311 | self.slice_by = 1 312 | if self.width > 32: 313 | self.__warning(f"disabling slice-by for width {self.width}") 314 | self.slice_by = 1 315 | if not self.reflect_in: 316 | self.__warning("disabling slice-by for non-reflected algorithm") 317 | self.slice_by = 1 318 | # FIXME tp: reintroduce this? 319 | # if self.width % 8 != 0: 320 | # self.__error("slice-by is only implemented for width multiples of 8") 321 | # if options.slice_by < self.width / 8: 322 | # self.__error("slice-by must be greater or equal width / 8") 323 | if self.c_std == "C89": 324 | self.__error("--slice-by not supported for C89") 325 | 326 | if options.algorithm is not None: 327 | alg = options.algorithm.lower() 328 | if alg in set(["bit-by-bit", "bbb", "all"]): 329 | self.algorithm |= self.algo_bit_by_bit 330 | if alg in set(["bit-by-bit-fast", "bbf", "all"]): 331 | self.algorithm |= self.algo_bit_by_bit_fast 332 | if alg in set(["table-driven", "tbl", "all"]): 333 | self.algorithm |= self.algo_table_driven 334 | if self.algorithm == 0: 335 | self.__error(f"unknown algorithm {options.algorithm}") 336 | 337 | if options.symbol_prefix is not None: 338 | self.symbol_prefix = options.symbol_prefix 339 | if options.include_files is not None: 340 | self.include_files = options.include_files 341 | if options.crc_type is not None: 342 | self.crc_type = options.crc_type 343 | if options.output_file is not None: 344 | self.output_file = options.output_file 345 | op_count = 0 346 | if options.check_string is not None: 347 | self.action = self.action_check_str 348 | self.check_string = options.check_string 349 | op_count += 1 350 | if options.check_hexstring is not None: 351 | self.action = self.action_check_hex_str 352 | self.check_string = options.check_hexstring 353 | op_count += 1 354 | if options.check_file is not None: 355 | self.action = self.action_check_file 356 | self.check_file = options.check_file 357 | op_count += 1 358 | if options.generate is not None: 359 | arg = options.generate.lower() 360 | if arg == 'h': 361 | self.action = self.action_generate_h 362 | elif arg == 'c': 363 | self.action = self.action_generate_c 364 | elif arg == 'c-main': 365 | self.action = self.action_generate_c_main 366 | elif arg == 'table': 367 | self.action = self.action_generate_table 368 | else: 369 | self.__error(f"don't know how to generate {options.generate}") 370 | op_count += 1 371 | 372 | if self.action == self.action_generate_table: 373 | if self.algorithm & self.algo_table_driven == 0: 374 | self.__error("the --generate table option is incompatible " 375 | "with the --algorithm option") 376 | self.algorithm = self.algo_table_driven 377 | elif self.algorithm not in set( 378 | [self.algo_bit_by_bit, self.algo_bit_by_bit_fast, self.algo_table_driven]): 379 | self.__error("select an algorithm to be used in the generated file. " 380 | "(Hint: use the --algorithm option.)") 381 | else: 382 | if self.tbl_idx_width != 8: 383 | self.__warning("reverting to Table Index Width = 8 " 384 | "for internal CRC calculation") 385 | self.tbl_idx_width = 8 386 | self.tbl_width = 1 << options.table_idx_width 387 | if op_count == 0: 388 | self.action = self.action_check_str 389 | if op_count > 1: 390 | self.__error("too many actions specified") 391 | 392 | if len(args) != 0: 393 | self.__error("unrecognized argument(s): {0:s}".format(" ".join(args))) 394 | 395 | def_params_acts = (self.action_check_str, self.action_check_hex_str, 396 | self.action_check_file, self.action_generate_table) 397 | if self.undefined_crc_parameters and self.action in set(def_params_acts): 398 | undefined_params_str = ", ".join(undefined_params) 399 | self.__error(f"undefined parameters: Add {undefined_params_str} or use --model") 400 | self.verbose = options.verbose 401 | 402 | def __warning(self, message): 403 | """ 404 | Print a warning message to stderr. 405 | """ 406 | sys.stderr.write(f"{self.program_name}: warning: {message}\n") 407 | 408 | def __error(self, message): 409 | """ 410 | Print a error message to stderr and terminate the program. 411 | """ 412 | self.__warning(message) 413 | sys.exit(1) 414 | 415 | 416 | def _model_cb(option, opt_str, value, parser): 417 | """ 418 | This function sets up the single parameters if the 'model' option has been selected 419 | by the user. 420 | """ 421 | model_name = value.lower() 422 | models = CrcModels() 423 | model = models.get_params(model_name) 424 | if model is not None: 425 | setattr(parser.values, 'width', model['width']) 426 | setattr(parser.values, 'poly', model['poly']) 427 | setattr(parser.values, 'reflect_in', model['reflect_in']) 428 | setattr(parser.values, 'xor_in', model['xor_in']) 429 | setattr(parser.values, 'reflect_out', model['reflect_out']) 430 | setattr(parser.values, 'xor_out', model['xor_out']) 431 | else: 432 | models = CrcModels() 433 | model_list = ", ".join(models.names()) 434 | raise OptionValueError(f"unsupported model {value}. Supported models are: {model_list}.") 435 | 436 | 437 | def _check_hex(dummy_option, opt, value): 438 | """ 439 | Checks if a value is given in a decimal integer of hexadecimal reppresentation. 440 | Returns the converted value or rises an exception on error. 441 | """ 442 | try: 443 | if value.lower().startswith("0x"): 444 | return int(value, 16) 445 | else: 446 | return int(value) 447 | except ValueError: 448 | raise OptionValueError(f"option {opt}: invalid integer or hexadecimal value: {value}.") 449 | 450 | 451 | def _check_bool(dummy_option, opt, value): 452 | """ 453 | Checks if a value is given as a boolean value (either 0 or 1 or "true" or "false") 454 | Returns the converted value or rises an exception on error. 455 | """ 456 | if value.isdigit(): 457 | return int(value, 10) != 0 458 | elif value.lower() == "false": 459 | return False 460 | elif value.lower() == "true": 461 | return True 462 | else: 463 | raise OptionValueError(f"option {opt}: invalid boolean value: {value}.") 464 | 465 | 466 | class MyOption(Option): 467 | """ 468 | New option parsing class extends the Option class 469 | """ 470 | TYPES = Option.TYPES + ("hex", "bool") 471 | TYPE_CHECKER = copy(Option.TYPE_CHECKER) 472 | TYPE_CHECKER["hex"] = _check_hex 473 | TYPE_CHECKER["bool"] = _check_bool 474 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | For a detailed list of changes see the [pycrc GitHub][pycrc github] page. 4 | This project adheres to [Semantic Versioning](http://semver.org/). 5 | 6 | 7 | 8 | ## [v0.11.0] - 2025-08-19 9 | 10 | ### Added 11 | 12 | - Add support for GitHub CI 13 | - Add build instructions for Debian/Ubuntu and derivatives. 14 | 15 | ### Changed 16 | 17 | - Minimum Python version is 3.8 18 | - The lookup table for the table-driven algorithm is now initialised at 19 | startup, speeding up the CRC calculation when called multiple times. 20 | - Remove the obsolete `__init__.py` file. 21 | - Minor speed improvement. 22 | 23 | ### Fixed 24 | 25 | - Make flake8 happy 26 | 27 | ## [v0.10.0] - 2022-11-01 28 | 29 | ### Added 30 | 31 | - Use pytest for regression tests 32 | - Update to pyproject.toml for packaging 33 | 34 | ### Removed 35 | 36 | - Removed Python 2 compatibility. Use pycrc v0.9.x if Python 2 is required. 37 | 38 | ### Fixed 39 | 40 | - The xor-in value is never reflected 41 | Thanks to Ralf Schlatterbeck 42 | 43 | 44 | ## [v0.9.3] - 2022-11-01 45 | 46 | ### Fixed 47 | - Fix compatibility with Python 3.10. 48 | Fixes #46. 49 | - Slightly improved the performance of the bit-by-bit-fast algorithm. 50 | 51 | 52 | ## [v0.9.2] - 2019-02-06 53 | 54 | ### Fixed 55 | - Fix code generator to output the table correctly. 56 | Fixes #28. 57 | - Ensure the data is aligned when using slice-by. 58 | Fixes #24. 59 | 60 | 61 | ## [v0.9.1] - 2017-09-09 62 | 63 | ### Added 64 | - Added setup.py script to install pycrc on the system, if desired. 65 | - Added checks about possibly unsuitable polynomials. Use the --force-poly 66 | option to override the error message. 67 | 68 | ### Changed 69 | - Completely rewritten the code generator back-end. The new back-end is more 70 | flexible and allows for a better optimisation of the generated expressions. 71 | - Moved the python code under the `pycrc` directory and changed how the pycrc 72 | sub-modules are included. Before one would write `import crc_xxx`, now one 73 | would write `import pycrc.xxx`. 74 | - New license for the documentation: 75 | [Creative Commons Attribution-Share Alike 4.0 Unported License][CC-BY-SA-4.0]. 76 | 77 | ### Fixed 78 | - Fixed binary files handling with Python 2.7. 79 | Fixes #11. Thanks to James Bowman. 80 | - Fixed some spelling. Thanks to Frank (ftheile) and ashelly. 81 | - Fixed Header Guard generation. Don't use underscores to start the header 82 | guard name. Thanks to Andre Hartmann. 83 | 84 | 85 | ## [v0.9] - 2016-01-06 86 | 87 | ### Added 88 | - Added Stephan Brumme to the `AUTHORS` file as his implementation of the 89 | slice-by algorithm is the basis for pycrc's implementation. 90 | - Added a new option `--slice-by`. This option is still experimental and 91 | limited in its use. 92 | 93 | ### Changed 94 | - Documented the experimental `--slice-by option`. 95 | - Simplified the implementation where Width is less than 8 bits. 96 | - Run the code through `pylint`. 97 | - __API change__: changed the names of the member variables from `CamelCase` to the 98 | format suggested in `PEP 0008` (lowercase letters and words separated by 99 | underscore). 100 | 101 | ### Fixed 102 | - Suppressed the `crc_reflect` function where not needed. Addresses part of #8. 103 | Thanks to Craig McQueen. 104 | - Allow strings with values greater than 0x80 in `--check-hexstring`. 105 | - When the CRC width is less than 8 then the `bit-by-bit` algorithm needs to 106 | apply the CRC mask to the final value. 107 | Fixes #10. Thanks to Steve Geo. 108 | - Fixed the initial value of the 16-bit `CCITT` algorithm. Renamed the model 109 | from `ccitt` to `crc-16-ccitt`. 110 | Fixes #7. Thanks to Craig McQueen. 111 | 112 | 113 | ## [v0.8.3] - 2015-08-31 114 | 115 | ### Changed 116 | - pycrc has a new [homepage][pycrc home]. 117 | - The [issue tracker on GitHub][pycrc issues] 118 | is now advertised as the preferred issue tracker. 119 | - Applied some minor optimisations to the generated table-driven code. 120 | - Belatedly added an authors file. Should I have forgotten to mention someone 121 | please don't hesitate to send a mail. 122 | - Upgraded documentation to DocBook 5. 123 | - Removed sourceforge mailing list from `README.md` in an effort to move pycrc 124 | away from sourceforge. 125 | - Removed the experimental `--bitwise-expression` option to facilitate 126 | restructuring of the code. 127 | - The preferred format for the input data for the Python API is now a byte 128 | array. But if a string is supplied it is decoded as UTF-8 string. 129 | Alternative formats are not supported and must be passed to the functions 130 | as byte arrays. 131 | - Changed the signature of the `crc_update()` function: the data argument is 132 | now a pointer to void to improve compatibility with C++. 133 | Thanks to Kamil Szczygieł. 134 | This closes GitHub issue #4. 135 | 136 | 137 | ## [v0.8.2] - 2014-12-04 138 | 139 | ### Changed 140 | - Smaller code cleanups. 141 | - Stated more clearly that the bitwise expression algorithm is experimental in 142 | the documentation. 143 | - Fixed a typo in the documentation. 144 | The description previously stated: 145 | "The reflected value of 0xa3 (10100010b) is 0x45 (01000101b)" 146 | but this should be: 147 | "The reflected value of 0xa2 (10100010b) is 0x45 (01000101b)" 148 | Thanks to Andreas Nebenfuehr for reporting the mistake. 149 | - Small cleanups. 150 | Added a tests for special cases. For now, added `crc-5` with non-inverted 151 | input. This test is currently failing. 152 | - Removed warning about even polynomials. As Lars Pötter rightly pointed out, 153 | polynomials may be even. 154 | Added a caveat emptor about even polinomials in the documentation. 155 | 156 | ### Fixed 157 | - The table-driven code for polynomials of width < 8 using a table index 158 | width < 8 was producing a wrong checksum. 159 | Thanks to Radosław Gancarz. 160 | - Updated the generated code to cope with big Widths (>32 bits) on 32 bit 161 | processors. 162 | Since C89 does not give a way to specify the minimum length of a data type, 163 | the test does not validate C89 code using Widths > 32. 164 | For C99, the `uint_fastN_t` data types are used or long long, if the Width is 165 | unknown. 166 | 167 | 168 | ## [v0.8.1] - 2013-05-17 169 | 170 | ### Changed 171 | - Updated [qm.py from GitHub][qm github]. 172 | - Explicitly stated that the output of pycrc is not considered a substantial 173 | part of the code of pycrc in `README.md`. 174 | - Re-organised the symbol table: grouped the code by functionality, not by 175 | algorithm. 176 | - The input to the CRC routines can now be bytes or strings. 177 | - Minor formatting change in the manpage. 178 | - Better python3 compatibility. 179 | - Added the files generated with the `bwe` algorithm to `check_files.sh`. 180 | 181 | ### Fixed 182 | - Fixed a bug in the handling of hexstrings in Python3. 183 | Thanks to Matthias Kuehlewein. 184 | - Added `--check-hexstring` to the tests. 185 | - Remove obsolete and unused `direct` parameter. 186 | Merge pull request #2 from smurfix/master. 187 | Thanks to Matthias Urlichs. 188 | - Don't recurse into main() when an unknown algorithm is selected. 189 | Merge pull request #2 from smurfix/master. 190 | Thanks to Matthias Urlichs. 191 | 192 | 193 | ## [v0.8] - 2013-01-04 194 | 195 | ### Added 196 | - Merged (private) bitwise-expression branch to main. 197 | This adds the highly experimental `bitwise-expression` (`bwe`) code generator 198 | target, which might one day be almost as fast as the table-driven code but 199 | much smaller. 200 | At the moment the generated code is bigger and slower than any other 201 | algorithm, so use at your own risk. 202 | 203 | ### Changed 204 | - Now it is possible to specify the `--include` option multiple times. 205 | - Completely revisited and reworked the documentation. 206 | - Updated the command line help screen with more useful command descriptions. 207 | - Removed the `-*- coding: Latin-1 -*-` string. 208 | - Updated the copyright year to 2013. 209 | - Renamed abbreviations to `bbb`, `bbf`, `tbl`. 210 | - It is possible now to abbreviate the algorithms (`bbb` for `bit-by-bit`, 211 | `bbf` for `bit-by-bit-fast` and `tbl` for `table-driven`). 212 | Added the list of supported CRC models to the error message when an 213 | unknown model parameter was supplied. 214 | - Documented the possibility to abbreviate the algorithms. Minor 215 | improvements in the documentation. 216 | - Added a note to `README.md` that version 0.7.10 of pycrc is the last one 217 | known to work with Python 2.4. 218 | - Updated a link to the list of CRC models. 219 | - Renamed i`README` to `README.md`. 220 | - Updated link to the [Catalogue of parametrised CRC algorithms][crc catalogue]. 221 | 222 | 223 | ## [v0.7.11] - 2012-10-20 224 | 225 | ### Changed 226 | - Improved Python3 compatibility. pycrc now requires Python 2.6 or later. 227 | - Added a test for compiled standard models. 228 | 229 | ### Fixed 230 | - Fixed a wrong `check` value of the `crc-64-jones` model. 231 | - Don't use `snprintf()` with `c89` code, changed to `sprintf()`. 232 | - Deleted `test.sh` shell script and replaced it with `test.py`. 233 | 234 | 235 | ## [v0.7.10] - 2012-02-13 236 | 237 | ### Added 238 | - Added the models `crc-12-3gpp`, `crc-16-genibus`, `crc-32-bzip2` and `crc-64-xz`. 239 | Taken from [Greg Cook's Catalogue of parametrised CRC algorithms][crc catalogue]. 240 | 241 | ### Changed 242 | - Bad-looking C code generated; make sure the `bit-by-bit`(`-fast`) code does not 243 | contain two instructions on one line. Thanks to "intgr" for the fix. 244 | - Some small code clean-up: use `set()` when appropriate. 245 | 246 | ### Fixed 247 | - Fixed a mistake in the man page that still used the old model name 248 | `crc-32mpeg` instead of `crc-32-mpeg`. Thanks to Marek Erban. 249 | 250 | 251 | ## [v0.7.9] - 2011-12-08 252 | 253 | ### Fixed 254 | - Fixed a bug in the generated C89 code that included `stdint.h`. 255 | Thanks to Frank (ftheile). 256 | Closes issue 3454356. 257 | - Fixed a bug in the generated C89 code when using a 64 bit CRC. 258 | - Using the `--verbose` option made pycrc quit without error message. 259 | 260 | 261 | ## [v0.7.8] - 2011-07-10 262 | 263 | ### Changed 264 | - When generating C code for the C89 or ANSI standard, don't include `stdint.h`. 265 | This closes issue 3338930 266 | - If no output file name is given while generating a C file, then pycrc will 267 | `#include` a hypothetical `pycrc.h` file instead of a `stdout.h` file. 268 | Also, added a comment on that line to make debugging easier. 269 | Closes issue 3325109. 270 | - Removed unused variable `this_option_optind` in the generated option parser. 271 | 272 | 273 | ## [v0.7.7] - 2011-02-11 274 | 275 | ### Changed 276 | - Updated the copyright year. 277 | Fixed some coding style issues found by `pylint` and `pychecker`. 278 | 279 | ### Fixed 280 | - Substituted the deprecated function `atoi()` with `int()`. Closes issue 3136566. 281 | Thanks to Tony Smith. 282 | - Updated the documentation using Windows-style calls to the Python interpreter. 283 | 284 | 285 | ## [v0.7.6] - 2010-10-21 286 | 287 | ### Changed 288 | - Rewritten macro parser from scratch. Simplified the macro language. 289 | - Changed a simple division (/) to a integer division (//) for Python3 290 | compatibility. 291 | 292 | ### Fixed 293 | - Fixed a minor bug in the command line parsing of the generated main function. 294 | 295 | 296 | ## [v0.7.5] - 2010-03-28 297 | 298 | ### Added 299 | - Python implementation of the `table-driven` algorithm can handle widths less 300 | than 8. 301 | - Suppressed warnings of unused cfg structure on partially defined models. 302 | 303 | ### Removed 304 | - Removed the half-baken and confusing `--direct` option. 305 | 306 | ### Changed 307 | - C/C++ code can now be generated for the table-driven algorithm with widths 308 | that are not byte-aligned or less than 8. 309 | This feature was heavily inspired by a similar feature in Danjel McGougan's 310 | [Universal Crc][universal crc]. 311 | - __API change__: introduced new variable `crc_shift`, member of the `crc_cfg_t` 312 | structure, which must be initialised manually when the width was undefined 313 | during C/C++ code generation. 314 | - Minor code cleanup. 315 | 316 | 317 | ## [v0.7.4] - 2010-01-24 318 | 319 | ### Added 320 | - Added `crc-16-modbus`. Closes issue 2896611. 321 | 322 | ### Changed 323 | - Changed the `xor-in` value of the `crc-64-jones` model. 324 | - Set xmodem parameters equal to the `zmodem` parameters. 325 | - Generate more uniform error messages. 326 | - Added a warning for even polynomials. 327 | 328 | ### Fixed 329 | - Fix for unused variable argv. 330 | Closes issue 2893224. Thanks to Marko von Oppen. 331 | 332 | 333 | ## [v0.7.3] - 2009-10-25 334 | 335 | ### Added 336 | - Added `crc-64-jones` CRC model. Thanks to Waterspirit. 337 | 338 | ### Changed 339 | - Renamed `crc-32mpeg` to `crc-32-mpeg`. 340 | 341 | 342 | ## [v0.7.2] - 2009-09-30 343 | 344 | ### Fixed 345 | - Fixed a bug that caused the result of the Python `table-driven` code not 346 | being evaluated at all. 347 | Closes issue 2870630. Thanks to Ildar Muslukhov. 348 | 349 | 350 | ## [v0.7.1] - 2009-04-05 351 | 352 | ### Added 353 | - Added `crc-32mpeg`. Thanks to Thomas Edwards. 354 | 355 | 356 | ## [v0.7] - 2009-02-27 357 | 358 | ### Added 359 | - Added the `--direct` option. 360 | - Added `--check-hexstring` option. Closes issue 2545183. 361 | Thanks to Arnim Littek. 362 | - Added a check for extra arguments on the command line. 363 | Closes issue 2545185. Thanks to Arnim Littek. 364 | 365 | ### Changed 366 | - Added one more example in the documentation. 367 | 368 | 369 | ## [v0.6.7] - 2008-12-11 370 | 371 | ### Changed 372 | - Run Python's 2to3 script on all files. 373 | Check the code on a x64 platform. 374 | - Fixed a bug that raised an exception when an unknown model was selected. 375 | 376 | 377 | ## [v0.6.6] - 2008-06-05 378 | 379 | ### Changed 380 | - New license for the documentation: 381 | [Creative Commons Attribution-Share Alike 3.0 Unported License][CC-BY-SA-3.0]. 382 | 383 | ### Fixed 384 | - Fixed a bug in the `print_params()` function. Closes issue 1985197. 385 | Thanks to Artur Lipowski. 386 | 387 | 388 | ## [v0.6.5] - 2008-03-03 389 | 390 | ### Added 391 | - Added dallas-1-wire 8 bit CRC. 392 | - Added `r-crc-16 model` (DECT (cordless digital standard) packets A-field 393 | according to ETSI EN 300 175-3 v2.1.1). 394 | Thanks to "raimondo". 395 | - Added `--crc-type` and `--include-file` options. 396 | - Added new file to handle CRC models. 397 | 398 | ### Changed 399 | - Added extern "C" declaration to the generated C header file. 400 | Thanks to Nathan Royer. 401 | - Changed the API to take the CRC model direct as parameter. Deleted the need 402 | for an obscure `opt` object. 403 | 404 | ### Fixed 405 | - Fixed a problem with the generated code for bit-by-bit-fast algorithms. 406 | Thanks to Hans Bacher. 407 | 408 | 409 | ## [v0.6.4] - 2007-12-05 410 | 411 | ### Fixed 412 | - Fixed a bug in the code generator for the `table-driven` 413 | algorithm. Thanks to Tom McDermott. Closes issue 1843774 414 | 415 | 416 | ## [v0.6.3] - 2007-10-13 417 | 418 | ### Added 419 | - Added new models: `crc-5`, `crc-15`, `crc-16-usb`, `crc-24`, `crc-64`. 420 | The new models are taken from Ray Burr's CrcMoose. 421 | 422 | ### Fixed 423 | - Fixed some portability problems in the generated code. 424 | Thanks to Helmut Bauer. Closes issue 1812894. 425 | - The option `--check-file` works now with `--width` < 8. Closes issue 1794343. 426 | - Removed unnecessary restriction on the width when using the `bit-by-bit-fast` 427 | algorithm. Closes issue 1794344. 428 | 429 | 430 | ## [v0.6.2] - 2007-08-25 431 | 432 | ### Changed 433 | - Simplified the table-driven code. Closes issue 1727128. 434 | - Changed the macro language syntax to a better format. 435 | - Renamed `crc_code_gen.py` to `crc_parser.py`. 436 | - Documented the usage of the `crc_*` modules. 437 | 438 | ### Fixed 439 | - The parameter to `--check-string` was ignored. Closes issue 1781637. 440 | 441 | 442 | ## [v0.6.1] - 2007-08-12 443 | 444 | ### Added 445 | - Added test for C89 compilation. 446 | - Added a test case to loop over the input bytes one by one. 447 | 448 | ### Removed 449 | - Deleted obsolete options. 450 | 451 | ### Changed 452 | - Tidied up the documentation. 453 | Code cleanup. 454 | 455 | ### Fixed 456 | - Bugfix in the source code generator for C89: 457 | Compilation error due to mismatch of parameters in the `crc_finalize` 458 | funtion. 459 | - Changes related to 919107: Code generator includes `reflect()` function even 460 | if not needed. 461 | - Fixed a typo in the C89 source code generator. 462 | Thanks to Helmut Bauer. 463 | 464 | 465 | ## [v0.6] - 2007-05-21 466 | 467 | ### Added 468 | - Added the `--std` option to generate C89 (ANSI) compliant code. 469 | - Added a new check to the test script which validate all possible 470 | combination of undefined parameters. 471 | - Made the generated main function cope with command line arguments. 472 | - Added the `--generate` table option. 473 | - Added a template engine for the code generation. Split up `pycrc.py` into 474 | smaller modules. 475 | - Added obsolete options again tor legacy reasons. 476 | - Added a better handling of the `--model` parameter. 477 | 478 | ### Changed 479 | - Reduced the size of the symbol table by re-arranging items. 480 | - Changed licence to the MIT licence. This makes the additional clause for 481 | generated source code obsolete. 482 | Changed all command line options with underscores to hyphen (e.g. 483 | `table_driven` becomes `table-driven`). 484 | Added the option `--generate` which obsoletes the old options `--generate_c` 485 | `--generate_h` etc. 486 | 487 | 488 | ## [v0.5] - 2007-03-25 489 | 490 | ### Fixed 491 | - Fixed bug 1686404: unhandled exception when called with both options 492 | `--table_idx_width` and `--check_file`. 493 | - Eliminated useless declaration of `crc_reflect`, when not used. 494 | - Corrected typos in the documentation. 495 | 496 | 497 | ## [v0.4] - 2007-01-26 498 | 499 | ### Added 500 | - Added more parameter sets (now supported: `crc-8`, `crc-16`, `citt`, `kermit`, 501 | `x-25`, `xmodem`, `zmodem`, `crc-32`, `crc-32c`, `posix`, `jam`, `xfer`) from 502 | [Greg Cook's Catalogue of parametrised CRC algorithms][crc catalogue]. 503 | - Added Doxygen documentation strings to the functions. 504 | - Added the `--symbol_prefix` option. 505 | - Added the `--check_file` option. 506 | - Added a non-regression test on the generated C source. 507 | 508 | ### Changed 509 | - Eliminated needless documentation of not generated functions. 510 | - Many corrections to the manual (thanks Francesca) Documented the new 511 | parameter sets. 512 | - Added some new tests, disabled the random loop. 513 | 514 | ### Fixed 515 | - Corrected many typos and bad phrasing (still a lot to do) Documented the 516 | `--symbol_prefix` option. 517 | 518 | 519 | ## [v0.3] - 2007-01-14 520 | 521 | ### Added 522 | - First public release pycrc v0.3 523 | 524 | 525 | 526 | [Unreleased]: https://github.com/tpircher/pycrc 527 | [v0.10.0]: https://github.com/tpircher/pycrc/releases/tag/v0.10.0 528 | [v0.9.3]: https://github.com/tpircher/pycrc/releases/tag/v0.9.3 529 | [v0.9.2]: https://github.com/tpircher/pycrc/releases/tag/v0.9.2 530 | [v0.9.1]: https://github.com/tpircher/pycrc/releases/tag/v0.9.1 531 | [v0.9]: https://github.com/tpircher/pycrc/releases/tag/v0.9 532 | [v0.8.3]: https://github.com/tpircher/pycrc/releases/tag/v0.8.3 533 | [v0.8.2]: https://github.com/tpircher/pycrc/releases/tag/v0.8.2 534 | [v0.8.1]: https://github.com/tpircher/pycrc/releases/tag/v0.8.1 535 | [v0.8]: https://github.com/tpircher/pycrc/releases/tag/v0.8 536 | [v0.7.11]: https://github.com/tpircher/pycrc/releases/tag/v0.7.11 537 | [v0.7.10]: https://github.com/tpircher/pycrc/releases/tag/v0.7.10 538 | [v0.7.9]: https://github.com/tpircher/pycrc/releases/tag/v0.7.9 539 | [v0.7.8]: https://github.com/tpircher/pycrc/releases/tag/v0.7.8 540 | [v0.7.7]: https://github.com/tpircher/pycrc/releases/tag/v0.7.7 541 | [v0.7.6]: https://github.com/tpircher/pycrc/releases/tag/v0.7.6 542 | [v0.7.5]: https://github.com/tpircher/pycrc/releases/tag/v0.7.5 543 | [v0.7.4]: https://github.com/tpircher/pycrc/releases/tag/v0.7.4 544 | [v0.7.3]: https://github.com/tpircher/pycrc/releases/tag/v0.7.3 545 | [v0.7.2]: https://github.com/tpircher/pycrc/releases/tag/v0.7.2 546 | [v0.7.1]: https://github.com/tpircher/pycrc/releases/tag/v0.7.1 547 | [v0.7]: https://github.com/tpircher/pycrc/releases/tag/v0.7 548 | [v0.6.7]: https://github.com/tpircher/pycrc/releases/tag/v0.6.7 549 | [v0.6.6]: https://github.com/tpircher/pycrc/releases/tag/v0.6.6 550 | [v0.6.5]: https://github.com/tpircher/pycrc/releases/tag/v0.6.5 551 | [v0.6.4]: https://github.com/tpircher/pycrc/releases/tag/v0.6.4 552 | [v0.6.3]: https://github.com/tpircher/pycrc/releases/tag/v0.6.3 553 | [v0.6.2]: https://github.com/tpircher/pycrc/releases/tag/v0.6.2 554 | [v0.6.1]: https://github.com/tpircher/pycrc/releases/tag/v0.6.1 555 | [v0.6]: https://github.com/tpircher/pycrc/releases/tag/v0.6 556 | [v0.5]: https://github.com/tpircher/pycrc/releases/tag/v0.5 557 | [v0.4]: https://github.com/tpircher/pycrc/releases/tag/v0.4 558 | [v0.3]: https://github.com/tpircher/pycrc/releases/tag/v0.3 559 | 560 | [pycrc home]: https://pycrc.org 561 | [pycrc github]: https://github.com/tpircher/pycrc 562 | [pycrc issues]: https://github.com/tpircher/pycrc/issues 563 | [crc catalogue]: http://regregex.bbcmicro.net/crc-catalogue.htm 564 | [universal crc]: http://mcgougan.se/universal_crc/ 565 | [qm github]: https://github.com/tpircher/quine-mccluskey 566 | [CC-BY-SA-3.0]: https://creativecommons.org/licenses/by-sa/3.0/ 567 | [CC-BY-SA-4.0]: https://creativecommons.org/licenses/by-sa/4.0/ 568 | -------------------------------------------------------------------------------- /doc/pycrc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ]> 27 | 28 | 29 | 30 | 31 | pycrc 32 | pycrc 33 | &project_version; 34 | 35 | 36 | &author_firstname; 37 | &author_surname; 38 | 39 | Author of pycrc and this manual page. 40 | &author_email; 41 | 42 | &date; 43 | 44 | 45 | 46 | pycrc 47 | 1 48 | 49 | 50 | 51 | pycrc 52 | a free, easy to use Cyclic Redundancy Check (CRC) calculator and C source code generator. 53 | 54 | 55 | 56 | 57 | python pycrc.py 58 | OPTIONS 59 | 60 | 61 | 62 | 63 | Description 64 | 65 | pycrc 66 | is a CRC reference implementation in Python and a C source code generator for parametrised CRC models. 67 | The generated C source code can be optimised for simplicity, 68 | speed or small memory footprint, as required on small embedded systems. 69 | 70 | The following operations are implemented: 71 | 72 | 73 | calculate the checksum of a string (ASCII or hex) 74 | 75 | 76 | calculate the checksum of a file 77 | 78 | 79 | generate the header and source files for a C implementation. 80 | 81 | 82 | 83 | 84 | pycrc supports the following variants of the CRC algorithm: 85 | 86 | 87 | &bit-by-bit; or &bbb;: 88 | the basic algorithm which operates individually on every bit of the augmented message 89 | (i.e. the input data with &width; zero bits added at the end). 90 | This algorithm is a straightforward implementation of the basic polynomial division and 91 | is the easiest one to understand, but it is also the slowest one among all possible 92 | variants. 93 | 94 | 95 | 96 | &bit-by-bit-fast; or &bbf;: 97 | a variation of the simple &bit-by-bit; algorithm. 98 | This algorithm still iterates over every bit of the message, but does not augment 99 | it (does not add &width; zero bits at the end). 100 | It gives the same result as the &bit-by-bit; method by 101 | carefully choosing the initial value of the algorithm. 102 | This method might be a good choice for embedded platforms, where code space is more 103 | important than execution speed. 104 | 105 | 106 | 107 | &table-driven; or &tbl;: 108 | the standard table driven algorithm. 109 | This is the fastest variant because it operates on one byte at a time, as opposed to one 110 | bit at the time. 111 | This method uses a look-up table (usually of 256 elements), which might not be acceptable 112 | for small embedded systems. The number of elements in the look-up table can be reduced 113 | with the command line switch. 114 | The value of 4 bits for the table index (16 elements in the look-up table) can be a good 115 | compromise between execution speed and code size. 116 | 117 | 118 | The option enables a variant of the &table-driven; 119 | algorithm that operates on 32 bits of data or more at a time rather than 8 bits. 120 | This can dramatically speed-up the calculation of the CRC, at the cost of 121 | increased code and data size. 122 | Note: this option is experimental and not well-tested. 123 | Check your results and please raise bugs if you find problems. 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | Options 132 | 133 | 134 | 135 | 136 | 137 | 138 | show the program version number and exit. 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | show this help message and exit. 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | be more verbose; in particular, print the value of the parameters and the chosen model to stdout. 158 | 159 | 160 | 161 | 162 | STRING 163 | 164 | 165 | calculate the checksum of a string (default: 123456789). If the string contains non-ASCII characters then it will be UTF-8 decoded. 166 | 167 | 168 | 169 | 170 | STRING 171 | 172 | 173 | calculate the checksum of a hexadecimal number string. 174 | 175 | 176 | 177 | 178 | FILE 179 | 180 | 181 | calculate the checksum of a file. If the file contains non-ASCII characters then it will be UTF-8 decoded. 182 | 183 | 184 | 185 | 186 | CODE 187 | 188 | 189 | generate C source code; choose the type from {h, 190 | c, c-main, table}. 191 | 192 | 193 | 194 | 195 | STD 196 | 197 | 198 | specify the C dialect of the generated code from {C89, ANSI, C99}. 199 | 200 | 201 | 202 | 203 | ALGO 204 | 205 | 206 | choose an algorithm from {bit-by-bit, bbb, 207 | bit-by-bit-fast, bbf, 208 | table-driven, tbl, 209 | all}. 210 | 211 | 212 | 213 | 214 | MODEL 215 | 216 | 217 | choose a parameter set from 218 | {crc-5, 219 | crc-8, 220 | dallas-1-wire, 221 | crc-12-3gpp, 222 | crc-15, 223 | crc-16, 224 | crc-16-usb, 225 | crc-16-modbus, 226 | crc-16-genibus, 227 | crc-16-ccitt, 228 | r-crc-16, 229 | kermit, 230 | x-25, 231 | xmodem, 232 | zmodem, 233 | crc-24, 234 | crc-32, 235 | crc-32c, 236 | crc-32-mpeg, 237 | crc-32-bzip2, 238 | posix, 239 | jam, 240 | xfer, 241 | crc-64, 242 | crc-64-jones, 243 | crc-64-xz}. 244 | 245 | 246 | 247 | 248 | NUM 249 | 250 | 251 | use NUM bits in the &poly;. 252 | 253 | 254 | 255 | 256 | HEX 257 | 258 | 259 | use HEX as &poly;. 260 | 261 | 262 | 263 | 264 | BOOL 265 | 266 | 267 | reflect the octets in the input message. 268 | 269 | 270 | 271 | 272 | HEX 273 | 274 | 275 | use HEX as initial value. 276 | 277 | 278 | 279 | 280 | BOOL 281 | 282 | 283 | reflect the resulting checksum before applying the &xor_out; value. 284 | 285 | 286 | 287 | 288 | HEX 289 | 290 | 291 | xor the final CRC value with HEX. 292 | 293 | 294 | 295 | 296 | NUM 297 | 298 | 299 | speed-up the &table-driven; calculation by operating on 300 | NUM octets of data rather than a 301 | single octet at a time. 302 | NUM must be one of the values 303 | {4, 8, 304 | 16}. 305 | 306 | 307 | 308 | 309 | NUM 310 | 311 | 312 | use NUM bits to index the CRC table; 313 | NUM must be one of the values 314 | {1, 2, 315 | 4, 8}. 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | override any errors about possibly unsuitable 324 | polynoms. pycrc does not allow even polynoms or 325 | polynoms that are wider than &width;. Use this option 326 | to override the error, if you know what you are 327 | doing. 328 | 329 | 330 | 331 | 332 | STRING 333 | 334 | 335 | when generating source code, use STRING 336 | as prefix to the exported C symbols. 337 | 338 | 339 | 340 | 341 | STRING 342 | 343 | 344 | when generating source code, use STRING as crc_t type. 345 | 346 | 347 | 348 | 349 | FILE 350 | 351 | 352 | when generating source code, include also FILE as header file. 353 | This option can be specified multiple times. 354 | 355 | 356 | 357 | 358 | FILE 359 | 360 | 361 | FILE 362 | 363 | 364 | write the generated code to FILE instead of stdout. 365 | 366 | 367 | 368 | 369 | 370 | 371 | The CRC Parametric Model 372 | 373 | The parametric model follows Ross N. Williams' convention described in 374 | A Painless Guide to CRC Error Detection Algorithms, 375 | often called the Rocksoft Model. 376 | Since most people are familiar with this kind of parameters, pycrc follows this convention, described as follows: 377 | 378 | 379 | &width; 380 | 381 | 382 | The number of significant bits in the CRC &poly;, 383 | excluding the most significant 1. 384 | This will also be the number of bits in the final CRC result. 385 | In previous versions of pycrc only multiples of 8 could be used as 386 | &width; for the &table-driven; algorithm. 387 | As of version 0.7.5 any value is accepted for &width; for all algorithms. 388 | 389 | 390 | 391 | 392 | &poly; 393 | 394 | 395 | The unreflected polynomial of the CRC algorithm. 396 | 397 | 398 | The &poly; may be specified in its standard form, 399 | i.e. with bit &width;+1 set to 1, but the most significant 400 | bit may also be omitted. 401 | For example, both numbers 0x18005 and 0x8005 are accepted for a 16-bit 402 | &poly;. 403 | 404 | 405 | Most polynomials used in real world applications are odd (the least significant 406 | bit is 1), but there are some good even ones. 407 | pycrc allows the use of even polynomials with the 408 | option. 409 | Some even polynomials may yield incorrect checksums depending on the used algorithm. 410 | Use at your own risk and if at all possible use a well-known MODEL above. 411 | 412 | 413 | 414 | 415 | &reflect_in; 416 | 417 | 418 | Reflect the octets of the message before processing them. 419 | 420 | 421 | A word is reflected or reversed by flipping its bits around the 422 | mid-point of the word. 423 | The most significant bit of the word is moved to the least significant position, 424 | the second-most significant bit is moved to the second-least significant position 425 | and so on. 426 | The reflected value of 0xa2 (10100010b) is 0x45 (01000101b), for example. 427 | 428 | 429 | Some CRC algorithms can be implemented more efficiently in a bit reversed version, 430 | that's why many of the standard CRC models use reflected input octets. 431 | 432 | 433 | 434 | 435 | &xor_in; 436 | 437 | 438 | The initial value (usually all 0 or all 1) for algorithms which operate on the 439 | non-augmented message, that is, any algorithm other than the 440 | &bit-by-bit; one. 441 | This value can be interpreted as a value which will be XOR-ed into the CRC register 442 | after &width; iterations of the 443 | &bit-by-bit; algorithm. 444 | This implies that the simple &bit-by-bit; algorithm must 445 | calculate the initial value using some sort of reverse CRC algorithm on the 446 | &xor_in; value. 447 | 448 | 449 | 450 | 451 | &reflect_out; 452 | 453 | 454 | Reflect the final CRC result. This operation takes place before XOR-ing the final CRC 455 | value with the &xor_out; parameter. 456 | 457 | 458 | 459 | 460 | &xor_out; 461 | 462 | 463 | A value (usually all bits 0 or all 1) which will be XOR-ed to the final CRC value. 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | This value is not exactly a parameter of a model but it is sometimes given together 472 | with the Rocksoft Model parameters. 473 | It is the CRC value of the parametrised model over the string 474 | 123456789 and 475 | can be used as a sanity check for a particular CRC implementation. 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | Code generation 485 | 486 | In the default configuration, the generated code is strict ISO C99. 487 | A minimal set of three functions are defined for each algorithm: 488 | crc_init(), crc_update() and crc_finalize(). 489 | Depending on the number of parameters given to pycrc, a different interface will be defined. 490 | A fully parametrised model has a simpler API, while the generated code for a runtime-specified 491 | implementation requires a pointer to a configuration structure as first parameter to all functions. 492 | 493 | 494 | The generated source code uses the type crc_t, which is used throughout the code 495 | to hold intermediate results and also the final CRC value. 496 | It is defined in the generated header file and its type may be overridden with the 497 | option. 498 | 499 | 500 | Fully parametrised models 501 | 502 | The prototypes of the CRC functions are normally generated by pycrc using the 503 | --generate h option. 504 | The CRC functions for a fully parametrised model will look like: 505 | 506 | 507 | 508 | #include <stdlib.h> 509 | typedef uint16_t crc_t; /* pycrc will use an appropriate size here */ 510 | 511 | 512 | 513 | crc_t crc_init 514 | 515 | 516 | 517 | 518 | 519 | crc_t crc_update 520 | crc_t crc 521 | const unsigned char *data 522 | size_t data_len 523 | 524 | 525 | 526 | 527 | crc_t crc_finalize 528 | crc_t crc 529 | 530 | 531 | 532 | 533 | The code snippet below shows how to use the generated functions. 534 | 535 | #include "pycrc_generated_crc.h" 536 | #include <stdio.h> 537 | 538 | int main(void) 539 | { 540 | static const unsigned char str1[] = "1234"; 541 | static const unsigned char str2[] = "56789"; 542 | crc_t crc; 543 | 544 | crc = crc_init(); 545 | crc = crc_update(crc, str1, sizeof(str1) - 1); 546 | crc = crc_update(crc, str2, sizeof(str2) - 1); 547 | /* more calls to crc_update... */ 548 | crc = crc_finalize(crc); 549 | 550 | printf("0x%lx\n", (long)crc); 551 | return 0; 552 | } 553 | 554 | 555 | 556 | 557 | 558 | Models with runtime-configurable parameters 559 | 560 | When the model is not fully defined then the missing parameters are stored in a structure of 561 | type crc_cfg_t. 562 | If a CRC function requires a value from the crc_cfg_t structure, then the first 563 | function argument is always a pointer to that structure. 564 | All fields of the configuration structure must be properly initialised before the first call 565 | to any CRC function. 566 | 567 | 568 | If the &width; was not specified when the code was generated, then 569 | the crc_cfg_t structure will contain three more fields: 570 | msb_mask, crc_mask and crc_shift. 571 | They are defined for performance reasons and must be initialised to the value given next to the 572 | field definition. 573 | 574 | 575 | For example, a completely undefined CRC implementation will generate a crc_cfg_t 576 | structure as below: 577 | 578 | typedef struct { 579 | unsigned int width; 580 | crc_t poly; 581 | bool reflect_in; 582 | crc_t xor_in; 583 | bool reflect_out; 584 | crc_t xor_out; 585 | 586 | // internal parameters 587 | crc_t msb_mask; // initialise as (crc_t)1u << (cfg->width - 1) 588 | crc_t crc_mask; // initialise as (cfg->msb_mask - 1) | cfg->msb_mask 589 | unsigned int crc_shift; // initialise as cfg->width < 8 ? 8 - cfg->width : 0 590 | } crc_cfg_t; 591 | 592 | 593 | 594 | msb_mask is a bitmask with the most significant bit of a 595 | &width; bits wide data type set to 1. 596 | 597 | crc_mask is a bitmask with all bits of a 598 | &width; bits wide data type set to 1. 599 | 600 | crc_shift is a shift counter that is used when 601 | &width; is less than 8. 602 | It is the number of bits to shift the CRC register to align its top bit to a byte boundary. 603 | 604 | 605 | 606 | The file test/main.c in the source package of pycrc 607 | contains a fully featured example of how to use the generated source code. 608 | A shorter, more compact main() function can be generated with the 609 | --generate c-main option. 610 | This second variant is the better option as it will always output valid code when 611 | some of the CRC parameters are known and some are unknown during code generation. 612 | 613 | 614 | 615 | 616 | Examples 617 | 618 | 619 | 620 | Calculate the CRC-32 checksum of the string 123456789: 621 | 622 | 623 | python pycrc.py --model crc-32 --check-string 123456789 624 | 625 | 626 | 627 | 628 | Generate the source code of the table-driven algorithm for an embedded application. 629 | 630 | 631 | The table index width of 4 bits ensures a moderate memory usage. 632 | To be precise, the size of the resulting table will be 16 * sizeof(crc_t). 633 | 634 | 635 | python pycrc.py --model crc-16 --algorithm table-driven --table-idx-width 4 --generate h -o crc.h 636 | 637 | 638 | python pycrc.py --model crc-16 --algorithm table-driven --table-idx-width 4 --generate c -o crc.c 639 | 640 | 641 | A variant of the c target is c-main: 642 | this target will generate a simple main() function in addition to 643 | the CRC functions: 644 | 645 | 646 | python pycrc.py --model crc-16 --algorithm table-driven --table-idx-width 4 --generate c-main -o crc.c 647 | 648 | 649 | 650 | 651 | Generate the CRC table only: 652 | 653 | 654 | python pycrc.py --model kermit --generate table -o crc-table.txt 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | See Also 664 | 665 | The homepage of pycrc is &project_homepage;. 666 | 667 | 668 | A list of common CRC models is at &project_models;. 669 | For a long list of known CRC models, see Greg Cook's 670 | Catalogue of Parameterised CRC Algorithms. 671 | 672 | 673 | 674 | 675 | Copyright 676 | 677 | This work is licensed under a 678 | Creative Commons Attribution-ShareAlike 4.0 International. 679 | 680 | 681 | 682 | --------------------------------------------------------------------------------