📑

pycrc.py でCRC計算コードを生成する

2021/09/25に公開

pycrc.py でCRC計算コードを生成する

CRC(=Cyclic Redundancy Code) は誤り検出符号の一種だが、用途によっていろんな方法が存在する。

CRC計算によっては、計算に必要なパラメータが異なり、必要になるたびに自前で実装するのは面倒だし、既存のコードを利用するにもライセンスに気を付ける必要がある。

pycrcはCRC計算を行うC言語のソースコードを生成してくれるジェネレータである。

引数を指定して実行することで、Cのソースコードと対応するヘッダを生成してくれる。

https://pycrc.org/ から tar.gz または zip ファイルをダウンロードして展開する。

注意

ややこしいのですが、pycrc とは別に、PyCRC というモジュールもある。

この記事で紹介するのは、pycrcでこちらは MIT ライセンスである。
PyCRC は GPLv3 ライセンスである。PyCRC に関しては調査してない。

コード生成

CRC-32 の計算コードを生成する

python3 の場合

python3 pycrc.py --model crc-32 --algorithm table-driven --generate h -o crc.h
python3 pycrc.py --model crc-32 --algorithm table-driven --generate c -o crc.c

このコードでは python の binascii.crc32 および zlib の crc32 と同じ CRC の計算コードを生成する。

参考: python の場合

python pycrc.py --model crc-32 --algorithm table-driven --generate h -o crc.h
python pycrc.py --model crc-32 --algorithm table-driven --generate c -o crc.c

または

./pycrc.py --model crc-32 --algorithm table-driven --generate h -o crc.h
./pycrc.py --model crc-32 --algorithm table-driven --generate c -o crc.c

生成したコードを利用する

生成したヘッダファイルにサンプルコードが記載されている、また tutorial の Write the main file にもあるとおり、crc_init で初期化して、crc_update で入力データを与えて、crc_finalize で最終結果を確定する。

https://github.com/m-tmatma/pycrc-test/blob/master/main.c

#include <stdio.h>
#include <string.h>
#include "crc.h"

crc_t get_crc(const unsigned char* data, size_t data_len)
{
    crc_t crc = crc_init();
    crc = crc_update(crc, data, data_len);
    crc = crc_finalize(crc);
    return crc;
}

int main(void)
{
    const char * data[] = {
        "ABC",
        "12345",
    };

    for (int i = 0; i < sizeof(data) / sizeof(data[0]); i++)
    {
        printf("%s: %08lx\n", data[i],
            get_crc(data[i], strlen(data[i]))
        );
    }
    return 0;
}

関数の prefix

通常は生成されるコードの関数名は crc_initcrc_update および、crc_finalize だが、--symbol-prefix= を指定すると関数名につけるプレフィックスを変更できる。

以下のように --symbol-prefix=HogeHoge_ を指定すると、

python3 pycrc.py --model crc-32 --algorithm table-driven --generate c -o crc.c --symbol-prefix=HogeHoge_
python3 pycrc.py --model crc-32 --algorithm table-driven --generate h -o crc.h --symbol-prefix=HogeHoge_

HogeHoge_initHogeHoge_updateHogeHoge_finalize の関数名で生成される。

デフォルトでは、crc_initcrc_updatecrc_finalize が生成されるので --symbol-prefix= の引数を省略した場合は、--symbol-prefix=crc_ が指定されているのと同じ動作をすることになる。

生成したコードのライセンス

https://pycrc.org/faq.html#code-ownership には

The sloppy answer is: you are free to do whatever you like
with the generated code, as long as you don't blame the author
for any malfunction or damage caused by the program.

However, as a courtesy, please keep the line that states
that the code was generated by pycrc.

とある。

実証コード

https://github.com/m-tmatma/pycrc-test
に動作するコードを登録している。

pycrc.py のヘルプ

以下は pycrc.py のヘルプです。

$ python3 pycrc.py --help
Usage: python pycrc.py [OPTIONS]

To calculate the checksum of a string or hexadecimal data:
    python pycrc.py [model] --check-string "123456789"
    python pycrc.py [model] --check-hexstring "313233343536373839"

To calculate the checksum of a file:
    python pycrc.py [model] --check-file filename

To generate the C source code and write it to filename:
    python pycrc.py [model] --generate c -o filename

The model can be defined either with the --model switch or by specifying each
of the following parameters:
    --width --poly --reflect-in --xor-in --reflect-out --xor-out

Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  -v, --verbose         be more verbose; print the value of the parameters and
                        the chosen model to stdout
  --check-string=STRING
                        calculate the checksum of a string (default:
                        '123456789')
  --check-hexstring=STRING
                        calculate the checksum of a hexadecimal number string
  --check-file=FILE     calculate the checksum of a file
  --generate=CODE       generate C source code; choose the type from {h, c,
                        c-main, table}
  --std=STD             choose the C dialect of the generated code from {C89,
                        ANSI, C99}
  --algorithm=ALGO      choose an algorithm from {bit-by-bit, bbb, bit-by-bit-
                        fast, bbf, table-driven, tbl, all}
  --model=MODEL         choose a parameter set from {crc-5, crc-8,
                        dallas-1-wire, crc-12-3gpp, crc-15, crc-16,
                        crc-16-usb, crc-16-modbus, crc-16-genibus,
                        crc-16-ccitt, r-crc-16, kermit, x-25, xmodem, zmodem,
                        crc-24, crc-32, crc-32c, crc-32-mpeg, crc-32-bzip2,
                        posix, jam, xfer, crc-64, crc-64-jones, crc-64-xz}
  --width=NUM           use NUM bits in the polynomial
  --poly=HEX            use HEX as polynomial
  --reflect-in=BOOL     reflect the octets in the input message
  --xor-in=HEX          use HEX as initial value
  --reflect-out=BOOL    reflect the resulting checksum before applying the
                        --xor-out value
  --xor-out=HEX         xor the final CRC value with HEX
  --slice-by=NUM        read NUM bytes at a time from the input. NUM must be
                        one of the values {4, 8, 16}
  --table-idx-width=NUM
                        use NUM bits to index the CRC table; NUM must be one
                        of the values {1, 2, 4, 8}
  --force-poly          override any errors about possibly unsuitable polynoms
  --symbol-prefix=STRING
                        when generating source code, use STRING as prefix to
                        the exported C symbols
  --crc-type=STRING     when generating source code, use STRING as crc_t type
  --include-file=FILE   when generating source code, include also FILE as
                        header file; can be specified multiple times
  -o FILE, --output=FILE
                        write the generated code to file instead to stdout

Discussion