Skip to main content

Multi-language CRC toolkit: generate verified code (C/C++, C#, Go, Java, Python, Rust, TypeScript, Verilog, VHDL), plus compute, detect, and reverse-engineer CRCs. Catalogue-driven, pure-stdlib, MCP server included.

Project description

crcglot

tests coverage ruff ty

A multi-language CRC toolkit. Generate verified code for C / C++ โš™๏ธ, Rust ๐Ÿฆ€, Go ๐Ÿšฆ, C# ๐Ÿ’ , Java โ˜•, Python ๐Ÿ, TypeScript ๐Ÿ”ท, Verilog ๐Ÿ”ง, and VHDL ๐Ÿ”Œ โ€” and compute, detect, and reverse-engineer CRCs, from Python or over MCP. Catalogue-driven, execution-verified, self-test embedded. Pure-stdlib package, zero runtime dependencies.

LLMs will gladly write you CRC code. It might even be right. crcglot doesn't ask you to trust the generator; it proves the output by running it: every algorithm, in every variant, in every language, is generated, compiled, and executed against the hardcoded canonical reveng catalogue vector (crc("123456789") == <check value>). More than 100 algorithms across nine languages, verified by execution rather than inspection, and every generated file embeds a self-test over four independent reference vectors so you can re-prove it on your own toolchain.

Quick start

uv tool install crcglot         # or: pip install crcglot
crcglot c crc32 file=mycrc

That's it. You now have mycrc.h and mycrc.c: a drop-in CRC-32 with a built-in _self_test() you can call to verify it reproduces four independent reference CRCs, anchored to the canonical reveng check value.

The whole model is three choices: which algorithm (crc32, crc16-modbus, โ€ฆ ; crcglot list shows the more than 100), which language (c / python / rust / vhdl / verilog / go / csharp / java / typescript), and whether you want it --fast (fastest the target supports, and the default) or --small (smallest code). crcglot figures out the implementation details, so you never have to know what "slice-by-8" is.

crcglot rust crc32 file=mycrc            # fastest Rust crc32 (the default) to mycrc.rs
crcglot c crc8 --small                   # smallest C crc8, to stdout

Installation

Tool Command Use when
uv (recommended) uv tool install crcglot You just want the crcglot CLI on PATH. Isolated install, no global pollution.
uv (as a library) uv add crcglot You're calling the generators from Python (e.g. a build script that emits CRC code into your repo).
pip pip install crcglot You don't have uv. Identical package, slower install.
pipx pipx install crcglot Same isolation story as uv tool, if pipx is what you have.

Python 3.11+, no other runtime dependencies: crcglot itself is pure stdlib. Per-target toolchains (gcc, rustc, tsx, iverilog, etc.) only matter if you want to run the generated code; the generator produces source either way.

Or use it from Python code:

from crcglot import LANGUAGES
header, source = LANGUAGES["c"].generator("crc32")

Both surfaces are documented in detail below.

What you get per language

Function Purpose
<fname>_init / _update / _finalize Streaming triple; feed data chunk by chunk
<fname> One-shot wrapper that calls the streaming triple
<fname>_self_test Verify against four independent reference CRCs on your toolchain

Every target ships a runtime-callable _self_test(): C returns 0/1; Rust / Go / C# / Java / TypeScript / Python / Verilog / VHDL return bool / boolean / bit. No #[cfg(test)] gating, so you can call it from your release build, a boot self-check, or a startup assertion.

How it's verified

The guarantee is behavioral, not structural. crcglot doesn't lint the generated code, it runs it. Three axes, fully crossed: every one of the more than 100 algorithms, in every variant the target supports (bit-by-bit, table-driven, slice-by-8), in every one of the nine languages, is executed and its output checked against the hardcoded canonical vector. Nothing ships on "the generator looks correct."

CI runs the Python-level suite on every push: every algorithm in the reveng catalogue is checked against its hardcoded canonical check value (not the catalogue's own check field, so a silent regression in the engine can't hide), and the Python generator is run end-to-end (generated, exec'd, and called on b"123456789") against the same hardcoded vectors. The slow tier on top of that compiles and executes the generated source for every algorithm in C, Rust, Go, C#, Java, TypeScript, Verilog, and VHDL via gcc / rustc / go / dotnet / javac+java / tsx (Node) / iverilog / ghdl and re-checks the runtime result: the same algorithm coverage, exercised through each real toolchain.

Every generated file also ships its own _self_test(). For a catalogue algorithm it now checks four fixed inputs โ€” the empty string, "123456789", all 256 byte values, and a 1 KiB pseudo-random pattern โ€” so the byte-table and the high-bit handling get exercised, not just the one short check string. The two large inputs are regenerated inside the self-test with a byte-at-a-time loop, so the embedded code carries no big array (it stays friendly to flash- and RAM-constrained targets). Those four reference CRCs are not computed by crcglot โ€” using the engine to grade itself would be circular. They come from two independent implementations (anycrc and crccheck) that had to agree, anchored to reveng's published value at the check string; both are dev-only tools, so the shipped package stays pure-stdlib. A custom (non-catalogue) polynomial has no independent reference, so it falls back to a single check value crcglot computed itself โ€” a weaker check that still catches a toolchain mismatch but, unlike a catalogue algorithm, can't catch an error shared by the generator and the generated code.

For every target except Python, you should call _self_test() once in your build environment, wired into a unit test, a startup assertion, or your boot self-check. Our CI proves the generator emits correct code on our reference toolchain; only running _self_test() on yours proves your compiler version, optimization flags, target endianness, and integer widths haven't introduced a subtle disagreement. Python is the exception: the interpreter that ran the CI suite is the one running your code, so the in-environment check would be redundant.

Documentation comments

The generated code is correct, and it's also documented. Every file gets a header (algorithm parameters, a copy-paste streaming example, the self-test contract) and a doc comment above each of the five functions, so a reader learns the init โ†’ update* โ†’ finalize streaming contract from the source, not from the tests. Pick the convention with --comment=<style>; plain (clean human-readable comments in each language's native syntax) is the default, and every language also has its idiomatic doc-tool style:

Language --comment styles
C / C++ โš™๏ธ plain, doxygen
C# ๐Ÿ’  plain, doxygen, docfx (XML /// <summary>)
Java โ˜• plain, doxygen, javadoc
Python ๐Ÿ plain, google, numpy, rest (Sphinx :param:)
Rust ๐Ÿฆ€ plain, rustdoc (/// + # Arguments)
Go ๐Ÿšฆ plain, godoc
TypeScript ๐Ÿ”ท plain, jsdoc (TSDoc)
Verilog ๐Ÿ”ง / VHDL ๐Ÿ”Œ plain
crcglot c crc32 --comment=doxygen        # /** @brief @param @return */
crcglot python crc32 --comment=numpy     # numpydoc underlined Parameters / Returns
crcglot rust crc32 --comment=rustdoc     # /// with # Arguments markdown

crcglot offers each language only the styles its doc-tool actually understands: crcglot rust --comment=doxygen is rejected, because Doxygen doesn't read Rust. The matrix is derived from the styles themselves; nothing hardcodes it.

Building a UI? The matrix is queryable, so a front end can populate a language โ†’ style dropdown with no hardcoding. Each record carries a machine name (the dropdown value, handed back to the generator), a human label, and a description:

from crcglot.comments import comment_styles_for_language
for s in comment_styles_for_language("python"):
    print(s.name, "|", s.label, "|", s.description)
# plain  | Plain            | Human-readable comments in the language's native syntax
# google | Google           | Google-style docstrings (Args / Returns / Note)
# numpy  | NumPy            | NumPy (numpydoc) docstrings, underlined Parameters / Returns
# rest   | reStructuredText | Sphinx field-list docstrings (:param: / :returns:)

The generators take the chosen name directly (LANGUAGES["python"].generator("crc32", comment_style="numpy")), and the same {name, label, description} records are served over MCP in the crcglot://languages.json resource (each language's comment_styles).

Why generate the docs instead of asking an LLM?

You still can: point an LLM at the output and let it write whatever prose you like; nothing here stops you. But the generated code is fully known: the parameters, the API contract, and the streaming semantics are deterministic facts, so the documentation can be deterministic too. That buys three things an LLM pass can't:

  • Reproducible. The same request produces the same comment, byte for byte. Everyone who generates crc32 gets the identical documentation: no drift, no "it phrased it differently this time," no diff churn between two runs.
  • Correct by construction, or wrong in exactly one place. The comment is rendered from the same source of truth as the code, so it can't hallucinate a parameter or misdescribe the API. And if a description is wrong, it's wrong uniformly: caught once, fixed once in the generator, and the fix reaches every output everywhere. Per-invocation LLM wrongness is the opposite: subtly different each time, and far harder to audit.
  • Free, offline, auditable. No API call, no token cost, no network; it runs in CI and on an air-gapped build. A reviewer (the class-III-medical-device kind) audits the comment generator once and can then trust every file it emits.

Layer an LLM on top when you want richer prose. The point is that the baseline everyone ships by default is deterministic, uniform, and reviewable.

Naming conventions

The generated public functions read like hand-written code in each target: Go and C# get PascalCase (Crc16ModbusUpdate), Java and TypeScript get camelCase (crc16ModbusUpdate), and C, Rust, Python, Verilog, and VHDL get snake_case (crc16_modbus_update). Those are the defaults, so a linter (govet, StyleCop, ESLint, โ€ฆ) won't flag the output. Override with --naming=<convention>; each language offers only the conventions its ecosystem actually uses (C is a free-for-all, Python and Rust are snake-only):

Language default --naming choices
C / C++ โš™๏ธ snake snake, camel, pascal
C# ๐Ÿ’  pascal pascal, camel
Go ๐Ÿšฆ pascal pascal, camel
Java โ˜• camel camel, pascal
TypeScript ๐Ÿ”ท camel camel, pascal
Rust ๐Ÿฆ€ snake snake
Python ๐Ÿ snake snake
Verilog ๐Ÿ”ง / VHDL ๐Ÿ”Œ snake snake
crcglot go crc32 --naming camel          # crc32Update instead of Crc32Update
crcglot c crc32 --naming pascal          # Crc32Update; the CRC32_H guard stays SCREAMING_SNAKE

Only the public function/method names are re-cased; header guards, table symbols, package names, and class names keep their own fixed idiom. An explicit name=NAME renames the CRC and is cased per language (so it follows --naming); symbol=NAME is the escape hatch, emitted verbatim and bypassing --naming. As with --comment, crcglot rust crc32 --naming=pascal is rejected (Rust is snake-only); the matrix is derived from LANGUAGES[code].naming, nothing hardcodes it. The same {name, label, description} records are served over MCP in crcglot://languages.json (each language's naming + default_naming).

CLI reference

crcglot <command> [options...]

crcglot list [GLOB] [--json]

Browse the catalogue. Optional GLOB filters by shell-style pattern (e.g. crc16-*). Exit code 1 if nothing matches.

crcglot list                # more than 100 algorithms
crcglot list 'crc32-*'      # just the CRC-32 family
crcglot list --json         # machine-readable list with full parameters

crcglot info <name>

Print parameters (width, poly, init, refin, refout, xorout, check, desc) for one algorithm. Exit 1 on unknown name.

crcglot info crc64-xz

crcglot detect [INPUTS...]

Brute-force identify which catalogue CRC matches a packet whose tail is the CRC. Useful for reverse-engineering unfamiliar protocols, debugging captured frames, or confirming a sample really uses the CRC you expect.

crcglot detect packet.bin                            # binary file (or '-' for stdin)
crcglot detect a.bin b.bin c.bin                     # multi-packet (intersected)
crcglot detect --text "123456789 cbf43926"           # text mode, inline
crcglot detect --text -                              # text mode, one packet per line on stdin
crcglot detect --hex "313233343536373839cbf43926"    # hex-encoded bytes
crcglot detect --algorithms 'crc16-*' packet.bin     # narrow the scan to a family
crcglot detect --match all packet.bin                # forensic: every candidate
crcglot detect --match set a.bin b.bin               # strict: succeed only on a single algorithm

--match selects the strategy: first (default; early-stop on the first hit, priority order is crc32, crc32-jamcrc, crc32-iscsi, then the rest of the catalogue), all (exhaustive forensic view), set (strict singleton: succeed only if exactly one algorithm survives across all packets). Exit 0 on match, 1 otherwise. For text packets the inferred separator + hex leader + case are reported so you can reproduce the same format via crcglot encode.

crcglot encode <algorithm> [<data>]

Build a packet by appending the CRC. Round-trip partner to detect: feed detect's (algorithm, endianness, padding) shape back to encode to rebuild a packet in the same format.

crcglot encode crc32 "123456789"                                # โ†’ "123456789 cbf43926"
crcglot encode crc32 "123456789" --sep $'\t' --leader 0x --upper # tab + "0x" + uppercase
crcglot encode crc32 --binary < data.bin > packet.bin           # binary, big-endian
crcglot encode crc32-iscsi --binary --little < data.bin         # binary, little-endian
Option Default Effect
--binary off Read stdin as bytes; write packet bytes to stdout.
--little off Little-endian CRC byte order (default: big).
--sep STR " " Text separator between data and hex.
--leader STR "" Text hex leader: "", "0x", or "0X".
--upper off Uppercase hex digits.
--fmt STR "{data}{sep}{leader}{crc}" str.format template; the four tokens may be reordered.

crcglot credits

Print acknowledgments for the upstream work crcglot builds on (also exported as crcglot.ATTRIBUTION / crcglot.ACKNOWLEDGMENTS). See Acknowledgments.

crcglot {c | csharp | go | java | python | rust | typescript | verilog | vhdl} <algorithm> [<algorithm>...] [options...] [tokens...]

Generate source code for the chosen target language. Pick your intent; crcglot picks the implementation:

Option / token Effect
--small Smallest code, zero RAM table (bit-by-bit). Works for any width.
--fast Fastest the target supports: slice-by-8 for width 32/64 on compiled targets, table-driven otherwise. The default when no variant flag is given.
--custom Use raw Rocksoft/Williams params instead of a catalogue lookup (see below).
--comment=STYLE Documentation style for the generated comments (default plain). See Documentation comments.
--naming=CONVENTION Casing of the public function/method names (snake / camel / pascal). Defaults to each language's idiomatic convention. See Naming conventions.
file=STEM Write to disk (extension picked per language; see below). Omit for stdout.
name=NAME Rename the CRC: replaces the algorithm name as the base for the functions / class / filename, cased per language (name=my-widget โ†’ my_widget.rs, MyWidget.java, MyWidget.cs). Single algorithm only.
symbol=NAME Escape hatch: emit this exact identifier verbatim, bypassing --naming. Single algorithm; not for Java. Prefer name= for the usual "call it X" case.

File extensions per language: C emits STEM.h + STEM.c; Python .py; Rust .rs; VHDL .vhd; Verilog .sv (SystemVerilog 2012); Go .go; C# .cs; Java .java; TypeScript .ts. For Java and C# the file is named after the public class (PascalCase of name= / the algorithm, or of STEM), so the stem must yield a legal class identifier; STEM is otherwise sanitized to a valid identifier (file=my-crc โ†’ my_crc.rs).

Bundle several algorithms into one file by naming more than one: crcglot c crc32 crc16-modbus crc8 file=mycrcs writes a single mycrcs.h / mycrcs.c containing all three (one .go / .rs / .cs / โ€ฆ for the other languages). Each algorithm keeps its own catalogue-derived function names (crc32, crc16_modbus, โ€ฆ) and the tables are namespaced per symbol, so they never collide. name= and symbol= rename a single CRC, so both are rejected with more than one algorithm; duplicates are de-duplicated; an unknown name aborts the whole bundle.

Expert overrides (you usually don't need these, since --fast chooses for you): --table forces the 256-entry single-table form, and --slice8 forces the 8-table form. They exist for the rare case where you want the middle of the size/speed curve explicitly, e.g. a RAM-constrained target where the 1 KiB table is fine but slice-by-8's 8 KiB isn't. --slice8 is CRC-32/64 + compiled targets only.

Rules:

  • The variant selectors --small / --fast / --table / --slice8 are mutually exclusive: pick at most one (exit 2 otherwise). No selector = --fast (the fastest the target supports); pass --small for the smallest code.
  • --slice8 python silently falls back to --table (CPython's per-int overhead eats the slice-by-8 speedup; stderr warns). --fast never needs this fallback; it only picks slice-by-8 where it actually applies.
  • Without file=, output goes to stdout. For C, header is emitted first, then source.
  • Every target embeds <symbol>_self_test() (C returns 0 on success; the rest return bool / boolean / bit). In constrained embedded targets, standard toolchain flags (-Wl,--gc-sections for C, LTO for Rust) strip whatever you don't call.

--custom (raw Rocksoft/Williams parameters)

For algorithms not in the catalogue:

crcglot c --custom width=16 poly=0x1234 init=0xFFFF \
         refin=true refout=true xorout=0x0000 file=mycustom
Param Required Notes
width=N yes 8, 16, 32, or 64 only
poly=X yes Hex (0x...) or decimal
init=X no Default 0. Hex or decimal.
refin=B no Default false. Accepts true/false/1/0/yes/no/on/off.
refout=B no Default false. Same boolean syntax.
xorout=X no Default 0.
name=NAME no Default crc_custom. Names the functions / class / filename (cased per language) and labels the comments.
desc=TEXT no Free-form description in comments.

The check value for the custom parameters is computed automatically (generic_crc(b"123456789", ...)) and embedded into the generated _self_test().

Catalogue

More than 100 algorithms covering everything from CRC-8 (ATM, AUTOSAR, Bluetooth, Maxim 1-Wire) through CRC-16 (Modbus, XMODEM, CCITT, IBM SDLC) through CRC-32 (Ethernet, bzip2, iSCSI, AUTOSAR) to CRC-64 (XZ, ECMA-182, NVMe, Redis), plus the non-byte-aligned families: CAN (CRC-15), CAN FD (CRC-17/21), FlexRay (CRC-11/24), LTE/BLE/OpenPGP (CRC-24), and the GSM/UMTS/CDMA2000 telecom set. Browse with crcglot list.

Programmatic API

Two registries, both keyed by short code:

LANGUAGES: supported target languages

from crcglot import LANGUAGES

for code, info in LANGUAGES.items():
    print(f"{info.emoji} {info.display_name:<10}  {info.extensions}  "
          f"{sorted(info.variants)}")
    # โ†’ โš™๏ธ C / C++       ('.h', '.c')  ['bitwise', 'slice8', 'table']
    # โ†’ ๐Ÿ’  C#            ('.cs',)      ['bitwise', 'slice8', 'table']
    # โ†’ ๐Ÿšฆ Go            ('.go',)      ['bitwise', 'slice8', 'table']
    # โ†’ โ˜• Java          ('.java',)    ['bitwise', 'slice8', 'table']
    # โ†’ ๐Ÿ Python        ('.py',)      ['bitwise', 'table']
    # โ†’ ๐Ÿฆ€ Rust          ('.rs',)      ['bitwise', 'slice8', 'table']
    # โ†’ ๐Ÿ”ท TypeScript    ('.ts',)      ['bitwise', 'slice8', 'table']
    # โ†’ ๐Ÿ”ง Verilog       ('.sv',)      ['bitwise']
    # โ†’ ๐Ÿ”Œ VHDL          ('.vhd',)     ['bitwise']

Each entry is a frozen LanguageInfo dataclass with:

  • code: dispatch key ("c", "csharp", ..., "typescript", "verilog")
  • extensions: file extension tuple ((".h", ".c") for C; single-element for the rest)
  • variants: subset of {"bitwise", "table", "slice8"} that the generator accepts
  • generator(name, ...): name-lookup callable (returns source string, or (header, source) tuple for C)
  • generator_from_entry(name, algo, ...): bypass the catalogue with a custom AlgorithmInfo
  • combiner(outputs, stem): merge several generator outputs into one file (powers multi-algorithm bundling); per-symbol tables keep the merge collision-free
  • emoji: single-grapheme pictographic identifier for terminals / docs
  • display_name: human-readable name (e.g. "C / C++", "TypeScript"), distinct from code
  • UI helpers: generate_files(...) returns ready-to-write GeneratedFiles; format_name(stem, kind) / format_filename(stem) case a stem to the identifier or filename crcglot will emit; validate_symbol(stem) pre-checks a name; module-level default_stem(algorithm) gives crcglot's default stem (the algorithm name, or crc_bundle for a bundle). A UI reads its name field from these instead of hardcoding per-language rules

ALGORITHMS: the reveng CRC catalogue

from crcglot import ALGORITHMS

modbus = ALGORITHMS["crc16-modbus"]
print(modbus.width, hex(modbus.check), modbus.desc)
# โ†’ 16 0x4b37 Modbus RTU serial protocol

# Filter to CRC-32 only.
crc32_family = [a for a in ALGORITHMS.values() if a.width == 32]

Each entry is a frozen AlgorithmInfo dataclass with the full Rocksoft / Williams parameter set: name, width, poly, init, refin, refout, xorout, check, desc.

Custom polynomials

from crcglot import AlgorithmInfo, Crc, LANGUAGES, generic_crc

# Compute the canonical check value for a custom poly.
spec = Crc(width=16, poly=0x1234, init=0xFFFF, refin=True, refout=True, xorout=0x0000)
check = generic_crc(b"123456789", spec)

# Build an AlgorithmInfo (a named, checked Crc) and feed it to any generator.
algo = AlgorithmInfo(
    width=16, poly=0x1234, init=0xFFFF,
    refin=True, refout=True, xorout=0x0000, check=check,
    desc="My custom CRC-16", source="custom",
)
code = LANGUAGES["rust"].generator_from_entry("my_crc16", algo, table=True)

Use with an MCP client (optional)

crcglot[mcp] exposes the CLI surface as a Model Context Protocol server so LLM clients (Claude Desktop, Cursor, mcp-cli, โ€ฆ) can call crc_detect / crc_reverse / crc_verify / crc_compute / crc_generate etc. as named tools. The LLM never has to remember a polynomial, slice bytes off a packet to find the CRC, or write a reflection loop; it asks crcglot. The three packet tools take the same input shape (a frame with the CRC at the tail, either binary hex/base64 or a data <sep> hexcrc text line): crc_detect names a known CRC, crc_reverse recovers an unknown / custom one, and crc_verify checks a frame against a named algorithm.

pip install 'crcglot[mcp]'        # the extra ships the MCP SDK
# or:  uv tool install 'crcglot[mcp]'

Then wire it into your MCP client. Claude Desktop's claude_desktop_config.json:

{
  "mcpServers": {
    "crcglot": {
      "command": "uvx",
      "args": ["--from", "crcglot[mcp]", "crcglot-mcp"]
    }
  }
}

Tools: crc_list ยท crc_info ยท crc_detect ยท crc_reverse ยท crc_verify ยท crc_encode ยท crc_compute ยท crc_compute_many ยท crc_generate ยท crc_credits. Resources: crcglot://catalogue.json ยท crcglot://languages.json ยท crcglot://variants.json. Full reference and Claude Desktop walkthrough live in docs/MCP.md.

Fast runtime CRC (optional C extension)

Beyond generating code, crcglot can compute CRCs at runtime, and it's fast.

Performance, stated honestly: with the C extension, crcglot computes any of the more than 100 CRCs from Python at compiled-C-class throughput on bulk data (~1.7 GB/s on a 1 MiB buffer, on par with generated C and ahead of generated Rust), and for IEEE CRC-32 / JAMCRC it delegates to the stdlib's hardware path (~tens of GB/s), faster than the generated code. The pure-Python fallback always works but is ~1000ร— slower. Two caveats: the "compiled-class" numbers need the extension installed (it ships in the prebuilt wheel), and they hold for bulk/streaming data; many tiny one-shot calls pay Pythonโ†”C overhead per call (use the batch API for those). All figures are platform-specific; see BENCHMARKS.md.

At runtime there's no variant choice to make, the same philosophy as --small/--fast on the generator, taken all the way: you just call crcglot.generic_crc(data, crc) (passing a Crc, or any AlgorithmInfo) and it picks the fastest path available on your machine. There's no table=/slice8= knob here; the speed you get depends only on whether the C extension is installed.

Under the hood it dispatches three ways (you never select among them):

  1. IEEE CRC-32 / JAMCRC โ†’ stdlib zlib.crc32 (hardware CRC folding: PCLMULQDQ on x86, PMULL / crc32 instructions on ARM): tens of GB/s. No software CRC out-runs silicon, so crcglot borrows the stdlib's path for the algorithms it covers.
  2. Everything else โ†’ the optional C extension (crcglot._c, slice-by-8 / table-driven): ~1-2 GB/s, ~2,000ร— over pure Python.
  3. No extension built โ†’ pure Python: always works, just slow.

The extension ships in the prebuilt wheels; pip install crcglot (or uv tool install crcglot) gets it on common platforms, with no extra to enable. To force a build from source instead of using a prebuilt wheel:

pip install --no-binary crcglot crcglot

It's a single abi3 wheel per platform (CPython 3.11+), and crcglot stays fully functional in pure Python if no wheel matches your platform.

from crcglot import Crc, generic_crc

# One-shot.  crc32 here rides the zlib hardware path automatically.
ieee = Crc(width=32, poly=0x04C11DB7, init=0xFFFFFFFF,
           refin=True, refout=True, xorout=0xFFFFFFFF)
crc = generic_crc(b"123456789", ieee)

Streaming and batch

โš ๏ธ Don't call generic_crc in a hot loop. It's a one-shot: for any table/slice-by-8 algorithm (everything byte-aligned except IEEE crc32 / jamcrc, which ride zlib) it rebuilds the lookup table on every call, with no cache. Looping it over many messages of the same algorithm rebuilds the table each iteration, which on small buffers is 4โ€“11ร— slower than necessary and only worsens the longer you loop. For many CRCs of the same algorithm, build the table once with a CrcStream and update per message (and independent streams run fully in parallel across threads). Use generic_crc for a single CRC; use streaming for repetition.

Two reasons to use the streaming API instead of generic_crc: chunked data (a message arriving in pieces, such as large files, sockets, or sensor logs) and repetition (many messages of the same algorithm, so you build the table once, not per call). It's the runtime counterpart to the generated init โ†’ update* โ†’ finalize triple. Bind the algorithm once by catalogue name, feed chunks, and read the finalized value on demand (hashlib idiom: update / digest / reset / copy):

from crcglot import crc_stream

s = crc_stream("crc32")           # by catalogue name
for chunk in chunks:              # any chunking โ€” the answer never changes
    s.update(chunk)
s.digest()        # 0xCBF43926 โ€” an int; non-destructive, call it again
s.hexdigest()     # 'cbf43926'

crc_stream is backend-smart, taking the same three-tier dispatch as generic_crc: stdlib zlib.crc32 for IEEE crc32 / jamcrc, the C extension when built, pure-Python otherwise. So it always works, and is fast where it can be. For a custom (non-catalogue) CRC, build it from a Crc value object (or its raw keyword parameters), or from an AlgorithmInfo:

from crcglot import CrcStream, Crc, ALGORITHMS

CrcStream.from_crc(Crc(width=16, poly=0x8005, init=0xFFFF, refin=True, refout=True, xorout=0))
CrcStream(width=16, poly=0x8005, init=0xFFFF, refin=True, refout=True, xorout=0)  # raw kwargs
CrcStream.from_info(ALGORITHMS["crc16-modbus"])

For high-volume small-buffer workloads (framed protocols, packet streams, bulk validation) where you have a list of payloads up front, generic_crc_many CRCs them all in one call, building the lookup table once for the whole batch and paying the Pythonโ†”C transition once, instead of rebuilding per call the way a loop of generic_crc would:

from crcglot import generic_crc_many, ALGORITHMS

a = ALGORITHMS["crc16-modbus"]
results = generic_crc_many(list_of_packets, a)   # one CRC per packet, in order

It uses the same dispatch as generic_crc (zlib for crc32 / jamcrc, the C extension's c_crc_many otherwise, pure-Python fallback), and is exposed over MCP as the crc_compute_many tool, so an agent can CRC a whole batch of captured frames in a single tool call.

See BENCHMARKS.md for measured throughput of each runtime path against the generated-code gallery.

Example output

See EXAMPLES.md for the actual generated source for crc32 across every language ร— implementation combination (C / Rust / Python / VHDL / Verilog / Go / C# / Java / TypeScript crossed with bit-by-bit, table-driven, and slice-by-8 where supported). Every block is reproducible with one CLI command.

Benchmarks

See BENCHMARKS.md for measured crc32 throughput across every (language ร— variant) cell at 1 KiB and 1 MiB. Within each language the trend is monotonic (bit-by-bit < table < slice-by-8) but the absolute speedup at each step depends heavily on how well the compiler optimizes the baseline: Rust's LLVM-vectorized bit-by-bit nearly ties its table-driven, while C# / Python see a 10ร—+ jump just from table-driven because their bitwise loops aren't vectorized. VHDL and Verilog are excluded: they're simulator references for hardware datapaths, not software runtime.

Acknowledgments

crcglot builds on:

  • The reveng CRC catalogue by Greg Cook: the canonical source of CRC algorithm parameters since 1999, and the source of the more than 100 parameter sets, descriptions, and check values every catalogue entry in crcglot is derived from.
  • zlib by Mark Adler, Jean-loup Gailly et al.: the runtime fast path for CRC-32/ISO-HDLC and JAMCRC, which take the PCLMULQDQ folding path on x86 and the PMULL / crc32 instructions on ARM.
  • The Rocksoft Model CRC parameterization by Ross N. Williams: the (width, poly, init, refin, refout, xorout, check) vocabulary every catalogue entry is expressed in.

crcglot credits prints this same content in the terminal, and crcglot.ATTRIBUTION / crcglot.ACKNOWLEDGMENTS expose it programmatically.

License

MIT

Project details


Download files

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

Source Distribution

crcglot-0.19.0.tar.gz (311.9 kB view details)

Uploaded Source

Built Distributions

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

crcglot-0.19.0-cp311-abi3-win_arm64.whl (207.2 kB view details)

Uploaded CPython 3.11+Windows ARM64

crcglot-0.19.0-cp311-abi3-win_amd64.whl (208.2 kB view details)

Uploaded CPython 3.11+Windows x86-64

crcglot-0.19.0-cp311-abi3-musllinux_1_2_x86_64.whl (223.9 kB view details)

Uploaded CPython 3.11+musllinux: musl 1.2+ x86-64

crcglot-0.19.0-cp311-abi3-musllinux_1_2_aarch64.whl (223.7 kB view details)

Uploaded CPython 3.11+musllinux: musl 1.2+ ARM64

crcglot-0.19.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (223.4 kB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ ARM64

crcglot-0.19.0-cp311-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (223.9 kB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ x86-64manylinux: glibc 2.5+ x86-64

crcglot-0.19.0-cp311-abi3-macosx_11_0_arm64.whl (205.2 kB view details)

Uploaded CPython 3.11+macOS 11.0+ ARM64

File details

Details for the file crcglot-0.19.0.tar.gz.

File metadata

  • Download URL: crcglot-0.19.0.tar.gz
  • Upload date:
  • Size: 311.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for crcglot-0.19.0.tar.gz
Algorithm Hash digest
SHA256 038d781a55edc1d044412d478686ba355c1b754ca3e2973d659a399123812585
MD5 93b384f6eaea580613b0802c86b8c918
BLAKE2b-256 623f5d2dfbb0f348f31b59cf389fb6b14f9db10386f0fc5276bb418913f76bee

See more details on using hashes here.

Provenance

The following attestation bundles were made for crcglot-0.19.0.tar.gz:

Publisher: wheels.yml on hucker/crcglot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file crcglot-0.19.0-cp311-abi3-win_arm64.whl.

File metadata

  • Download URL: crcglot-0.19.0-cp311-abi3-win_arm64.whl
  • Upload date:
  • Size: 207.2 kB
  • Tags: CPython 3.11+, Windows ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for crcglot-0.19.0-cp311-abi3-win_arm64.whl
Algorithm Hash digest
SHA256 605be35062cc6e29f9c6071a62fd70258a85754e05ea3e8dbf7d3ce5c771922b
MD5 31840fe4e8d46ab0e77fae5847df013a
BLAKE2b-256 8e4417f8efd76304a11823893d3154ac78661cc04ba4d44aa9491e72573e2dd7

See more details on using hashes here.

Provenance

The following attestation bundles were made for crcglot-0.19.0-cp311-abi3-win_arm64.whl:

Publisher: wheels.yml on hucker/crcglot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file crcglot-0.19.0-cp311-abi3-win_amd64.whl.

File metadata

  • Download URL: crcglot-0.19.0-cp311-abi3-win_amd64.whl
  • Upload date:
  • Size: 208.2 kB
  • Tags: CPython 3.11+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for crcglot-0.19.0-cp311-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 1bf9b7cc1c4e06ddfa948a04263b44a222553ef999e032715b66d055b399e5fc
MD5 35c14b30daada9cb0acf7b2725585b15
BLAKE2b-256 590fc191a36f720b615c963bc3f14037319812de66425c12084216b53d184fa6

See more details on using hashes here.

Provenance

The following attestation bundles were made for crcglot-0.19.0-cp311-abi3-win_amd64.whl:

Publisher: wheels.yml on hucker/crcglot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file crcglot-0.19.0-cp311-abi3-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for crcglot-0.19.0-cp311-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 7ed5f932704c2c3d6ca6fe97b962e59e1a03c9da321e80be2001b78a5ab82205
MD5 337516bf9c9a8e6c5c96cf5d08dce641
BLAKE2b-256 924437502e27f4e5cb3010cb239964527cd7b187b74a14c3a7ce560d04a650d2

See more details on using hashes here.

Provenance

The following attestation bundles were made for crcglot-0.19.0-cp311-abi3-musllinux_1_2_x86_64.whl:

Publisher: wheels.yml on hucker/crcglot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file crcglot-0.19.0-cp311-abi3-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for crcglot-0.19.0-cp311-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 9c082b9d5fd643a8abc7d40e4a2189317ced8754594d75ab6ae07fd88bd1aedd
MD5 17c85ea26bf15e1eedef66c982c2a397
BLAKE2b-256 8631412fc6f541699dbda235e29fbf0765dbcb4fd827689e8f97cbc71a0852f5

See more details on using hashes here.

Provenance

The following attestation bundles were made for crcglot-0.19.0-cp311-abi3-musllinux_1_2_aarch64.whl:

Publisher: wheels.yml on hucker/crcglot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file crcglot-0.19.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for crcglot-0.19.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 e3ff5b9b1dcd53cbe7d134c116cd8108522cb7a09a3e2147f0a51a2a8c5b7344
MD5 975cb4844a4390813f4d49bea9cf8e3a
BLAKE2b-256 663cb65abc4e1a8c4a48ed5d0eca0b0492120e0854c6f1df04122776557d9489

See more details on using hashes here.

Provenance

The following attestation bundles were made for crcglot-0.19.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: wheels.yml on hucker/crcglot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file crcglot-0.19.0-cp311-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for crcglot-0.19.0-cp311-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 411f2f4c352ce784507bba2bc5d46cdbe2fb92d779fe9252d2324a6fb5ff4704
MD5 cf60cb1412bdd65d5139f51442547734
BLAKE2b-256 9c7e3d32b1c4a8e6ca7658fb09bebbc14daade1267ca48aaec1b92f900445cb3

See more details on using hashes here.

Provenance

The following attestation bundles were made for crcglot-0.19.0-cp311-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: wheels.yml on hucker/crcglot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file crcglot-0.19.0-cp311-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for crcglot-0.19.0-cp311-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 7add1b77c45e6ab94b650565a7a957ec9cd8c15d8d3fae7d665f186646934086
MD5 2ebb2cea3036e0ad628eeec09b92ab3a
BLAKE2b-256 0f8777aef5ec1eed43e18b23cca3582e8b781d23e34a7bfdacd1f3f7658cae4b

See more details on using hashes here.

Provenance

The following attestation bundles were made for crcglot-0.19.0-cp311-abi3-macosx_11_0_arm64.whl:

Publisher: wheels.yml on hucker/crcglot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

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