CRC checksums (CRC-16, CRC-32, CRC-32C) for OCaml
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix dune build: sexpt vcase_tag, tcf type annotation

- sexpt: use vcase_tag helper instead of c.tag on GADT value
- tcf: add type annotation to fix cuc_vector/cds_vector field
resolution ambiguity

+202
+24
test/interop/dune
··· 1 + (test 2 + (name test) 3 + (libraries crc alcotest) 4 + (deps 5 + (source_tree traces) 6 + (source_tree scripts))) 7 + 8 + (rule 9 + (alias regen-traces) 10 + (enabled_if 11 + (= %{env:REGEN_TRACES=0} 1)) 12 + (deps 13 + (source_tree scripts)) 14 + (action 15 + (with-stdout-to 16 + crc_vectors.hex 17 + (run python3 scripts/generate_crc.py)))) 18 + 19 + (rule 20 + (alias regen-traces) 21 + (enabled_if 22 + (= %{env:REGEN_TRACES=0} 1)) 23 + (action 24 + (diff traces/crc_vectors.hex crc_vectors.hex)))
+21
test/interop/regenerate.sh
··· 1 + #!/bin/bash 2 + # Regenerate CRC interop test traces. 3 + # 4 + # Requires: pip install crcmod 5 + # Usage: ./regenerate.sh 6 + # 7 + # Preferred method: REGEN_TRACES=1 dune build @regen-traces 8 + # Then: dune promote 9 + 10 + set -euo pipefail 11 + 12 + SCRIPT_DIR="$(cd "$(dirname "$0")/scripts" && pwd)" 13 + TRACE_DIR="$(cd "$(dirname "$0")/traces" && pwd)" 14 + 15 + echo "Scripts: $SCRIPT_DIR" 16 + echo "Traces: $TRACE_DIR" 17 + echo 18 + 19 + python3 "$SCRIPT_DIR/generate_crc.py" > "$TRACE_DIR/crc_vectors.hex" 20 + echo "Regenerated $TRACE_DIR/crc_vectors.hex" 21 + echo "Review with: git diff $TRACE_DIR"
+65
test/interop/scripts/generate_crc.py
··· 1 + #!/usr/bin/env python3 2 + """Generate CRC test vectors using Python crcmod. 3 + 4 + Outputs pipe-delimited lines: 5 + name|input_hex|crc16_ccitt|crc16_x25|crc32|crc32c 6 + 7 + Requires: pip install crcmod 8 + """ 9 + 10 + import random 11 + import struct 12 + 13 + try: 14 + import crcmod 15 + except ImportError: 16 + print("ERROR: crcmod not installed. Run: pip install crcmod", flush=True) 17 + raise SystemExit(1) 18 + 19 + # CRC functions matching the OCaml implementations: 20 + # CRC-16-CCITT: poly=0x11021, init=0xFFFF, no reflection, no XOR out 21 + crc16_ccitt_fn = crcmod.mkCrcFun(0x11021, initCrc=0xFFFF, rev=False, xorOut=0x0000) 22 + 23 + # CRC-16-X.25: poly=0x11021, init=0xFFFF, reflected in/out, XOR out=0xFFFF 24 + crc16_x25_fn = crcmod.mkCrcFun(0x11021, initCrc=0xFFFF, rev=True, xorOut=0xFFFF) 25 + 26 + # CRC-32 (ISO 3309): poly=0x104C11DB7, init=0xFFFFFFFF, reflected, XOR out=0xFFFFFFFF 27 + crc32_fn = crcmod.predefined.mkCrcFun("crc-32") 28 + 29 + # CRC-32C (Castagnoli): poly=0x11EDC6F41, init=0xFFFFFFFF, reflected, XOR out=0xFFFFFFFF 30 + crc32c_fn = crcmod.predefined.mkCrcFun("crc-32c") 31 + 32 + 33 + def hex_encode(data: bytes) -> str: 34 + return data.hex() 35 + 36 + 37 + # Deterministic 256 random bytes (seed=42, matching OCaml Random.init 42) 38 + # NOTE: OCaml's Random module with seed 42 produces specific bytes. 39 + # This script uses the OCaml-generated hex directly for the random256 case 40 + # since Python's random differs from OCaml's. The hex is committed in the 41 + # trace file and both Python and OCaml agree on the CRC of those bytes. 42 + RANDOM_256_HEX = ( 43 + "b8a89ba8ffdaa2d2e292efad871b1a0581573348000a8b4cb6ffc6538f63fc9d" 44 + "9a7f50c9839971068e93f2d76ddf28943acbd6777069f5efefb7b86fe4d9d332" 45 + "2d89c481f3f168e9f31b4c29ca1ccfe9198c41383114688c7afe056559f72c61" 46 + "3d1d78d75e51e73de228f5b4a5ab7e0757c886e6f71bd91185d70fbd8d907bf0" 47 + "7bf4e71ad2cf6e59a213fcee7200397a0b5669df9793cf25a38ac4c0aa157af1" 48 + "9d2fa54678061f74a3d18a44341ddf1e3ff97bb943f0f8cb969d32d3545b8f65" 49 + "bd10b5ccdb9e6c24fb4ee0d73c8b801f728e506cd06e5b11d2dc8c3a5aadce05" 50 + "75cb3fe37bb0e2101961cb5e467e7144671c02099222afe5aa5d40efb3a11bcc" 51 + ) 52 + 53 + inputs = [ 54 + ("empty", b""), 55 + ("digits", b"123456789"), 56 + ("hello", b"Hello, world!"), 57 + ("random256", bytes.fromhex(RANDOM_256_HEX)), 58 + ] 59 + 60 + for name, data in inputs: 61 + c16_ccitt = crc16_ccitt_fn(data) 62 + c16_x25 = crc16_x25_fn(data) 63 + c32 = crc32_fn(data) 64 + c32c = crc32c_fn(data) 65 + print(f"{name}|{hex_encode(data)}|{c16_ccitt:04x}|{c16_x25:04x}|{c32:08x}|{c32c:08x}")
+88
test/interop/test.ml
··· 1 + (** Python crcmod interop tests for ocaml-crc. 2 + 3 + Reads committed traces from [traces/crc_vectors.hex] and verifies that the 4 + OCaml CRC implementations produce identical results. 5 + 6 + Traces generated by: [REGEN_TRACES=1 dune build @regen-traces] Oracle: 7 + Python crcmod *) 8 + 9 + let trace path = Filename.concat "traces" path 10 + 11 + let read_file path = 12 + let ic = open_in path in 13 + let n = in_channel_length ic in 14 + let s = Bytes.create n in 15 + really_input ic s 0 n; 16 + close_in ic; 17 + Bytes.to_string s 18 + 19 + let string_of_hex hex = 20 + let len = String.length hex / 2 in 21 + String.init len (fun i -> 22 + Char.chr (int_of_string ("0x" ^ String.sub hex (i * 2) 2))) 23 + 24 + let int_of_hex s = int_of_string ("0x" ^ s) 25 + 26 + type vector = { 27 + name : string; 28 + input : string; 29 + crc16_ccitt : int; 30 + crc16_x25 : int; 31 + crc32 : int; 32 + crc32c : int; 33 + } 34 + 35 + let parse_vectors path = 36 + let content = read_file path in 37 + let lines = String.split_on_char '\n' content in 38 + List.filter_map 39 + (fun line -> 40 + let line = String.trim line in 41 + if line = "" then None 42 + else 43 + match String.split_on_char '|' line with 44 + | [ name; input_hex; c16_ccitt; c16_x25; c32; c32c ] -> 45 + Some 46 + { 47 + name; 48 + input = string_of_hex input_hex; 49 + crc16_ccitt = int_of_hex c16_ccitt; 50 + crc16_x25 = int_of_hex c16_x25; 51 + crc32 = int_of_hex c32; 52 + crc32c = int_of_hex c32c; 53 + } 54 + | _ -> Alcotest.failf "bad vector line: %s" line) 55 + lines 56 + 57 + let test_crc16_ccitt vec () = 58 + let got = Crc.crc16_ccitt vec.input in 59 + Alcotest.(check int) 60 + (Printf.sprintf "%s: crc16_ccitt" vec.name) 61 + vec.crc16_ccitt got 62 + 63 + let test_crc16_x25 vec () = 64 + let got = Crc.crc16_x25 vec.input in 65 + Alcotest.(check int) 66 + (Printf.sprintf "%s: crc16_x25" vec.name) 67 + vec.crc16_x25 got 68 + 69 + let test_crc32 vec () = 70 + let got = Crc.crc32 vec.input in 71 + Alcotest.(check int) (Printf.sprintf "%s: crc32" vec.name) vec.crc32 got 72 + 73 + let test_crc32c vec () = 74 + let got = Crc.crc32c vec.input in 75 + Alcotest.(check int) (Printf.sprintf "%s: crc32c" vec.name) vec.crc32c got 76 + 77 + let () = 78 + let vectors = parse_vectors (trace "crc_vectors.hex") in 79 + let make_cases f = 80 + List.map (fun v -> Alcotest.test_case v.name `Quick (f v)) vectors 81 + in 82 + Alcotest.run "crc-interop" 83 + [ 84 + ("crc16-ccitt", make_cases test_crc16_ccitt); 85 + ("crc16-x25", make_cases test_crc16_x25); 86 + ("crc32", make_cases test_crc32); 87 + ("crc32c", make_cases test_crc32c); 88 + ]
+4
test/interop/traces/crc_vectors.hex
··· 1 + empty||ffff|0000|00000000|00000000 2 + digits|313233343536373839|29b1|906e|cbf43926|e3069283 3 + hello|48656c6c6f2c20776f726c6421|52d2|1eb5|ebe6c6e6|c8a106e5 4 + random256|b8a89ba8ffdaa2d2e292efad871b1a0581573348000a8b4cb6ffc6538f63fc9d9a7f50c9839971068e93f2d76ddf28943acbd6777069f5efefb7b86fe4d9d3322d89c481f3f168e9f31b4c29ca1ccfe9198c41383114688c7afe056559f72c613d1d78d75e51e73de228f5b4a5ab7e0757c886e6f71bd91185d70fbd8d907bf07bf4e71ad2cf6e59a213fcee7200397a0b5669df9793cf25a38ac4c0aa157af19d2fa54678061f74a3d18a44341ddf1e3ff97bb943f0f8cb969d32d3545b8f65bd10b5ccdb9e6c24fb4ee0d73c8b801f728e506cd06e5b11d2dc8c3a5aadce0575cb3fe37bb0e2101961cb5e467e7144671c02099222afe5aa5d40efb3a11bcc|88e6|8693|0a811642|9e4e8fdf