├── 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 |
--------------------------------------------------------------------------------